Android文檔Training之管理Activity生命週期

打開一個Activity

android系統不像其他的編程應用一樣用main()方法開始,而是在activity中通過activity的各個生命週期回調方法來初始化代碼.

理解Activity生命週期回調方法

在Activity的生命週期中,系統調用一系列核心生命週期方法一步步走向一個金字塔頂端,Activity生命週期每一個回調都相當於朝向金字塔的一步,隨着系統創造一個activity示例,每個回調方法都使得activity走向金字塔頂端更近一步,金字塔頂端就是activity運行在前臺可以與用戶交互的狀態,隨着用戶離開Activity,系統調用其他的生命週期回調方法慢慢走下金字塔,某些情況下,activity會在下來一兩步後停下等待(比如當用戶切換到其他的app中),這種情況activity有可能回到金字塔頂端(如果用戶切回到當前activity),並且activity恢復到離開activity時的樣子
Activity生命週期示意圖,摘自AndroidAPI文檔
根據你自己的activity的複雜度,你可以不必需要重寫所有生命週期方法,但是你必須理解每一個生命週期回調方法的重要性以確保你的app的體驗和所期望的一致,重寫生命週期方法使得你的app用戶體驗更好,主要體現在以下幾個方面:

1.當用戶在使用你的app的時候收到電話或者用戶切換到別的app的時候不會崩潰.
2.當用戶不是正在使用你的app的時候不浪費有價值的系統資源.
3.如果用戶不離開你的app再返回你的app的時候不關閉用戶的使用進度.
4.當屏幕在landscape和portrait之間(橫豎屏)切換的時候不會崩潰或失去用戶的進度.

在接下來的學習中我們將知道,Activity在各種不同的狀態之間切換的時候有好幾種情形。然而,只有三種狀態是靜態的,也就是說,activity可以在這三個狀態可以長時間停留。

Resumed(恢復/運行狀態)
    在這種狀態,activity在前臺並且可以和用戶交互(有時也稱作“運行”狀態)
Paused(暫停狀態)
    在這種狀態,activity是被另一個activity擋住一部分的,這擋住它的activity是半透明的或者沒有覆蓋整個屏幕,暫停的activity不能接收用戶的指令也不能執行任何代碼.
Stopped(停止狀態)
    在這種狀態,activity完全被隱藏,且對用戶是不可見的,它被認爲是在後臺運行,當進入stopped狀態時,activity實例和所有的狀態信息比如成員變量都被保存起來了,但是它也不能執行任何代碼. 

其他的狀態(Created 和Started)都是很短暫的狀態,系統很快的通過調用生命週期的方法來從他們跳轉到下一個狀態,系統調用onCreate之後,會馬上調用onStart, 緊接着就是onResume了.以上就是基本的activity生命週期,現在我們將來學習特殊的生命週期行爲了.

指定App的默認啓動Activity

當用戶在主屏上選擇你的app圖標,系統調用在你的app中定義的默認啓動的activity的onCreate方法,這個activity是你的app用戶界面的主要入口,你可以在AndroidManifest.xml定義你的程序入口activity是哪個activity. 具體定義如下
<activity android:name=".MainActivity" android:label="@string/app_name">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

注意:在新建的android項目中默認有一個activity是app的入口activity. 如果你的所有的activity都是要麼沒有Main action或者LAUNCHER category,或者兩者都沒有,那麼你的app圖標將不會出現在在主屏app列表中.

創造一個實例

大多數包含多個activity的app允許用戶表現不同的action,無論你的activity是程序的主入口(點擊app圖標進入的activity)還是迴應用戶action打開的activity,系統將通過onCreate()方法創造這個activity的實例.

你必須重寫onCreate方法來執行基本的應用啓動邏輯,這隻在activity的生命週期中執行一次,例如,你重寫onCreate方法應該定義用戶界面和實例化類範圍內的變量.

例如,接下來的onCreate()方法的實例向我們展示了執行基本的activity啓動的一些代碼,比如定義用戶界面(xml佈局文件中定義的),定義成員變量,配置一部分UI.

