本文詳細介紹了 Android 應(yīng)用編程中 Activity 的生命周期、通信方式和 Intent Filter 等內(nèi)容,并提供了一些日常開發(fā)中經(jīng)常用到的關(guān)于 Activity 的技巧和方法。通過本文,你可以進一步了接 Android 中 Activity 的運作方式。
和 J2ME 的 MIDlet 一樣,在 android 中,Activity 的生命周期交給系統(tǒng)統(tǒng)一管理。與 MIDlet 不同的是安裝在 android 中的所有的 Activity 都是平等的。
Activity 的狀態(tài)及狀態(tài)間的轉(zhuǎn)換
在 android 中,Activity 擁有四種基本狀態(tài):
- Active/Runing 一個新 Activity 啟動入棧后,它在屏幕最前端,處于棧的最頂端,此時它處于可見并可和用戶交互的激活狀態(tài)。
- Paused 當 Activity 被另一個透明或者 Dialog 樣式的 Activity 覆蓋時的狀態(tài)。此時它依然與窗口管理器保持連接,系統(tǒng)繼續(xù)維護其內(nèi)部狀態(tài),所以它仍然可見,但它已經(jīng)失去了焦點故不可與用戶交互。
- Stoped 當 Activity 被另外一個 Activity 覆蓋、失去焦點并不可見時處于 Stoped 狀態(tài)。
- Killed Activity 被系統(tǒng)殺死回收或者沒有被啟動時處于 Killed 狀態(tài)。
當一個 Activity 實例被創(chuàng)建、銷毀或者啟動另外一個 Activity 時,它在這四種狀態(tài)之間進行轉(zhuǎn)換,這種轉(zhuǎn)換的發(fā)生依賴于用戶程序的動作。下圖說明了 Activity 在不同狀態(tài)間轉(zhuǎn)換的時機和條件:
圖 1. Activity 的狀態(tài)轉(zhuǎn)換

如上所示,Android 程序員可以決定一個 Activity 的“生”,但不能決定它的“死”,也就時說程序員可以啟動一個 Activity,但是卻不能手動的“結(jié)束”一個 Activity。當你調(diào)用 Activity.finish() 方法時,結(jié)果和用戶按下 BACK 鍵一樣:告訴 Activity Manager 該 Activity 實例完成了相應(yīng)的工作,可以被“回收”。隨后 Activity Manager 激活處于棧第二層的 Activity 并重新入棧,同時原 Activity 被壓入到棧的第二層,從 Active 狀態(tài)轉(zhuǎn)到 Paused 狀態(tài)。例如:從 Activity1 中啟動了 Activity2,則當前處于棧頂端的是 Activity2,第二層是 Activity1,當我們調(diào)用 Activity2.finish() 方法時,Activity Manager 重新激活 Activity1 并入棧,Activity2 從 Active 狀態(tài)轉(zhuǎn)換 Stoped 狀態(tài), Activity1. onActivityResult(int requestCode, int resultCode, Intent data) 方法被執(zhí)行,Activity2 返回的數(shù)據(jù)通過 data 參數(shù)返回給 Activity1。
Android 是通過一種 Activity 棧的方式來管理 Activity 的,一個 Activity 的實例的狀態(tài)決定它在棧中的位置。處于前臺的 Activity 總是在棧的頂端,當前臺的 Activity 因為異常或其它原因被銷毀時,處于棧第二層的 Activity 將被激活,上浮到棧頂。當新的 Activity 啟動入棧時,原 Activity 會被壓入到棧的第二層。一個 Activity 在棧中的位置變化反映了它在不同狀態(tài)間的轉(zhuǎn)換。Activity 的狀態(tài)與它在棧中的位置關(guān)系如下圖所示:
圖 2. Activity 的狀態(tài)與它在棧中的位置關(guān)系

