標籤:android 應用 開發
資料管理
對於唯讀資料,一種常用的管理員模式是在onCreate函數中進行資料的載入,直到組件的onDestory函數被調用時在進行釋放。
// 緩衝唯讀資料 private Object readOnlyData; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 讀取資料到記憶體 readOnlyData = readOnlyData(); } private Object readOnlyData() { return null ; } @Override protected void onDestroy() { super.onDestroy(); // 將資料置空,加速回收 readOnlyData = null ; }
如果資料支援讀寫操作,則需要在onResume或者onCreate中進行讀取,而在onPause中實現儲存。
因為當onPause函數被調用後,該介面組件就處於可回收的狀態。當資源緊張時,系統會強行銷毀組件對象。
對象中所有未持久化的修改就會丟失。對於讀寫資料處理的模型樣本如下:
// 緩衝可讀寫的資料 private Object data; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 讀取資料到記憶體 data = readData(); } @Override protected void onResume() { super.onResume(); data = readData(); } private Object readData() { // TODO 讀取資料 return null ; } private void writeData(Object data) { // TODO 寫入資料 } @Override protected void onPause() { super.onPause(); writeData( data); } @Override protected void onDestroy() { super.onDestroy(); // 將資料置空,加速回收 data = null ; }
狀態管理
當系統將介面組件切離前台狀態(即onPause函數調用前),會先行調用onSavaInstanceState函數。在該函數中,開發人員可以講組件中的狀態資料寫入參數的outState對象中。outState的物件類型是Bundle,他是通過索引值對的方式進行資料的儲存。
onCreate —— 如果含有state資料,則先調用onRestoreInstanceState。
onRestoreInstanceState —— 組件進入前台狀態前,先調用恢複資料。
onSaveInstanceState —— 組件離開前台狀態,先調用狀態儲存資料,在調用onPause。如果使用者是主動離開前台狀態,則不會觸發該狀態。
boolean needSaveDraft = true; // 如果是非主動離開時,則會調用onSaveInstanceState @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // 先儲存修改狀態,用於恢複啟動時恢複該資訊 outState.putBoolean( "NEED_SAVE_DRAFT", needSaveDraft ); // 表示在被動退出時無需儲存 needSaveDraft = false ; } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); if (savedInstanceState != null) { needSaveDraft = savedInstanceState.getBoolean("NEED_SAVE_DRAFT" ); } } @Override protected void onPause() { super.onPause(); // 如果不是被動推動,則詢問使用者 if (needSaveDraft ) { showAskSaveDraftDialog(); } } private void showAskSaveDraftDialog() { // TODO Auto-generated method stub }
當前onSaveInstanceState函數調用完成後,儲存狀態資訊的outState對象中的資料就由系統進程代為保管,不論該應用進程是否被系統回收,這些資料都不會丟失。
如果savedInstanceState為空白,說明這是一次全新的構造,反之則說明這是一次恢複性的構造。介面組件可以利用該參數中的資訊將介面狀態恢複到系統回收前的狀態。
區分是恢複性構造還是全新的狗仔,是開發中需要妥善處理的細節。如果是全新的構造,介面組件中需要分析調用發送的Intent對象,控制商務程序;而如果是恢複性構造,則需要將上次緩衝的資訊一一恢複。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { needSaveDraft = savedInstanceState.getBoolean("NEED_SAVE_DRAFT" ); } else { // TODO 解析Intent對象傳入的參數或者Action // Intent intent = getIntent(); // intent.getBundleExtra(""); } }
為了降低開發人員的負擔,Android中的大部分的系統控制項都實現了狀態緩衝的邏輯。在onSaveInstanceState函數調用前,介面組件會遍曆整個控制項樹,將各個控制項儲存下來。等到onRestoreInstanceState函數被調用時在進行恢複。
如果系統內建的控制項狀態緩衝邏輯不符合開發人員的需求,開發人員可以調用View.setSaveEnabled函數關閉對應控制項對象的自動緩衝,在onSaveInstanceState函數中自行管理控制項的狀態。
用於狀態管理的onSaveInstanceState 和 onRestoreInstanceState並不屬於基本的生命週期函數,但是狀態管理的操作還是和組件的生命週期有必然的聯絡,開發人員同樣需要妥善利用好這些函數,處理由於生命週期變更引起的變化。
註冊管理
介面組件在於使用者互動的過程中有時候需要隨著系統狀態的變化及時的更新資訊。比如地址資訊。
介面組件可以通過監聽相關的事件資訊來捕獲這些變化。如果所監聽的事件的變化,僅當組件在前台狀態時才需要生效(比如廣播事件的監聽,地理位置的變更等),則需要早onResume中註冊,在onPause中登出。
LocationManager mLocationManager; LocationListener mLocationListener; @Override protected void onResume() { super.onResume(); mLocationManager.requestLocationUpdates(provider, minTime, minDistance, listener ); } @Override protected void onPause() { super.onPause(); mLocationManager.removeUpdates(mLocationListener ); }
線程管理
在應用開發中,網路通訊、資料操作、複雜計算等都需要耗費大量的時間,因此應用通常需要採用多線程的設計模式,在後台線程中執行此類耗時的操作。
Android的組件生命週期,是一個典型的同步處理邏輯。對於多線程架構沒有提供良好的支援模型。這個需要開發人員根據自己的需求,充分利用好組件的生命週期,合理的安排線程的構造及銷毀。
如果線程的生命週期和該組件的生命週期緊密聯絡,就需要在介面組件生命週期中管理該線程,一旦線程被介面組件構造出來,就需要在onDestory中明確終止該線程,回收其線程空間。否則,將導致線程資源泄漏。
但是僅在onDestory回收線程依然不夠完美,因為在資源緊張的情況下,系統會強行回收組件,此時組件的onDestory函數可能並沒有調用,從而導致線程資源泄漏。
一個更好的線程管理方案,是將線程的控制代碼資訊當做介面組件的狀態資訊緩衝下來。如果系統強行回收該對象組件,則需要在組件再次被構造時,根據緩衝的線程控制代碼找到該線程,從而避免線程泄露。
static final String THREAD_WORKER_ID = “thread_id”;
Thread workerThread;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { long threadId = savedInstanceState.getLong(THREAD_WORKER_ID); workerThread = findThreadById(threadId); } else { // TODO 建立新的線程 } } private Thread findThreadById( long id) { // 完善根據線程id,找到該線程 的過程 http://bao231.iteye.com/blog/1917616?utm_source=tuicool return null ; } @Override protected void onDestroy() { super.onDestroy(); if (workerThread != null) { workerThread.interrupt(); workerThread = null ; } }
服務元件的生命週期
服務的使用方式可分為兩種,分別是調用服務和綁定服務。這兩種不同的使用方式下,生命週期略微有不同。
但不論在何種使用模式下,組件的生命週期都是從onCreate中開始,至onDestory中結束。因為服務元件開發中,可以選擇在onCreate中做資料載入等初始化工作,而在onDestory中做資料銷毀,線程終止等清理工作。
在繫結模式下,onBind函數被調用時,說明服務以及被前台介面組件綁定。服務元件應根據調用者傳遞的Intent對象,在該函數內載入資源,構建通訊對象,等待綁定者的調用。當介面組件完成相關操作時,需會解除與服務元件的綁定。此時,onUnBind函數會被調用,可以在該函數中做一些統計和資源清理工作。
被綁定服務元件的進程狀態,與綁定該服務的介面組件密切相關。如果綁定組件為前台介面組件,則改服務所處的進程即為前台進程。反之也相同。
Android系統不會輕易回收前台進程或者可視進程,所以出於綁定狀態的組件通常也不會被強制停止。對於開發人員而言,綁定服務後一定不要忘記選擇在合適的時機接觸綁定,否則將使服務元件停留在前台或可視狀態無法回收,從而浪費系統資源。
在調用模式,當服務元件執行onStartCommand函數時,服務所在的進程為前台進程,擁有最高的優先順序。當onStartCommand函數執行完成後,如果沒有顯示的調用stopSelf等相關函數來停止服務元件,那麼該服務元件將會成為後台組件繼續提供服務,直至調用stopSelf函數停止,或者等待系統強行回收。
onStartCommand函數中增加三個傳回值和控制參數,用於指定後台服務元件的運行方式,其中最重要的傳回值有三個:
START_STICKY —— 系統會對該服務元件負責到底,在強行回收該組件後後,在資源寬裕的時候還會調用onStartCommand函數重新啟動該服務。直到調用stopSelf函數。對於開發人員而言,編寫傳回值為START_STRICKY,一定要在合適的時機調用stopSelf函數主動關閉服務,否則會無限期的消耗系統資源。
START_NOT_SRICKY —— 說明系統可以無條件的回收該組件,而無需關注服務是否完成,也不需要負責服務的重新啟動。
START_REDELIVER_INTENT —— 則意味著需要保障該服務元件能夠完整的處理完每一個Intent對象。
觸發器組件的生命週期:
觸發器的生命週期是最短暫的,其整個生命週期就是構造觸發器對象,然後執行onReceive函數。對於執行完onReceive函數,系統會立即出發銷毀觸發器的組件對象,回收其佔用的資源。
生命週期內,onReceive函數內部不能夠處理耗時任務。
資料來源組件的生命週期
理論上來說,資料來源組件沒有所謂的生命週期,因此資料來源組件的狀態不作為進程優先順序的判斷依據。所以系統在回收進程資源時,並不會將資料來源的銷毀事件告知開發人員。
但Android會在構造資料來源組件時調用onCreate函數。開發人員可以在該函數中資料化資料來源所需的資料庫或者其他資料內容。
由此可知,在資料來源組件中部署延遲寫入等寫最佳化策略是不合適,因為資料來源組件可能會被系統靜默回收,從而導致未持久化的寫入資料丟失。所以在資料來源組件的實現中,寫最佳化策略應該交由上層調用去實現,或者下層資料存放區者去處理。
一旦資料來源組件構造出來,就會保持長期啟動並執行狀態直至其所在的進程被系統回收。所以不要再資料來源組件中緩衝過多的資料,以免佔用記憶體空間。
Android應用常規開發技巧——善用組件生命週期