TextView mTextView; // Member variable for text view in the layout

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Set the user interface layout for this Activity
    // The layout file is defined in the project res/layout/main_activity.xml file
    setContentView(R.layout.main_activity);

    // Initialize member TextView so we can manipulate it later
    mTextView = (TextView) findViewById(R.id.text_message);

    // Make sure we're running on Honeycomb or higher to use ActionBar APIs
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        // For the main activity, make sure the app icon in the action bar
        // does not behave as a button
        ActionBar actionBar = getActionBar();
        actionBar.setHomeButtonEnabled(false);
    }
}

注意:使用SDK_INT來防止舊版本的系統執行新版本系統的代碼,這部分代碼在Android 2.0 (sdk = 5)及以上是好使的,舊版本系統執行這段代碼會出現runtime異常.

一旦onCreate()方法執行完畢,系統將馬上調用onStart()和onResume(),你的Activity絕不會在Created或者Started狀態下停留,理論上說,activity在onStart()被調用後是可視的,但onResume()方法會馬上跟着執行且actvity會停留在Resumed狀態除非有東西或者事件改變了它,比如來電話了,用戶打開了另外一個activity或者屏幕關閉了.

在接來的課程中,你將會看到其他的生命週期方法,onStart()和onResume()方法在當你的activity的狀態需要從Paused或者Stopped狀態中恢復的時候的作用。

注意:onCreate()方法包含一個參數savedInstanceState,我們將在後面的課程中關於重建一個activity的時候來討論.

基本的生命週期示意圖,摘自Android文檔

摧毀一個Activity

一個activity的第一個生命週期函數是onCreate(), 最後一個生命週期函數是onDestory(), 系統會在你的activity實例要被完全從系統內存中移除時調用該方法.

大多數app不需要重寫這個方法因爲局部的類引用將與activity同時被銷燬,你的activity 應該在onPause()或者onStop()方法中執行清理操作,然而如果你的activity在onCreate()方法創建了一個後臺線程或者長時間的資源佔用可能在沒有合適的關閉下會造成內存泄漏,你應該在onDestory()方法中殺死它們.

@Override
public void onDestroy() {
    super.onDestroy();  // Always call the superclass

    // Stop method tracing that the activity started during onCreate()
    android.os.Debug.stopMethodTracing();
}

注意:系統只能在調用onPause()和onStop()後纔會調用onDestory(),除非你在onCreate方法中調用了finish()方法,在某些情況下,比如你的activity決定啓動另一個activity ,你可能會在onCreate()方法中調用finish() 然後銷燬activity,在這種情況下,系統將會馬上調用onDestory()而不調用其他的生命週期方法.

暫停和恢復/繼續一個Activity

在正常的app使用過程中,前臺activity有時會被另外的可視化組件阻塞,導致activity進入Paused狀態,例如,當一個半透明的activity打開的時候(比如dialog樣式的activity),先前的activity暫停了,只要這個activity是部分可視的但卻沒有獲取到焦點 它就是paused狀態.

然而,只要這個這個activity被完全的阻塞了並且不能被看見,它將進入stoped狀態(下節課中我們將討論).

由於你activity進入了paused狀態,系統調用你的activity中的onPause()方法,它允許你停止在暫停狀態下不該繼續的動作,或者保留一些應該被永久保留以防止用戶離開你的app的信息,這樣當你的用戶從paused狀態回來的時候,系統將恢復它並且調用onResume方法.

注意:當你的app收到要調用onPause方法時,這可能是表示你的activity將會進入onPause狀態一段時間並且用戶可能會某一時刻返回到你的activity,然而,這通常代表着用戶要離開你的activity了.

暫停你的activity

當系統調用你的activity的onPause()方法時,理論上意味着你的activity還是部分可見的,但大多數通常是表示用戶離開你的activity了,並且通常很快進入stopped狀態你應該嚐嚐使用onPause()回調來:

