以下均轉自Android遊戲編程入門經典,轉載請標明出處
對於Android開發人員來說,什麼是最糟糕的事情?讓一切工作都停止啟動並執行記憶體回收!
我們先來看看Input介面
package org.example.androidgames.framework;import java.util.List;public interface Input { public static class KeyEvent { public static final int KEY_DOWN = 0; public static final int KEY_UP = 1; public int type; public int keyCode; public char keyChar; public String toString() { StringBuilder builder = new StringBuilder(); if (type == KEY_DOWN) builder.append("key down, "); else builder.append("key up, "); builder.append(keyCode); builder.append(","); builder.append(keyChar); return builder.toString(); } } public static class TouchEvent { public static final int TOUCH_DOWN = 0; public static final int TOUCH_UP = 1; public static final int TOUCH_DRAGGED = 2; public int type; public int x, y; public int pointer; public String toString() { StringBuilder builder = new StringBuilder(); if (type == TOUCH_DOWN) builder.append("touch down, "); else if (type == TOUCH_DRAGGED) builder.append("touch dragged, "); else builder.append("touch up, "); builder.append(pointer); builder.append(","); builder.append(x); builder.append(","); builder.append(y); return builder.toString(); } } public boolean isKeyPressed(int keyCode); public boolean isTouchDown(int pointer); public int getTouchX(int pointer); public int getTouchY(int pointer); public float getAccelX(); public float getAccelY(); public float getAccelZ(); public List<KeyEvent> getKeyEvents(); public List<TouchEvent> getTouchEvents();}
我們從定義兩個類開始,KeyEvent和TouchEvent。KeyEvent類定義了一些該常量類型,TouchEvent也是一樣。當事件類型為KEY_UP時,一個KeyEvent執行個體將會記錄它的類型、按鍵代碼和Unicode字元。
TouchEvent的代碼也很相似,它定義了TouchEvent事件類型、觸點相對於UI組件原點的位置和觸控螢幕的驅動程式賦予手指的指標ID。只要手指還在螢幕上,那麼它的指標ID是不會變的。如果按下了兩隻手指,那麼當手指0離開時,手指1會保持其ID不變直到它離開觸控螢幕。當一個新的手指按下時會得到第一個未佔用的ID,本例中將會得到0。指標ID通常是按順序分配的,但是並不保證一定如此。
接下來便是Input介面的輪詢方法,其用途很明顯。Input.isKeyPressed()接受一個keyCode參數,並返回對應當前按鍵是否被按下資訊。Input.isTouchDown()、Input.getTouchX()和Input.getTouchY()返回是否按下一個給定的指標,以及其當前的x和y座標。注意,如果實際上相應的指標沒有觸控螢幕幕的話,其座標是沒法確定的。
Input.getAccelX()、Input.getAccelY()、Input.getAccelZ()返回加速計在各個軸上的值。
最後兩個方法用於基於事件的處理,它將返回上次我們調用這些方法後所記錄的KeyEvent和TouchEvent執行個體。該事件按發生時間排序,最新發生的事件排在列表的最後面。
現在回到記憶體回收行程設計上來,在Input介面裡getTouchEvents()和getKeyEvents()方法。它將返回TouchEvent和KeyEvent列表。在鍵盤和觸摸事件處理常式中,我們將不斷建立這兩個類的執行個體,並將它們儲存在內部處理常式的列表中。當一個鍵被按下或手指觸控螢幕幕時,Android的輸入系統會觸發很多事件。所以我們會不斷建立新執行個體,在很短的時間間隔內,這些事件會被記憶體回收行程收集。為了避免這種情況,我們將實現一個稱之為實力入池(instance
pooling)的概念。它不是在一個類中不斷地建立新執行個體,而是簡單地重用以前的執行個體。Pool類是實現這種行為的一種很便捷的方式。
package org.example.androidgames.framework;import java.util.ArrayList;import java.util.List;public class Pool<T> {private final List<T> freeObjects;private final PoolObjectFactory<T> factory;private final int maxSize;public interface PoolObjectFactory<T>{public T createObject();}public Pool(PoolObjectFactory<T> factory, int maxSize){this.factory = factory;this.maxSize = maxSize;this.freeObjects =new ArrayList<T>(maxSize);}public T newObject(){T object = null;if(freeObjects.size() == 0)object = factory.createObject();elseobject = freeObjects.remove(freeObjects.size() - 1);return object;}public void free(T object){if(freeObjects.size() < maxSize)freeObjects.add(object);}}
Pool<T>這裡使用了泛型。這個類是個通用類型的類,泛型允許我們在Pool類中儲存任何類型的對象,而無須對類型進行轉換。
首先定義的是一個名為PoolObjectFactory的介面,它是泛型的。它只有一個方法createObject(),該方法將返回一個新對象,其具有泛型型別的Pool/PoolObjectFactory執行個體。
Pool類有3個成員:ArrayList用於儲存以入池的對象,PoolObjectFactory用於產生Pool類中包含的類型的新執行個體,第三個成員用於存放Pool可以容納的最大對象數量。最後一個成員是必需的,這樣Pool才不會無限制增長;否則,我們將可能會遇到一個記憶體耗盡的異常。
Pool類的建構函式接受一個PoolObjectFactory和所儲存物件的最大數量。這兩個參數都儲存在各自的成員變數裡,並且我們執行個體化一個新的ArrayList,其大小設為對象的最大數量。
newObject()方法負責通過PoolObjectFactory.newObject()方法給我們傳遞一個全新的Pool執行個體,或者在freeObjectsArrayList中有對象的情況下,返回一個已入池的執行個體。如果使用這種方法,只要Pool類中有一些Object Storage Service在freeObjects列表中,我們將得到一些可回收的對象。否則,該方法將通過工廠類建立一個新的對象。
free()方法允許我們重新插入不再使用的對象。它的工作就是如果freeObjects列表中沒有滿,就將對象插入到freeObjects列表中。如果列表已滿,則不再添加對象,並且對象可能在free()方法下一次執行時被記憶體回收行程回收。
至於我們如果用Pool類建立一個TouchEvent執行個體,時候不早了,下次再寫