編輯:中級開發
簡介: 本文詳細介紹了 Android 應用編程中 Activity 的生命周期、通信方式和 Intent Filter 等內容,並提供了一些日常開發中經常用到的關於 Activity 的技巧和方法。通過本文,你可以進一步了接 android 中 Activity 的運作方式。
Activity 的生命周期
和 J2ME 的 MIDlet 一樣,在 android 中,Activity 的生命周期交給系統統一管理。與 MIDlet 不同的是安裝在 android 中的所有的 Activity 都是平等的。
Activity 的狀態及狀態間的轉換
在 android 中,Activity 擁有四種基本狀態:
當一個 Activity 實例被創建、銷毀或者啟動另外一個 Activity 時,它在這四種狀態之間進行轉換,這種轉換的發生依賴於用戶程序的動作。下圖說明了 Activity 在不同狀態間轉換的時機和條件:
圖 1. Activity 的狀態轉換
如上所示,android 程序員可以決定一個 Activity 的“生”,但不能決定它的“死”,也就時說程序員可以啟動一個 Activity,但是卻不能手動的“結束”一個 Activity。當你調用Activity.finish()方法時,結果和用戶按下 BACK 鍵一樣:告訴 Activity Manager 該 Activity 實例完成了相應的工作,可以被“回收”。隨後 Activity Manager 激活處於棧第二層的 Activity 並重新入棧,同時原 Activity 被壓入到棧的第二層,從 Active 狀態轉到 Paused 狀態。例如:從 Activity1 中啟動了 Activity2,則當前處於棧頂端的是 Activity2,第二層是 Activity1,當我們調用 Activity2.finish()方法時,Activity Manager 重新激活 Activity1 並入棧,Activity2 從 Active 狀態轉換 Stoped 狀態,Activity1. onActivityResult(int requestCode, int resultCode, Intent data)方法被執行,Activity2 返回的數據通過 data參數返回給 Activity1。
Activity 棧
android 是通過一種 Activity 棧的方式來管理 Activity 的,一個 Activity 的實例的狀態決定它在棧中的位置。處於前台的 Activity 總是在棧的頂端,當前台的 Activity 因為異常或其它原因被銷毀時,處於棧第二層的 Activity 將被激活,上浮到棧頂。當新的 Activity 啟動入棧時,原 Activity 會被壓入到棧的第二層。一個 Activity 在棧中的位置變化反映了它在不同狀態間的轉換。Activity 的狀態與它在棧中的位置關系如下圖所示:
圖 2. Activity 的狀態與它在棧中的位置關系
如上所示,除了最頂層即處在 Active 狀態的 Activity 外,其它的 Activity 都有可能在系統內存不足時被回收,一個 Activity 的實例越是處在棧的底層,它被系統回收的可能性越大。系統負責管理棧中 Activity 的實例,它根據 Activity 所處的狀態來改變其在棧中的位置。
Activity 生命周期
在 android.app.Activity類中,android 定義了一系列與生命周期相關的方法,在我們自己的 Activity 中,只是根據需要復寫需要的方法,Java 的多態性會保證我們自己的方法被虛擬機調用,這一點與 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(); }
這些方法的說明如下:
setContentVIEw(R.layout.main);
當然,也可從 savedInstanceState中讀我們保存到存儲設備中的數據,但是需要判斷 savedInstanceState是否為 null,因為 Activity 第一次啟動時並沒有數據被存貯在設備中:
if(savedInstanceState!=null){ savedInstanceState.get("Key"); }
圖 3. 這些方法的調用時機
此外,android 還定義了一些不常用的與生命周期相關的方法可用:
protected void onPostCreate(Bundle savedInstanceState); protected void onRestart(); protected void onPostResume();
android 提供的文檔詳細的說明了它們的調用規則。
創建一個 Activity
在 android 中創建一個 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> 節點說明 Activity,將 apk 文件安裝後,系統根據這裡的說明來查找讀取 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
Activity.startActivity()方法可以根據傳入的參數啟動另外一個 Activity:
Intent intent =new Intent(CurrentActivity.this,OtherActivity.class); startActivity(intent);
當然,OtherActivity同樣需要在 androidManifest.XML 中定義。
使用 Intent 通信
在 Android 中,不同的 Activity 實例可能運行在一個進程中,也可能運行在不同的進程中。因此我們需要一種特別的機制幫助我們在 Activity 之間傳遞消息。android 中通過 Intent 對象來表示一條消息,一個 Intent 對象不僅包含有這個消息的目的地,還可以包含消息的內容,這好比一封 Email,其中不僅應該包含收件地址,還可以包含具體的內容。對於一個 Intent 對象,消息“目的地”是必須的,而內容則是可選項。
在上面的實例中通過 Activity. startActivity(intent)啟動另外一個 Activity 的時候,我們在 Intent 類的構造器中指定了“收件人地址”。
如果我們想要給“收件人”Activity 說點什麼的話,那麼可以通過下面這封“e-mail”來將我們消息傳遞出去:
Intent intent =new Intent(CurrentActivity.this,OtherActivity.class); // 創建一個帶“收件人地址”的 email Bundle bundle =new Bundle();// 創建 email 內容 bundle.putBoolean("boolean_key", true);// 編寫內容 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");// 讀取內容 bundle.getString("string_key");
上面我們通過 bundle對象來傳遞信息,bundle維護了一個HashMap<String, Object>對象,將我們的數據存貯在這個 HashMap 中來進行傳遞。但是像上面這樣的代碼稍顯復雜,因為 Intent 內部為我們准備好了一個 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
SharedPreferences 使用 XML 格式為 Android 應用提供一種永久的數據存貯方式。對於一個 android 應用,它存貯在文件系統的/data/ data/your_app_package_name/shared_prefs/目錄下,可以被處在同一個應用中的所有 Activity 訪問。android 提供了相關的 API 來處理這些數據而不需要程序員直接操作這些文件或者考慮數據同步問題。
// 寫入 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 在內的很多種數據存貯方式,比如 SQLite,文件等,程序員可以通過這些 API 實現 Activity 之間的數據交換。如果必要,我們還可以使用 IPC 方式。
Activity 的 Intent Filter
Intent Filter 描述了一個組件願意接收什麼樣的 Intent 對象,Android 將其抽象為 android.content.IntentFilter 類。在 Android 的 androidManifest.XML 配置文件中可以通過 <intent-filter >節點為一個 Activity 指定其 Intent Filter,以便告訴系統該 Activity 可以響應什麼類型的 Intent。
當程序員使用 startActivity(intent) 來啟動另外一個 Activity 時,如果直接指定 intent 了對象的 Component 屬性,那麼 Activity Manager 將試圖啟動其 Component 屬性指定的 Activity。否則 android 將通過 Intent 的其它屬性從安裝在系統中的所有 Activity 中查找與之最匹配的一個啟動,如果沒有找到合適的 Activity,應用程序會得到一個系統拋出的異常。這個匹配的過程如下:
圖 4. Activity 種 Intent Filter 的匹配過程
Action 匹配
Action 是一個用戶定義的字符串,用於描述一個 Android 應用程序組件,一個 Intent Filter 可以包含多個 Action。在 androidManifest.XML 的 Activity 定義時可以在其 <intent-filter >節點指定一個 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 預定義了一系列的 Action 分別表示特定的系統動作。這些 Action 通過常量的方式定義在 android.content. Intent中,以“ACTION_”開頭。我們可以在 android 提供的文檔中找到它們的詳細說明。
URI 數據匹配
一個 Intent 可以通過 URI 攜帶外部數據給目標組件。在 <intent-filter >節點中,通過 <data/>節點匹配外部數據。
mimeType 屬性指定攜帶外部數據的數據類型,scheme 指定協議,host、port、path 指定數據的位置、端口、和路徑。如下:
<data android:mimeType="mimeType" android:scheme="scheme" android:host="host" android:port="port" android:path="path"/>
如果在 Intent Filter 中指定了這些屬性,那麼只有所有的屬性都匹配成功時 URI 數據匹配才會成功。
Category 類別匹配
<intent-filter >節點中可以為組件定義一個 Category 類別列表,當 Intent 中包含這個列表的所有項目時 Category 類別匹配才會成功。
一些關於 Activity 的技巧
鎖定 Activity 運行時的屏幕方向
Android 內置了方向感應器的支持。在 G1 中,android 會根據 G1 所處的方向自動在豎屏和橫屏間切換。但是有時我們的應用程序僅能在橫屏 / 豎屏時運行,比如某些游戲,此時我們需要鎖定該 Activity 運行時的屏幕方向,<activity >節點的android:screenOrIEntation屬性可以完成該項任務,示例代碼如下:
<activity android:name=".EX01" android:label="@string/app_name" android:screenOrIEntation="portrait">// 豎屏 , 值為 landscape 時為橫屏 ………… </activity>
全屏的 Activity
要使一個 Activity 全屏運行,可以在其 onCreate()方法中添加如下代碼實現:
// 設置全屏模式 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // 去除標題欄 requestWindowFeature(Window.FEATURE_NO_TITLE);
在 Activity 的 Title 中加入進度條
為了更友好的用戶體驗,在處理一些需要花費較長時間的任務時可以使用一個進度條來提示用戶“不要著急,我們正在努力的完成你交給的任務”。如下圖:
在 Activity 的標題欄中顯示進度條不失為一個好辦法,下面是實現代碼:
// 不明確進度條 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); setContentView(R.layout.main); setProgressBarIndeterminateVisibility(true); // 明確進度條 requestWindowFeature(Window.FEATURE_PROGRESS); setContentVIEw(R.layout.main); setProgress(5000);
簡介: 對於需要跨應用程序執行期間或生命期而維護重要信息的應用程序來說,能夠在移動設備上本地存儲數據是一種非常關鍵的功能。作為一名開發人員,您經常需要存儲諸如
兩位Google開發者演示了Android 3.0——該開源移動操作系統的首個支持平板設備的版本。Android 3.0也被稱為蜂巢(Honeycomb),這也是第一個
很多開發者不知道ListVIEw列表控件的快速滾動滑塊是如何啟用的,這裡android開發網告訴大家,輔助滾動滑塊只需要一行代碼就可以搞定,如果你使用XML布局只需要在
從Android 3.1開始將支持USB配件連接到Android設備,通過附件通訊協議,同時在Android 2.3.4中通過附加庫也可以兼容這些功能,目前可以在Nex