停止動畫或者其他正在繼續消耗CPU的行爲.
提交沒有保存的改變,只在用戶在離開的時候期望這些改變是永久保存的情況下執行(比如郵箱草稿).
釋放系統資源,比如廣播接收器,傳感器(例如GPS)或者任意其他在activity是暫停狀態下且用戶不需要的影響電池壽命的資源文件.

例如,如果你的應用使用了照相機,onPause()方法將是一個好的地方來釋放資源.

@Override
public void onPause() {
    super.onPause();  // Always call the superclass method first

    // Release the Camera because we don't need it when paused
    // and other activities might need to use it.
    if (mCamera != null) {
        mCamera.release()
        mCamera = null;
    }
}

一般來說,你不應該在onPause()中永久存儲這些改變(比如表單中的個人信息),只有當你很確信在onPause()中這些用戶期望做出的改變自動需要永久保存起來時(比如郵件草稿)你才能這麼做. 然而,你應該儘量避免CPU在onPause()中高強度工作,比如寫入數據到數據庫,因爲這將減慢activity跳轉到下一個activity的可視化的轉變(你應該把這些高強度的運算放在onStop()中執行).

你應該使得在onPause()中的計算相當簡單以使得如果你的activity要被stop的時候,用戶的跳轉到下一個activity的速度很快.

注意:當你的activity被暫停了, Activity實例確是一直存在於內存之中,並且當activity回覆的時候會被再次調用. 你不需要再次實例化已經在曾走向Resumed狀態被調用的生命週期的方法中被創建的組件.

恢復你的activity

當用戶從Paused狀態恢復時,系統將調用onResume()方法.

我們注意到系統在每次進入前臺用戶視野的時候都會掉用這個方法,包括首次創建的時候,這樣我們應該重寫onResume()來初始化在onPause()中被釋放的組件並執行任何其他的必須activity進入Resumed狀態需要初始化的變量的初始化(比如,開始動畫和只在activity獲取到用戶焦點時候初始化的組件).

下面onResume()的例子 上面onPause() 例子的相對方法,所以它初始化了在activity進入暫停狀態下釋放的照相機資源 .

@Override
public void onResume() {
    super.onResume();  // Always call the superclass method first

    // Get the Camera instance as the activity achieves full user focus
    if (mCamera == null) {
        initializeCamera(); // Local method to handle camera init
    }
}

停止並重新開始Activity

合適的停止和重新開始activity一個在activity生命週期中重要的過程,它確保了你的用戶知道你的app一直是活着的麼而且沒有丟失他們的進度,有這樣幾個關鍵的場景(activity停止並重新開始):

用戶打開了最近使用的app窗口並且從你的app切換到另一個app,你的app中的activity目前就處於前臺並且是停止狀態. 如果用戶從主屏圖標或者最近使用的app列表中回到你的app,那麼這個activity將會restart.
用戶在你的app中打開了一個新的activity,則當前的activity會在第二個activity創建的時候停止. 如果用戶在第二個activity頁面按了返回按鈕,澤第一個activity會restart.

Antivity提供了2個生命週期方法,onStop() 和 onRestart(),這兩個方法允許你具體的處理你的activity在變爲stopped和restarted狀態的過程. 不像表示部分UI被擋住的paused狀態,stopped狀態保證了UI不再是可視的並且用戶耳朵焦點已經在另一個activity上(或者別的app上).

注意:因爲系統在activity處於stopped狀態時保持我們的Activity實例在內存中,所以很可能你不需要重寫onStop()和onRestart()方法(甚至是onStart()方法也不需要重寫),對大多數activity來說 他們都是相對比較簡單的,activity將會stop並restart剛剛好,你也許只需要使用onPause()來暫停正在執行的動作和與系統資源斷開連接。

停止你的activity

當你的activity收到onStop()的回調方法,它就不再是可視的了,並且它應該釋放在用戶不再使用的時候不再需要的絕大多數資源,一旦你的activity停止了系統也許會在恢復系統內存的時候會銷燬實例,在極端情況下,系統也許會簡單的殺死你的app進程而不調用你的activity的onDestory()方法 ,素以使用onStop()來釋放資源是防止內存泄漏的重要的手段.

