Android Activity 詳解

Activity為Android應用程序的一個關鍵組成部分,它通常提供一個用戶界面用來和用戶交互以完成某個功能,比如撥號,拍照,發送電子郵件或者是瀏覽地圖, 在移動設備上,Activity通常占據整個屏幕,但Android也支持部分屏幕或是浮動窗口。

 

一個Android應用通常由多個Activity構成,不同Activity之間采用低耦合度設計,其中某個Activity可以稱為應用的“主Activity”,作為在用戶單擊應用圖標時顯示的初始界面。然後每個Activity都可以觸發其它的Activity以往常某種功能。每當一個新Activity啟動後,之前的Activity將處於“停止”狀態,但是Android系統會繼續保留之前的Activity的狀態,這樣就形成一個“Activity”棧結構(稱為“Back Stack”)。新Activity啟動後被Android系統推放到“Activity”棧的最前面並且獲取用戶焦點(響應按鍵,觸摸事件等),這個“Activity”棧采用“後進先出”的棧機制,因此當用戶完成當前Activity功能後,單擊“回退”鍵,當前Activity從“Activity”棧退棧並被“銷毀”,之前的Activity變為當前Activity並且恢復之前的狀態。

 

當一個Activity由於有新的Activity啟動轉變到“停止”狀態時,Android系統將通過Activity的生命周期回調函數來通知該Activity。根據Activity當前狀態的不同,系統將觸發Activity多個不同的生命周期回調函數—創建,停止,恢復,銷毀等。通過回調函數可以為Activity的不同狀態添加不同的處理方法,比如當Activity停止時,可以釋放某些系統資源,比如網絡,數據庫連接等,而當恢復某個Activity時可以重新獲取這些系統資源。

 

創建Activity

為瞭創建一個Activity,你必須從Activity或是Activity的某個子類派生一個新的Activity子類,在這個子類中,你必須實現Activity生命周期中的幾個回調函數,比如Activity創建,恢復,停止及銷毀時的回調方法。其中兩個最重要的回調方法如下:

 

onCreate()Activity必須實現這個方法,Android系統在創建Activity時將調用該回調函數,在你的實現方法,你應該為Activity中使用到的關鍵部件做初始化,最重要的時,此時你可以調用setContentView()為Activity設置用戶界面佈局。

 

onPause()Android系統在用戶將要離開你的Activity之前調用(盡管這不總是意味著該Activity將被銷毀)。通常此時你需要完成保存數據的工作以便用戶後面回到你的Activity以恢復之前的狀態。

 

實現用戶界面

Android用戶界面是由View子類構造的層次結構來實現的,每個View控件代表Activity用戶界面中某個矩形空間,通常可以響應用戶事件,比如一個View控件可以為一個可以響應用戶點擊的按鈕。

 

Android提供瞭很多內置的View控件,你可以直接用在UI佈局中。“Widget(小組件)”為具有可視部分且通常支持用戶交互的View控件,比如按鈕,文本框,多選框或是圖像。“Layout(佈局)”為ViewGroup的子類,用來管理包含在其中的其它View控件的大小和位置,比如有線性佈局,網格佈局等,你也可以通過派生View或ViewGroup來創建自定義的小組件或是佈局。

 

最常用的定義用戶界面的方法是通過XML來描述,使用這種方法定義用戶界面可以很好的實現UI和應用邏輯的分離。你可以通過setContentView()來為Activity設置UI佈局。

 

除此之外,你也可以通過代碼來創建用戶界面。

 

 在Manifest文件中申明Activity

你必須在Android應用的清單文件(AndroidManifest.xml)中申明Activity才可以在應用中使用該Activity。你可以通過打開Android應用的清單文件,然後添加一個<activity>元素作為<application>的子節點。比如:

 

 

[html]  

<manifest … >  

  <application … >  

      <activity android:name=".ExampleActivity" />  

      …  

  </application … >  

  …  

</manifest >  

 

<manifest … >

  <application … >

      <activity android:name=".ExampleActivity" />

      …

  </application … >

  …

</manifest >

 

 

 

 

除android:name 屬性之外,你還可以為Activity添加其它一些屬性定義,比如圖標,顯示主題等,android:name是唯一必須設置的屬性,它定義瞭Activity的類名稱,一旦定義最好不要修改,否則有可能破壞其它某些功能,比如應用的快捷方式等。

 

使用IntentFilter

