這一篇我們主要講下應用程式生命週期、Activity生命週期、Intent在Activity中的應用。
一、應用程式生命週期
與大多數傳統的作業系統環境不同,Android應用程式並不能控制自己的生命週期。所以應用的各個組件(activity,Service……)就得時刻小心的監聽應用的狀態變化對它們的影響,防止在不適當的時機被終止掉。
在Android中每個應用都具有獨立的進程運行在獨立的Dalvik(Android特有的虛擬機器)。各個應用在運行時的進程管理和記憶體管理都是相對獨立的。Android使用一種"侵佔性"的方式管理系統資源,這意味著為了釋放資源給高優先順序的程式(通常情況下是正在與使用者進行直接互動的程式),某些進程及其宿主程式將會在沒有任何提示警告的情況下被無情的結束。
理解應用的優先順序和進程狀態
結束一些進程是為了釋放回收資源,那麼那些進程會被結束?是A先結束還是B先結束呢?這決定於宿主應用程式的優先順序了。一個應用的優先順序等同於具有最高優先順序的組件的優先順序。如果此刻兩個程式具有相同的優先順序,那麼曾經哪個進程處於低優先順序的時間較長,就會被結束掉。進程的優先順序受到進程間附屬關係的影響,比如A應用依賴的Service或者Content Provider是由B應用提供的,那麼B將會具有更高的優先順序。所有的Android應用都會遺留在記憶體中運行,直到系統需要釋放回收資源才會被結束掉。各種進程及其對應的優先順序。
Activity Process 活動進程是其宿主程式更使用者直接互動的組件,Android會嘗試通過回收各種資源來保證其運行狀態。此類進程數量少,通常是最後被結束掉的。
活動進程包含下面幾種:
- Activity處於啟用狀態,也就是說處於foreground,接收響應使用者事件
- Activities, Services, or Broadcast Receivers 正在執行處理OnReceive 事件
- Services 正在執行處理 onStart, onCreate, or onDestroy 事件
Visible Process 可視化,但是不處於活動狀態的Activity。它們不處於foreground也不接受響應使用者事件。這中情況發生在Activity有部分被覆蓋了(比如我們點擊A裡面的一個按鈕,彈出一個Dialog,此時A就變成一個可視進程)。可視進程同樣很少,只有在極端的情況下,為了保證Activity Process的運行才會被結束掉。
Started Service Process 注意,是啟動後的服務進程。服務進程需要在沒有可視化介面的情況下持續運行,因為服務沒有和使用者直接互動。它們依然被認為是前台進程,只有當Visible和Activity Process需要資源的時候才會被結束。
Background Process 一個進程附屬在不可視的Activity 也沒有任何啟動的服務進程就變成了後台進程。通常情況下Android裡面會有一大堆後台進程,Android用 last-seen-first-killed 這種方式來結束此類進程,從而為foreground進程提供資源。
Empty Process 為了改善整個系統的效能,Android會在記憶體中保留已經結束生命週期的應用程式。Android保留這些緩衝為了是應用能夠快速啟動。它們將會按照一貫的情況被結束掉。
綜上所述,正確的組織應用程式,確保穩定運行,防止運行中途被終止是非常重要的。
二、Activity的生命週期
正確理解Activity的生命週期是非常重要的,只有正確理解Activity的生命週期,才能確保應用程式提供一個符合邏輯的的使用者體驗以及正確管理應用程式本身的資源。Android中的應用程式並不能管理自身的生命週期,而是由系統統一管理的,當然Activity也是這樣子的。在運行時管理以及是否結束Activity進程,Activity的狀態決定了應用程式的優先順序。反過來,程式的優先順序也影響著在運行時是否會終止Activity以及保持Activity持續運行。
1、活動棧
活動在棧中的位置決定了活動狀態。當一個新的活動啟動時,這個活動就會被當作前景螢幕,並被放到活動棧的棧頂。當使用者觸發"返回"事件或者前景活動關閉時,下一個活動就會移到棧頂並啟用。如所示:
之前說過,Android中應用程式的優先順序是由其最高優先順序的組件(一般就是Activity)決定的。因此當Android需要終止某些應用程式釋放系統資源時,就依據活動棧來決定應用程式優先順序的高低,從而終止那些低優先順序的應用程式。
2、活動狀態
- 啟用 當一個活動處於活動棧棧頂,它是可見的,可設定焦點,可接受使用者的輸入。Android會試圖不惜一切代價,甚至是終止處於棧底的活動來回收資源,以保證棧頂活動對資源的需求。當有新的活動被啟用,這個活動就會被暫停或者停止。
- 暫停 活動可見,但是不能聚焦,此時活動就處於暫停狀態。當活動暫停時無法接受使用者事件。在極端的情況下,Android會終止一個處於暫停狀態的活動,來回收系統資源。當一個活動被完全覆蓋,它就處於停止狀態。
- 停止 當一個活動不可見,它就處於停止狀態。活動會將狀態和組件資訊保留在記憶體中。當一個活動轉入停止狀態,我們需要儲存資料和UI狀態。一個活動關閉或者退出,就進入不活動狀態。
- 不活動(睡眠) 活動被關閉後,在重新啟動之前就處於不活動狀態。此狀態的Activity並不會存在活動棧中,在顯示和使用之前需要重新啟動。
Activity狀態的改變是不確定的,這是完全由Android的記憶體管理器控制的。Android會事先關閉那些含有處於睡眠狀態,停止狀態甚至是暫停狀態活動的應用程式。為了提供一個良好的使用者體驗,Activity狀態的改變對使用者來說應該是透明的,所以當Activity進入暫停和停止狀態,儲存UI狀態和持久化資料是很重要的。一旦活動重新啟用,則恢複相關資料。
3、監聽活動狀態變化
為了確保對活動狀態變化作出正確的響應,Android提供一個系列的事件控制器,監聽活動的在整個生命週期的狀態變化。活動的生命週期進一步細化,可以分為:全周期(Full Lifetime)>可視期(Visible Lifetime)>啟用期(Active Lifetime),如所示:
如所示,Activity狀態的改變都會觸發對應的方法。
Full Lifetime 全周期介於調用 onCreate 與 onDestroy 之間。在一些情況下,終止一個活動並不調用 onDestroy 。Activity通過調用 onCreate 來初始化使用者介面,資料,啟動服務以及線程。onCreate 方法有個Bundle對象參數,這個參數含有最後一次調用 onSaveIntanceState 儲存的UI狀態資料。我們可以在 onCreate 中利用這個參數來恢複UI狀態資料,或者重寫 onRestoreInstanceState 。重寫 onDestroy 釋放資源,關閉資料庫等操作。
為了寫出比較高效的代碼,有個比較好的建議是避免建立短期對象。快速的建立和銷毀對象容易增加片段回收線程的壓力,這樣會直接影響使用者體驗。
Visible Lifetime 可視期介於調用 onStart 與 onStop 之間。此時Activity是可見的,但是不能響應使用者事件。一個Activity在其生命週期中是有可能經曆多個可視期的。在非常極端的情況下,系統也有可能終止一個處於可視期的活動,這種情況很少見。onStop 方法通常用於暫時或者停止那些用來更新UI的動畫,線程,定時器,服務等,所以當活動不可見的時佔用的系統資源是很少的。當活動由不可見狀態轉化為可見狀態時,在 onStart 中再啟動相關的線程和服務。onStart 和 onStop 同樣也用來註冊和取消註冊(unregister)那些用來更新UI的廣播接收者。在活動不可見時我們需要取消註冊接受者(Receivers),特別是那些支援目的動作(Intent)以及更新UI的接收者。
Activity Lifetime 當活動的 onResume 被調用,活動進入啟用期;當 onPause 被調用時,啟用期結束。一個活動被啟用,它將處於前景螢幕,同時響應使用者的事件。同樣在活動的生命週期結束之前,存在多個啟用期,一旦有新的活動被啟用,當前活動將會失去焦點(暫停甚至停止……)。啟用期是活動生命週期比較活躍的部分,會頻繁調用 onResume 和 onPause ,所以為了有良好的使用者體驗,在 onResume 和 onPause 方法中的代碼需要有更高的效率。
在調用 onPause 之前,活動會調用 onSaveInstanceState 來儲存活動的UI狀態到 Bundle ,這個 Bundle 就是 onCreate 和 onRestoreInstanceState 這兩個方法的參數了。onSaveInstanceState 儲存UI狀態(比如多選按鈕的狀態,介面焦點=),這樣當活動被重新啟用時,能夠正確顯示被暫停前的UI。多數的活動都重寫 onPause 方法來提交未儲存的資料,我們也可以在此選擇是否暫停線程,廣播接收者,這完全依軟體架構本身而定。在 onResume 中一般不執行太多的代碼,建立UI一般在 onCreate 或者 onRestoreInstanceState 中實現。
三、Intent
Intent的中文意思是目的。在Android中也是"目的"的意思。就是我們要去哪裡,從這個activity要前往另一個Activity就需要用到Intent。
範例程式碼一:
//定義一個Intent
Intent intent = new Intent(IntentDemo.this, AnotherActivity2.class);
//啟動Activity
startActivity(intent);
以上範例程式碼的作用是從IntentDemo這個activity切換到AnotherActivity2。這是Intent其中一種構造方法,指定兩個Activity。為什麼需要指定兩個活動呢?因為在Android中有一個活動棧,這樣的構造方式才能確保正確的將前一個活動壓入棧中,才能在觸發返回鍵的時候活動能夠正確出棧。
注意:所有的Activity都必須先在AndroidManifest.xml裡面配置聲明。一下為本文用到的程式設定檔
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.halzhang.android.intent" android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".IntentDemo" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".AnotherActivity" android:label="another">
<intent-filter>
<action android:name="android.intent.action.EDIT" />
<!-- category一定要配置,否則報錯:找不到Activity -->
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".AnotherActivity2" android:label="another2">
<intent-filter>
<action android:name="android.intent.action.EDIT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="3" />
<!--
上面配置的兩個activity具有相同的action類型,都為"android.intent.action.EDIT"
當Intent的action屬性為Intent.ACTION_EDIT時,系統不知道轉向哪個Activity時,
就會彈出一個Dialog列出所有action為"android.intent.action.EDIT"的
Activity供使用者選擇
-->
</manifest>
1、Intent的建構函式
公用建構函式:
1、Intent() 空建構函式
2、Intent(Intent o) 拷貝建構函式
3、Intent(String action) 指定action類型的建構函式
4、Intent(String action, Uri uri) 指定Action類型和Uri的建構函式,URI主要是結合程式之間的資料共用ContentProvider
5、Intent(Context packageContext, Class<?> cls) 傳入組件的建構函式,也就是上文提到的
6、Intent(String action, Uri uri, Context packageContext, Class<?> cls) 前兩種結合體
Intent有六種建構函式,3、4、5是最常用的,並不是其他沒用!
Intent(String action, Uri uri) 的action就是對應在AndroidMainfest.xml中的action節點的name屬性值。在Intent類中定義了很多的Action和Category常量。
範例程式碼二:
1: Intent intent = new Intent(Intent.ACTION_EDIT, null);
2: startActivity(intent);
範例程式碼二是用了第四種建構函式,只是uri參數為null。執行此代碼的時候,系統就會在程式主設定檔AndroidMainfest.xml中尋找
<action android:name="android.intent.action.EDIT" />對應的Activity,如果對應為多個activity具有<action android:name="android.intent.action.EDIT" />此時就會彈出一個dailog選擇Activity,如右圖:
如果是用範例程式碼一那種方式進行發送則不會有這種情況。
2、利用Intent在Activity之間傳遞資料
在Main中執行如下代碼:
Bundle bundle = new Bundle();
bundle.putStringArray("NAMEARR", nameArr);
Intent intent = new Intent(Main.this, CountList.class);
intent.putExtras(bundle);
startActivity(intent);
在CountList中,代碼如下:
Bundle bundle = this.getIntent().getExtras();
String[] arrName = bundle.getStringArray("NAMEARR");
以上代碼就實現了Activity之間的資料傳遞!