編輯:Android開發實例
Activity 的生命周期
和 J2ME 的 MIDlet 一樣,在 android 中,Activity 的生命周期交給系統統一管理。與 MIDlet 不同的是安裝在 android 中的所有的 Activity 都是平等的。
Activity 的狀態及狀態間的轉換
在 android 中,Activity 擁有四種基本狀態:
Active/Runing一個新 Activity 啟動入棧後,它在屏幕最前端,處於棧的最頂端,此時它處於可見並可和用戶交互的激活狀態。
Paused 當 Activity 被另一個透明或者 Dialog 樣式的 Activity 覆蓋時的狀態。此時它依然與窗口管理器保持連接,系統繼續維護其內部狀態,所以它仍然可見,但它已經失去了焦點故不可與用戶交互。
Stoped 當 Activity 被另外一個 Activity 覆蓋、失去焦點並不可見時處於 Stoped狀態。
Killed Activity 被系統殺死回收或者沒有被啟動時處於 Killed狀態。
當一個 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();
}
這些方法的說明如下:
protected void onCreate(Bundle savedInstanceState)一個 Activity 的實例被啟動時調用的第一個方法。一般情況下,我們都覆蓋該方法作為應用程序的一個入口點,在這裡做一些初始化數據、設置用戶界面等工作。大多數情況下,我們都要在這裡從 xml 中加載設計好的用戶界面。例如:
setContentView(R.layout.main);
當然,也可從 savedInstanceState中讀我們保存到存儲設備中的數據,但是需要判斷 savedInstanceState是否為 null,因為 Activity 第一次啟動時並沒有數據被存貯在設備中:
if(savedInstanceState!=null){
savedInstanceState.get("Key");
}
protected void onStart()該方法在 onCreate() 方法之後被調用,或者在 Activity 從 Stop 狀態轉換為 Active 狀態時被調用。
protected void onResume()在 Activity 從 Pause 狀態轉換到 Active 狀態時被調用。
protected void onResume()在 Activity 從 Active 狀態轉換到 Pause 狀態時被調用。
protected void onStop()在 Activity 從 Active 狀態轉換到 Stop 狀態時被調用。一般我們在這裡保存 Activity 的狀態信息。
protected void onDestroy()在 Active 被結束時調用,它是被結束時調用的最後一個方法,在這裡一般做些釋放資源,清理內存等工作。
圖 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 中定義。
Activity 之間通信
使用 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);
前文介紹了Android中MediaPlayer用法的時候稍微介紹了SurfaceView,SurfaceView由於可以直接從內存或者DMA等硬件接口取得圖像數
Android應用程序可以在許多不同地區的許多設備上運行。為了使應用程序更具交互性,應用程序應該處理以適合應用程序將要使用的語言環境方面的文字,數字,文件等。在本章中,我
Android提供的剪貼板框架,復制和粘貼不同類型的數據。數據可以是文本,圖像,二進制流數據或其它復雜的數據類型。Android提供ClipboardManager、Cl
步驟:分兩步 一、usb連接: 在Ubuntu掛載使用MTP設備步驟如下: 1.將MTP設備連接至PC機 2.如果是第一次使用MTP設備需要安裝以下軟件,否則可以