如上所示,除了最頂層即處在 Active 狀態(tài)的 Activity 外,其它的 Activity 都有可能在系統(tǒng)內(nèi)存不足時被回收,一個 Activity 的實例越是處在棧的底層,它被系統(tǒng)回收的可能性越大。系統(tǒng)負責(zé)管理棧中 Activity 的實例,它根據(jù) Activity 所處的狀態(tài)來改變其在棧中的位置。
在 android.app.Activity 類中,Android 定義了一系列與生命周期相關(guān)的方法,在我們自己的 Activity 中,只是根據(jù)需要復(fù)寫需要的方法,Java 的多態(tài)性會保證我們自己的方法被虛擬機調(diào)用,這一點與 J2ME 中的 MIDlet 類似。
public class OurActivity extends Activity { protected void onCreate(Bundle savedInstanceState); protected void onStart(); protected void onResume(); protected void onPause(); protected void onStop(); protected void onDestroy(); } |
這些方法的說明如下:
- protected void onCreate(Bundle savedInstanceState) 一個 Activity 的實例被啟動時調(diào)用的第一個方法。一般情況下,我們都覆蓋該方法作為應(yīng)用程序的一個入口點,在這里做一些初始化數(shù)據(jù)、設(shè)置用戶界面等工作。大多數(shù)情況下,我們都要在這里從 xml 中加載設(shè)計好的用戶界面。例如:
setContentView(R.layout.main); |
當然,也可從 savedInstanceState 中讀我們保存到存儲設(shè)備中的數(shù)據(jù),但是需要判斷 savedInstanceState 是否為 null ,因為 Activity 第一次啟動時并沒有數(shù)據(jù)被存貯在設(shè)備中:
if(savedInstanceState!=null){ savedInstanceState.get("Key"); } |
- protected void onStart() 該方法在 onCreate() 方法之后被調(diào)用,或者在 Activity 從 Stop 狀態(tài)轉(zhuǎn)換為 Active 狀態(tài)時被調(diào)用。
- protected void onResume() 在 Activity 從 Pause 狀態(tài)轉(zhuǎn)換到 Active 狀態(tài)時被調(diào)用。
- protected void onResume() 在 Activity 從 Active 狀態(tài)轉(zhuǎn)換到 Pause 狀態(tài)時被調(diào)用。
- protected void onStop() 在 Activity 從 Active 狀態(tài)轉(zhuǎn)換到 Stop 狀態(tài)時被調(diào)用。一般我們在這里保存 Activity 的狀態(tài)信息。
- protected void onDestroy() 在 Active 被結(jié)束時調(diào)用,它是被結(jié)束時調(diào)用的最后一個方法,在這里一般做些釋放資源,清理內(nèi)存等工作。
圖 3. 這些方法的調(diào)用時機