儘管onPause() 方法在onStop()之前調用, 你還是應該使用onStop()來執行更大的更密集型的cpu運算 ,比如把信息寫入數據庫.

例如,這裏重寫了onStop()方法保存了便條草稿的內容能夠到本地的存儲:

@Override
protected void onStop() {
    super.onStop();  // Always call the superclass method first

    // Save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    ContentValues values = new ContentValues();
    values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
    values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());

    getContentResolver().update(
            mUri,    // The URI for the note to update.
            values,  // The map of column names and new values to apply to them.
            null,    // No SELECT criteria are used.
            null     // No WHERE columns are used.
            );
}

當你的activity進入了stopped狀態,activity對象仍然在內存中,他將會在activity恢復的時候被調用,你不需要再次初始化已經初始化過了的組件,系統也一直追蹤佈局文件中每個View上午當前的狀態,所有如果用戶在EditText組件中輸入了一段文本,這些內容會被記錄起來,所以你不必擔心有沒有必要去保存或者修復它.

注意:即便當activity處於stopped狀態,系統銷燬了activity,它仍然保持着View對象的狀態(比如EditText中的文本),並把它存儲到Bundle(鍵值對)中,並且在用戶打開相同的實例的時候恢復他們(下一節我們講關於使用Bundle保存數據以防activity被銷燬或重建).

開始/重新開始 Activity

當我們的actvity從stopped狀態來到前臺,它收到了onRestart()方法的回調,系統同時會調用onStart() 方法,onStart()方法在每次activity變成可視狀態都會調用,要麼在onRestart()之後執行,要麼在onCreat()之後執行,然而onRestart() 方法,只有當activity從stopped狀態恢復時會被調用,所以你能使用它來執行特殊的復原工作,這也許是必須的工作僅當activity先前已經被停止了但還沒有被銷燬的時候.

app需要使用onRestart()來存儲activity狀態這件事是不尋常的,所有對這個方法應用到一般的app沒有任何的準則,然而因爲你的onStop() 方法應該基本清除所有你的activity的資源,你將需要在acticity重新開始的時候重新實例化他們,不得不說的還有,你也需要在首次創建activity(內存中沒有activity的實例)的時候實例化他們. 因爲這個原因, 你應該經常使用onStart() 回調方法作爲onStop()方法的對應方法, 因爲系統在創建activity和restart activity的時候都會調用onStart()方法.

例如,因爲用戶在回到我們activity的時候可能已經離開我們的app很長一段時間了,onStart() 方法是一個好地方來驗證需要的系統特性是不是能用.

@Override
protected void onStart() {
    super.onStart();  // Always call the superclass method first

    // The activity is either being restarted or started for the first time
    // so this is where we should make sure that GPS is enabled
    LocationManager locationManager = 
            (LocationManager) getSystemService(Context.LOCATION_SERVICE);
    boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);

    if (!gpsEnabled) {
        // Create a dialog here that requests the user to enable GPS, and use an intent
        // with the android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS action
        // to take the user to the Settings screen to enable GPS when they click "OK"
    }
}

@Override
protected void onRestart() {
    super.onRestart();  // Always call the superclass method first

    // Activity being restarted from stopped state    
}

當系統銷燬了你的activity,它調用你的activity的onDestory方法。
因爲一般來說你本應該在onStop()中釋放完了大多數資源, 但如果同時收到了調用onDestroy()的命令, 這種情況一般不多見,這個方法是你最後的機會來清除資源以防止內存泄漏,所以你應該確認額外的線程已經被銷燬並且像追蹤類的長時間運行的行爲也被停止了.

重新創建一個Activity

有許多你的activity因爲正常的app行爲被銷燬的場景,例如當用戶按下了返回按鈕或者你的activity通過調用finish()發信號銷燬自己, 系統也有可能會在activity處於stopped狀態並且很長一段時間沒有被使用的時候銷燬activity,或者前臺activity需要更多的資源所以系統不得不關閉後臺進程來恢復內存.