在Android清單中也可以為Activity定義各種Intent Filter,這是通過<intent-filter>元素來定義的,定義Intent Filter的目的是為瞭描述應用的其它組件如何來啟動該Activity。

 

當你使用Android SDK 開發環境創建新應用時,自動創建的Activity在Android清單文件中的定義自動包含瞭一個Intent Filter,如下:

 

 

[html] 

<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">  

    <intent-filter>  

        <action android:name="android.intent.action.MAIN" />  

        <category android:name="android.intent.category.LAUNCHER" />  

    </intent-filter>  

</activity>  

 

<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">

    <intent-filter>

        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />

    </intent-filter>

</activity>

 

 

 

 

其中<action> 的值為android.intent.action.MAIN 表示該Activity為應用的“主”入口點。而<category>的值為android.intent.category.LAUNCHER表示這個Activity 會顯示在Android系統的應用程序列表中(用戶可以通過單擊應用圖標啟動該Activity)。

 

如果你不想讓其它應用來啟動你應用的某個Activity,則不要為該Activity定義任何Intent Filter,你可以在應用通過顯式調用來啟動不含任何IntentFilter的Activity,而其它應用則無法觸發該Activity。

 

 啟動Activity

你可以調用startActivity()通過傳入描述需啟動的Activity的Intent參數來啟動一個Activity。這個Intent可以明確指明需要啟動的Activity或者說明你需要哪種功能的Activity(然後系統根據要求自動選擇合適的Activity),Intent也可以傳入少量數據給被啟動的Activity。

 

在你自己的應用中,常見的是簡單的啟動一個已知的Activity,這可以通過構建一個明確定義需要啟動Activity的Intent來實現。比如下面代碼明確啟動名為SignInActivity的Activity。

 

 

[java] 

Intent intent = new Intent(this, SignInActivity.class);  

startActivity(intent);  

 

Intent intent = new Intent(this, SignInActivity.class);

startActivity(intent);

 

 

 

 

然後有時你的應用需要完成某種功能,比如發送Email,短消息或者是使用你Activity的數據來更新系統狀態,在這種情況下,你的應用中可能並沒有定義具有上述功能的Activity,那麼你可以利用設備中其它應用中提供的Activity來完成這些功能。此時,Intent就顯得非常有用,你可以創建一個Intent,在這個Intent中描述你想要完成的功能,然後讓Android系統來決定啟動合適的Activity。如果系統中有多個可以往常同樣功能的Activity,系統可以讓用戶來選擇使用那個Activity來完成該項任務。比如,你想讓用戶發送Email,你可以使用如下定義的Intent:

 

 

[java]  

Intent intent = new Intent(Intent.ACTION_SEND);  

intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);  

startActivity(intent);  

 

Intent intent = new Intent(Intent.ACTION_SEND);

intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);

startActivity(intent);

 

 

 

 

由EXTRA_EMAIL 附加參數指定的為需要發送的Email的地址。當被啟動的Email應用響應這個Intent時會讀取這個Email地址,然後填到收件人部分。在這種情況下,當用戶推出啟動的Email應用後,你原先的應用將重新恢復運行。

 

 啟動Activity並等待Activity返回結果

有時當你啟動另外一個Activity後,希望從啟動的Activity獲得返回結果,此時可以通過調用startActivityForResult()而不是前面的startActivity()來實現。為瞭獲得由Activity返回的結果,你需要實現onActivityResult()回調函數。當啟動的Activity完成後,將使用onActivityResult()回調函數經由Intent返回結果。

 

比如,你可能希望用戶從通信錄中選擇一個聯系人,然後你的應用可以對這個聯系人的信息做些處理,下面代碼示例可以幫助你完成所需功能:

 

 

 

這個例子顯示瞭使用onActivityResult()處理Activity返回值的基本邏輯。條件語句中第一個判斷是檢查請求是否成功,成功的話,resultCode的值為RESULT_OK,第二個判斷請求是否為已知的請求,本例使用和startActivityResult同樣的參數值來和requestCode做比較。如果兩個條件都滿足,你就可以處理由Activity返回的數據瞭。本例使用ContentResolver 查詢指定的聯系人信息,ContentResolver用法可能參見後面的介紹。

 

 終止一個Activity

你可以調用Activity的finish()方法結束該Activity。你也可以通過調用finishActivity () 來終止先前啟動的Activity。

 

要註意的是,在大部分情況下,你不需要使用上面的方法來強制終止一個Activity的運行,而是由Android系統來管理這些Activity的生命周期。因此你無需調用finish來結束你的Activity,使用這些方法反而可能會影響最終的用戶體驗。

 

 管理Activity的生命周期

