天天看點

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

打開一個Activity

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

了解Activity生命周期回調方法

在Activity的生命周期中,系統調用一系列核心生命周期方法一步步走向一個金字塔頂端,Activity生命周期每一個回調都相當于朝向金字塔的一步,随着系統創造一個activity示例,每個回調方法都使得activity走向金字塔頂端更近一步,金字塔頂端就是activity運作在前台可以與使用者互動的狀态,随着使用者離開Activity,系統調用其他的生命周期回調方法慢慢走下金字塔,某些情況下,activity會在下來一兩步後停下等待(比如當使用者切換到其他的app中),這種情況activity有可能回到金字塔頂端(如果使用者切回到目前activity),并且activity恢複到離開activity時的樣子

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

根據你自己的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文檔Training之管理Activity生命周期

摧毀一個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分層狀态資訊.