當你的activity因爲用戶按下返回按鈕被銷燬或者activity自己關閉自己,在系統看來因爲這些行爲導致的activity實例永遠消失了,這代表着activity不再被需要了,然而,如果系統銷燬因爲系統約束銷燬了activity(而不是正常的app行爲),則儘管真實的activity實例確實消失了,系統記得它存在過,這使得如果用戶再返回這個activity,系統會使用保存好的數據(activity被銷燬時保存的)創建一個新的activity的實例.
這些系統把他們用來複原先前的狀態的數據也被稱爲“實例化狀態”,它是存儲在Bundle對象中的鍵值對.

注意:你的activity將會在你旋轉屏幕的時候被銷燬然後重建,當你的屏幕改變了方向。系統將會銷燬並重建前臺activity因爲屏幕特徵已經改變了而且你的activity也許需要去加載對應的與原來不一樣的資源(比如說layout佈局).

默認地,系統使用Bundle實例化狀態來保存你的activity佈局中的View對象的信息(比如EditText對象中的text文本值),所以如果你的activity實例被銷燬並且重建,佈局的狀態被自動恢復到它先前的狀態,然而,你的activity也許有更多的你想恢復的狀態信息,比如activity中追蹤用戶進度的成員變量.

注意:爲了使android系統恢復你的activity中所有view的狀態,每個view必須擁有一個唯一的ID,用 android:id 這個屬性來設置.

爲了保存activity狀態額更多的數據,你必須重寫onSaveInstanceState() 回調方法. 系統在用戶離開你的activity的時候調用這個方法,並把這些數據傳遞給Bundle對象,這樣他們就會在activity意外被銷燬的時候被保存起來,如果系統一定要在自之後重建activity實例,它會回傳相同的Bundle 對象到onRestoreInstanceState() 和onCreate() 方法中.

保存Activity的狀態

隨着你的activity開始進入stopped狀態,系統調用onSaveInstanceState() 方法,所以你的activity能用鍵值對的方法保存狀態信息. 這個方法的默認的重寫保存了關於activity的View 分層信息,比如EditText中的文本信息或者ListView滾動到的位置.

爲保存你的activity的更多狀態信息,你必須重寫onSaveInstanceState() 並增加鍵值對到Bundle對象. 例如:

static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
...

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user's current game state
    savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
    savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);

    // Always call the superclass so it can save the view hierarchy state
    super.onSaveInstanceState(savedInstanceState);
}

注意: 一定要調用父類的onSaveInstanceState() ,這樣默認的view分層信息狀態纔會保存到Bundle對象中.

恢復你的Activity的狀態

當你的activity先前被摧毀後被重建,你可以從系統傳給你的Bundle中恢復你保存的狀態, onCreate() 和onRestoreInstanceState() 回調方法會收到相同的包含相同狀態信息的Bundle對象.

因爲onCreate() 方法在創造一個新的activity實例或者重建先前的activity中被調用. 你必須在嘗試從中取值的之前檢查是否這個狀態Bundle是不是空的,如果是空的,則系統正在創建一個新的實例,反之則是在恢復先前被銷燬的activity實例.

例如:下面是你在onCreate()中取狀態數據的實例

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); // Always call the superclass first

    // Check whether we're recreating a previously destroyed instance
    if (savedInstanceState != null) {
        // Restore value of members from saved state
        mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
        mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
    } else {
        // Probably initialize members with default values for a new instance
    }
    ...
}

如果不在onCreate()方法中恢復,你也可以選擇重寫
onRestoreInstanceState(),這個方法將在系統調用完onStart()方法後調用,只有當至少一個保存的狀態要恢復時系統纔會調用onRestoreInstanceState(),所以你不需要檢查Bundle是不是空的:

public void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy
    super.onRestoreInstanceState(savedInstanceState);

    // Restore state members from saved instance
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
    mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}

注意: 一定要調用父類的 onRestoreInstanceState()才能恢復View分層狀態信息.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章