通過實現Activity的生命周期的回調函數來管理Activity的生命周期是開發健壯,靈活的應用的關鍵。Activity的生命周期直接影響到與之關聯的其它Activity,它所對應的任務以及“Activity”棧。

 

一個Activity可以存在下面三種關鍵的狀態:

 

·        Resumed該Activity處於前臺並獲取用戶焦點(本狀態通常稱為“運行”)

 

·        Paused此時有其它Activity處於前臺並有用戶焦點。但本Activity依然可見,也就是說,有其它可見的Activity覆蓋在本Activity之上,而且上面的Activity為半透明或者沒有占據整個屏幕。處於“Paused”狀態的Activity依然是活動的(該Activity依然占據內存並保持其所有狀態數據,並且仍由WindowsManager管理),在系統空閑內存極低時還是有可能被Android系統清除的。

 

·        Stopped該Activity完全被其它Activity覆蓋,此時該Activity在後臺運行,處於“Stopped”狀態的Activity也是活動的(該Activity依然占據內存並保持其所有狀態數據但和WindowsManager分離開),本Activity不可見,在系統其它部分需要內存時隨時可能被清除。

 

 實現生命周期回調函數

當Activity在上面幾種狀態轉變時,系統通過Activity生命周期函數來通知Activity。你可以重載所有這些回調函數在狀態變化時添加合適的操作。下面的代碼框架包含瞭所有基本的生命周期回調函數:

 

 

[java]  

public class ExampleActivity extends Activity {  

    @Override  

    public void onCreate(Bundle savedInstanceState) {  

        super.onCreate(savedInstanceState);  

        // The activity is being created.   

    }  

    @Override  

    protected void onStart() {  

        super.onStart();  

        // The activity is about to become visible.   

    }  

    @Override  

    protected void onResume() {  

        super.onResume();  

        // The activity has become visible (it is now "resumed").   

    }  

    @Override  

    protected void onPause() {  

        super.onPause();  

        // Another activity is taking focus    

        //(this activity is about to be "paused").   

    }  

    @Override  

    protected void onStop() {  

        super.onStop();  

        // The activity is no longer visible (it is now "stopped")   

    }  

    @Override  

    protected void onDestroy() {  

        super.onDestroy();  

        // The activity is about to be destroyed.   

    }  

}  

 

public class ExampleActivity extends Activity {

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        // The activity is being created.

    }

    @Override

    protected void onStart() {

        super.onStart();

        // The activity is about to become visible.

    }

    @Override

    protected void onResume() {

        super.onResume();

        // The activity has become visible (it is now "resumed").

    }

    @Override

    protected void onPause() {

        super.onPause();

        // Another activity is taking focus 

        //(this activity is about to be "paused").

    }

    @Override

    protected void onStop() {

        super.onStop();

        // The activity is no longer visible (it is now "stopped")

    }

    @Override

    protected void onDestroy() {

        super.onDestroy();

        // The activity is about to be destroyed.

    }

}

 

 

 

 

要註意的是,你在實現這些回調函數時要調用基類對應的方法。如上面代碼實現。

 

總的來說,上面的回調函數定義一個Activity完整的生命周期,通過實現這些回調函數,你可以監視Activity生命周期的三個嵌套的循環。

 

·        Actvity整個生命周期,它發生在onCreate()和onDestroy之間,在你的Activity的onCreate()方法中你應該一些“全局”狀態的設置,並且在onDestroy()釋放所有資源。例如,你的Activity中有一個在後臺下載數據的線程,這個線程可以在onCreate()創建,而在onDestroy()中終止該線程。

 

·        Activity可見生命周期,它發生在onStart()和onStop()之間。在這個過程中,用戶可以看到這個Activity的用戶界面並可以和它交互。比如,在新Activity啟動後,當前Activity的onStop()被調用並且隱藏到後臺。在這兩個方法之間,你可以保持一些需要顯示給用戶的資源,比如,你可以在onStart()中註冊一個BroadcastReceiver來監視一些可能影響到UI的變化(比如網絡狀態),而在onStop()中註銷這個BroadcastReceiver。隨著Activity由顯示到隱藏的交替變化,系統可能會重復多次調用Activity的onStart()和onStop()方法。

 