此外,Android 還定義了一些不常用的與生命周期相關(guān)的方法可用:
protected void onPostCreate(Bundle savedInstanceState); protected void onRestart(); protected void onPostResume(); |
Android 提供的文檔詳細的說明了它們的調(diào)用規(guī)則。
在 android 中創(chuàng)建一個 Activity 是很簡單的事情,編寫一個繼承自 android.app.Activity 的 Java 類并在 AndroidManifest.xml 聲明即可。下面是一個為了研究 Activity 生命周期的一個 Activity 實例(工程源碼見下載):
Activity 文件:
public class EX01 extends Activity { private static final String LOG_TAG = EX01.class.getSimpleName(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Log.e(LOG_TAG, "onCreate"); } @Override protected void onStart() { Log.e(LOG_TAG, "onStart"); super.onStart(); } @Override protected void onResume() { Log.e(LOG_TAG, "onResume"); super.onResume(); } @Override protected void onPause() { Log.e(LOG_TAG, "onPause"); super.onPause(); } @Override protected void onStop() { Log.e(LOG_TAG, "onStop"); super.onStop(); } @Override protected void onDestroy() { Log.e(LOG_TAG, "onDestroy "); super.onDestroy(); } } |
AndroidManifest.xml 中通過 <activity> 節(jié)點說明 Activity,將 apk 文件安裝后,系統(tǒng)根據(jù)這里的說明來查找讀取 Activity,本例中的說明如下:
<activity android:name=".EX01" 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.startActivity() 方法可以根據(jù)傳入的參數(shù)啟動另外一個 Activity:
Intent intent =new Intent(CurrentActivity.this,OtherActivity.class); startActivity(intent); |
當然, OtherActivity 同樣需要在 AndroidManifest.xml 中定義。
在 Android 中,不同的 Activity 實例可能運行在一個進程中,也可能運行在不同的進程中。因此我們需要一種特別的機制幫助我們在 Activity 之間傳遞消息。Android 中通過 Intent 對象來表示一條消息,一個 Intent 對象不僅包含有這個消息的目的地,還可以包含消息的內(nèi)容,這好比一封 Email,其中不僅應(yīng)該包含收件地址,還可以包含具體的內(nèi)容。對于一個 Intent 對象,消息“目的地”是必須的,而內(nèi)容則是可選項。
在上面的實例中通過 Activity. startActivity(intent) 啟動另外一個 Activity 的時候,我們在 Intent 類的構(gòu)造器中指定了“收件人地址”。
如果我們想要給“收件人”Activity 說點什么的話,那么可以通過下面這封“e-mail”來將我們消息傳遞出去:
Intent intent =new Intent(CurrentActivity.this,OtherActivity.class); // 創(chuàng)建一個帶“收件人地址”的 email Bundle bundle =new Bundle();// 創(chuàng)建 email 內(nèi)容 bundle.putBoolean("boolean_key", true);// 編寫內(nèi)容 bundle.putString("string_key", "string_value"); intent.putExtra("key", bundle);// 封裝 email startActivity(intent);// 啟動新的 Activity |
那么“收件人”該如何收信呢?在 OtherActivity 類的 onCreate() 或者其它任何地方使用下面的代碼就可以打開這封“e-mail”閱讀其中的信息:
Intent intent =getIntent();// 收取 email Bundle bundle =intent.getBundleExtra("key");// 打開 email bundle.getBoolean("boolean_key");// 讀取內(nèi)容 bundle.getString("string_key"); |
上面我們通過 bundle 對象來傳遞信息, bundle 維護了一個 HashMap<String, Object> 對象,將我們的數(shù)據(jù)存貯在這個 HashMap 中來進行傳遞。但是像上面這樣的代碼稍顯復(fù)雜,因為 Intent 內(nèi)部為我們準備好了一個 bundle ,所以我們也可以使用這種更為簡便的方法:
Intent intent =new Intent(EX06.this,OtherActivity.class); intent.putExtra("boolean_key", true); intent.putExtra("string_key", "string_value"); startActivity(intent); |
接收:
Intent intent=getIntent(); intent.getBooleanExtra("boolean_key",false); intent.getStringExtra("string_key"); |
SharedPreferences 使用 xml 格式為 Android 應(yīng)用提供一種永久的數(shù)據(jù)存貯方式。對于一個 Android 應(yīng)用,它存貯在文件系統(tǒng)的 /data/ data/your_app_package_name/shared_prefs/ 目錄下,可以被處在同一個應(yīng)用中的所有 Activity 訪問。Android 提供了相關(guān)的 API 來處理這些數(shù)據(jù)而不需要程序員直接操作這些文件或者考慮數(shù)據(jù)同步問題。
// 寫入 SharedPreferences SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE); Editor editor = preferences.edit(); editor.putBoolean("boolean_key", true); editor.putString("string_key", "string_value"); editor.commit(); // 讀取 SharedPreferences SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE); preferences.getBoolean("boolean_key", false); preferences.getString("string_key", "default_value"); |
Android 提供了包括 SharedPreferences 在內(nèi)的很多種數(shù)據(jù)存貯方式,比如 SQLite,文件等,程序員可以通過這些 API 實現(xiàn) Activity 之間的數(shù)據(jù)交換。如果必要,我們還可以使用 IPC 方式。
Intent Filter 描述了一個組件愿意接收什么樣的 Intent 對象,Android 將其抽象為 android.content.IntentFilter 類。在 Android 的 AndroidManifest.xml 配置文件中可以通過 <intent-filter > 節(jié)點為一個 Activity 指定其 Intent Filter,以便告訴系統(tǒng)該 Activity 可以響應(yīng)什么類型的 Intent。
當程序員使用 startActivity(intent) 來啟動另外一個 Activity 時,如果直接指定 intent 了對象的 Component 屬性,那么 Activity Manager 將試圖啟動其 Component 屬性指定的 Activity。否則 Android 將通過 Intent 的其它屬性從安裝在系統(tǒng)中的所有 Activity 中查找與之最匹配的一個啟動,如果沒有找到合適的 Activity,應(yīng)用程序會得到一個系統(tǒng)拋出的異常。這個匹配的過程如下:
圖 4. Activity 種 Intent Filter 的匹配過程