·        Activity的前臺生命周期,它發生在onResume()和onPause()之間,在這段時間內,Activit處在所有其它Activity的前面並具有用戶焦點。一個Activity可以多次在前後臺之間切換。比如,在設備進入睡眠狀態時或是顯示對話框會調用Activity的onStop()方法。由於這個狀態交替頻繁,因此要避免在這兩個方法中添加過多的操作以免影響不同Activity之間切換的速度而需要用戶等待。

 

下圖顯示瞭這些循環和Activity的可能的狀態變化圖,矩形代表瞭可以重載的回調函數。

 

 

圖1.                   Activity生命周期

 

   下面的表格列出瞭上面所說的所有生命周期方法並給出較為詳細的說明,並且給出當每個回調函數結束後系統是否可以終止這個Activity。

 

 

 

表2Activity 生命中周期回調函數匯總

 

回調方法

 說明

 可否終止

 下個狀態

 

onCreate()

 創建Activity時調用,在這個方法中通常你做些靜態配置—創建View,綁定List數據源等等。這個函數的傳入參數包含瞭Activity之前的狀態(如果之前保存的狀態),這個方法後面總是跟著onStart()方法。

 否

 onStart()

 

    

 onRestart()

 在Activity停止之後再重新啟動之前調用,總是緊跟著onStart()方法。

 否

 onStart()

 

onStart()

 在Activity剛要變為可見前調用。如果Activity轉到前臺,後面的方法為onResume(),如果隱藏到後臺,則後面調用onStop()

 否

 onResume() 

onStop()

 

    

 onResume()

 在Activity就要可以和用戶發生交互前調用。此時該Activity處於“Activity棧”的頂端,可以響應用戶輸入。後面總是跟著onPause()方法。

 否

 onPause()

 

onPause()

 在系統就要啟動其它Activity之前調用。在這個方法典型的用法是保存一些尚未保存的數據,停止一下可能占用CPU資源比如動畫等操作。這些方法必須很快完成,因為隻有這些操作完成後後面的Activity才會啟動。如果Activity轉到前臺,後面的方法為onResume(),如果隱藏到後臺,則後面調用onStop()

 是

 onResume() 

onStop()

 

onStop()

 在Activity不再可見時調用,這可能發生在Activity被銷毀或是有其它Activity啟動覆蓋瞭整個屏幕時。如果之後Activity恢復到可見則調用onRestart(),如果Activity結束則調用onDestroy()

 是

 onRestart()

onDestroy()

 

onDestroy()

 在Activity被銷毀之前調用,這是Activity最後一個可能接受到的通知。在Activity調用finish()或者系統需要內存臨時銷毀Activity會調用,你可以通過方法isFinishing()來區分這兩種情況。

 是

 無

 

 

 

 

上表中列“可否終止”代表Android系統在該回調函數結束後是否而無需再執行Activity的任何代碼就可以殺死運行該Activity的進程。三個方法後面標記為“是”(onPause(),onStop()和onDestroy())。因為onPause()排在三個方法的第一位,一旦創建Activity之後,在系統內存不夠的緊急情況下可以殺死該進程,onPause()在Activity終止前肯定會被調用。而onStop()和onDestroy()卻不一會被調用。因此你應該在onPause()中保存一些關鍵數據。然而你也應該在onPause()中有選擇性的保存數據,這些因為耗時的操作會影響後續Activity的啟動,從而影響用戶體驗。

 

 保存Activity狀態

前面介紹管理Activity生命周期上簡要提到當Activity暫停或是停止時,Activity的狀態會暫時保持。這是因為當Activity處於暫停或是停止狀態時,Activity對象依然在內存中,關於這個Activity的所有信息和狀態依舊是活動的。因此用戶所做的對Activity的改變在Activity回到前臺後,Activity仍舊能保持之前的狀態。

 

然而,如果Android系統在系統內存不足時銷毀瞭某個Activity,Activity對象不會繼續保持著內存中,此時系統再恢復這個Activity到前臺時就不能簡單的恢復到Activity之前的狀態瞭。如果用戶需要重新回到這個Activity,Android系統是通過重新創建這個Activity的實例來實現的。對於最終用戶來說,他並一定知道他回到的這個Activity是由系統重新創建的新的Activity對象,他一樣希望這個Activity能夠保持之前的狀態。在這種情況下,你必須保證有關Activity一些重要的狀態信息能夠保存下來以便恢復,這可以通過實現另外一個回調函數onSaveInstanceState()來完成Activity狀態的保存。

 