Action 是一個用戶定義的字符串,用于描述一個 Android 應(yīng)用程序組件,一個 Intent Filter 可以包含多個 Action。在 AndroidManifest.xml 的 Activity 定義時可以在其 <intent-filter > 節(jié)點指定一個 Action 列表用于標示 Activity 所能接受的“動作”,例如:
<intent-filter > <action android:name="android.intent.action.MAIN" /> <action android:name="com.zy.myaction" /> …… </intent-filter> |
如果我們在啟動一個 Activity 時使用這樣的 Intent 對象:
Intent intent =new Intent(); intent.setAction("com.zy.myaction"); |
那么所有的 Action 列表中包含了“ com.zy.myaction ”的 Activity 都將會匹配成功。
Android 預(yù)定義了一系列的 Action 分別表示特定的系統(tǒng)動作。這些 Action 通過常量的方式定義在 android.content. Intent 中,以“ ACTION_ ”開頭。我們可以在 Android 提供的文檔中找到它們的詳細說明。
一個 Intent 可以通過 URI 攜帶外部數(shù)據(jù)給目標組件。在 <intent-filter > 節(jié)點中,通過 <data/> 節(jié)點匹配外部數(shù)據(jù)。
mimeType 屬性指定攜帶外部數(shù)據(jù)的數(shù)據(jù)類型,scheme 指定協(xié)議,host、port、path 指定數(shù)據(jù)的位置、端口、和路徑。如下:
<data android:mimeType="mimeType" android:scheme="scheme" android:host="host" android:port="port" android:path="path"/> |
如果在 Intent Filter 中指定了這些屬性,那么只有所有的屬性都匹配成功時 URI 數(shù)據(jù)匹配才會成功。
<intent-filter > 節(jié)點中可以為組件定義一個 Category 類別列表,當 Intent 中包含這個列表的所有項目時 Category 類別匹配才會成功。
Android 內(nèi)置了方向感應(yīng)器的支持。在 G1 中,Android 會根據(jù) G1 所處的方向自動在豎屏和橫屏間切換。但是有時我們的應(yīng)用程序僅能在橫屏 / 豎屏?xí)r運行,比如某些游戲,此時我們需要鎖定該 Activity 運行時的屏幕方向, <activity > 節(jié)點的 android:screenOrientation 屬性可以完成該項任務(wù),示例代碼如下:
<activity android:name=".EX01" android:label="@string/app_name" android:screenOrientation="portrait">// 豎屏 , 值為 landscape 時為橫屏 ………… </activity> |
要使一個 Activity 全屏運行,可以在其 onCreate() 方法中添加如下代碼實現(xiàn):
// 設(shè)置全屏模式 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // 去除標題欄 requestWindowFeature(Window.FEATURE_NO_TITLE); |
為了更友好的用戶體驗,在處理一些需要花費較長時間的任務(wù)時可以使用一個進度條來提示用戶“不要著急,我們正在努力的完成你交給的任務(wù)”。如下圖:
在 Activity 的標題欄中顯示進度條不失為一個好辦法,下面是實現(xiàn)代碼:
// 不明確進度條 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); setContentView(R.layout.main); setProgressBarIndeterminateVisibility(true); // 明確進度條 requestWindowFeature(Window.FEATURE_PROGRESS); setContentView(R.layout.main); setProgress(5000); |
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