Android系統會在銷毀Activity之前調用onSaveInstanceState()。系統傳入Bundle參數,在這個參數中可以保存name-value對,你可以用來使用如 putString()或putInt()等方法保存Activity的狀態。之後,如果系統銷毀瞭你的Activity而用戶又想回到你的Activity時,Android系統將重新創建你的Activity的實例並在onCreate()或onRestoreInstanceState()方法這傳入這個Bundle參數。你可以在這兩個方法中任選其一來恢復Activity之前的狀態。如果沒有可以恢復的狀態,Bundle的值為Null(這種情況他通常為首次創建該Activity實例)。

 

圖2.                   保存/恢復Activity狀態

 

   要值得註意系統並不會完全保證在銷毀Activity之前一定會調用onSaveInstanceState(),這是因為在某些情況下沒有必要保存這些狀態(比如用戶使用“回退”鍵離開你的Activity,用戶明確指明要退出你的Activity,此時也沒有保存之前的狀態),如果系統調用onSaveInstanceState(),它將在onStop()之前有時也在onPause()之前調用。

 

   然而,即使你沒有實現onSaveInstanceState(),某些Activity的狀態也被Activity的缺省onSaveInstanceState()實現保存。尤其是這個缺省實現將會調用UI佈局中每個View的onSaveInstanceState()方法,這允許每個View有機會保存一些需要保存的信息。幾乎每個Android內置的UI小組件都實現瞭這個方法,比如一些UI變化會在Activity重建時自動保存和恢復。比如EditText(文本框)可以保持用戶輸入的文本。CheckBox可以保存之前的選擇狀態。唯一需要的工作是為這些UI小組件提供唯一的ID(使用android:id屬性)。如果沒有指定小組件的ID,這些UI組件的狀態就不會自動保存和恢復。

 

            盡管onSaveInstanceState()的缺省實現可以保持一些有用的UI信息,你可能還是需要重載這個函數以支持額外的信息保存。要記住這重載的方法中調用基類的方法以支持缺省的UI變化的自動保存和恢復。

 

測試你的應用是否支持保存Activity的狀態的一個好方法是通過旋轉設備從而使屏幕的顯示方向發生變化。當屏幕顯示方向發生變化時,系統將首先銷毀然後重新創建Activity以便使用可能存在的新的資源來顯示以適應新的屏幕配置變化。由此,在系統重新創建Activity完整的恢復Activity狀態非常重要,用戶可能不斷的轉動屏幕。

 

 處理配置變化

   某些設置實時改變一些系統配置(比如屏幕顯示方向,鍵盤及語言),每當有這種配置上的變化時,Android系統將重新創建正在運行的Activity(系統將先調用onDestroy(),然後緊接著調用onCreate()方法),這樣的設計可以幫助你的應用在這些配置發生變化時,可以根據新配置調用適合的資源(比如給不同哦屏幕顯示方向使用不同的佈局設計)。

 

如果你的Activiy具有正確的設計以適應由於比如顯示方向變化引起的Activity重啟,能夠正確的保存和恢復Activity狀態,你的應用將能更好適應一些生命周期中的突發事件。

 

實現上面所說的Activity重啟的最好的方法時重之前介紹過的onSaveInstanceState()和onRestoreInstanceState(或onCreate)方法。

 

協調多個Activity

   當一個Activity啟動另外一個Activity時,這兩個Activity都會發生生命周期狀態的轉變。第一個Activity會暫停而後停止(某些情況下,如果它在後臺仍可見的話,不會轉到停止狀態)。而另外一個Activity會被創建。如果這些Activity需要共享一些保存在SD卡或是其它什麼地方的數據,瞭解在創建好第二個Activity後第一個Activity有可能沒有完全停止非常重要。這兩個過程完全有可能重疊。

 

   生命周期函數調用的順序是定義好的,尤其是運行在同一個進程中的兩個Activity,其中一個Activity啟動另外一個Activity。下面為ActivityA啟動 Activity B 後可能發生的操作順序:

 

1.       執行Activity A的 onPause()方法。

 

2.       順序執行Activity B的onCreate(),onStart()和onResume()方法(ActivityB獲得用戶焦點)

 

3.       如果之後,Activity A不可見,將執行Activity A的onStop()方法

 

這種可以事先預計的生命周期方法執行的順序允許你有效的管理數據在不同Activity之間的傳遞。比如,你必須在第一個Activity停止前完成對數據庫的寫入以便後續的Activity可以從數據庫中讀取。因此你應該在onPause而不是onStop中完成上述操作。

 

 

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *