天天看點

Android Develop Training——管理Activity的生命周期(Managing the Activity Lifecycle)

       Activity生命周期的不同狀态作為離開和傳回你應用的通道,比如當你第一次啟動時,Activity将處于最頂層并接收使用者的焦點,在這個過程中android系統會調用activity的一系列生命周期的回調方法。當使用者做了一個啟動另一個視窗或者跳轉到另一個程式的操作,系統将調用activity生命周期的其它回調方法将其置于背景(此時這個activity雖然處于不可見狀态,但它仍然是存在的)。

       通過activity的回調方法,你可以定義在離開和進入該視窗的一些行為。比如你做了一個音樂播放器,在離開播放視窗時你可能會暫停音頻播放并中斷網絡連接配接,等到再次傳回到這個視窗時,你又會再次連接配接網絡來播放音樂。

       本課将會對activity生命周期的各個回調方法做詳細地講解,讓你知道如何在各個回調方法中如何處理來避免離開了視窗之後還占用系統資源。

啟動一個視窗

         與其它通過main方法來啟動程式的程式設計語言不同的是,Android通過調用它生命周期的相關方法來執行個體化視窗,系統通過調用一系列的回調來啟動和關閉視窗。

         本課将向你介紹Activity的一些重要回調方法,通過正确地管理它們來建立你的第一個視窗執行個體。

了解Activity生命周期回調方法

         在Activity的生命周期中,系統将調用一系列其生命周期回調方法一步一步地來執行個體化視窗。

         當使用者離開視窗時,系統會調用其它的回調方法将視窗置于背景,此時,這個視窗隻有等到從其它視窗傳回到這個界面時才能将其恢複。

Android Develop Training——管理Activity的生命周期(Managing the Activity Lifecycle)

         需要實作什麼回調方法根據你的具體實作情況而定,你并不是必須要實作所有的回調的。重要的是你要了解Activity的每個回調方法,在正确的回調裡面做正确的事情。在管理activity的生命周期的時候你重點需要注意以下幾點:

1、  當使用者使用你的app的時候,突然來電話了或者是跳轉到其它程式的時候不能崩潰掉;

2、  當視窗處于背景時不要占用系統資源;

3、  使用者離開你的界面,回來時不能有資源丢失的情況;

4、  橫豎屏切換時不能有程式崩潰或者程序被銷毀的情況。

在接下來的課程中将介紹activity在不同狀态間切換時回調,activity通常情況處于Resumed、Paused或者Stopped中的一種狀态。

Resumed

         該狀态下,視窗處于前台并且使用者是可以和視窗進行互動的(多數時候又稱為“運作時”狀态)。

Paused

         該狀态下,是目前視窗的上層有半透明或者是沒有覆寫整個螢幕的視窗處于前台,此時此刻,處于Paused狀态的視窗既不接收使用者的輸入,也不執行任何功能。

Stopped

         該狀态下,視窗處于完全不可見狀态,但仍在背景運作,視窗的所有狀态以及成員變量的的值都被保留,但不能執行任何功能。

         有些暫态的生命周期回調方法(比如Created和Started)都是一閃而過的,也就是說,當系統調用onCreate()回調方法後,系統接着回調用onStart()方法,緊接着又調用了onResume()回調。

         上面僅僅介紹了activity基本的生命周期回調,接下來将要學習一些特定的生命周期行為。

指定你程式的主入口

         程式的主入口就是當使用者在桌面點選你應用的圖示,進入程式時調用onCreate()方法的那個視窗,它就是你程式的主界面。

         主入口需要你去工程根目錄下的清單檔案AndroidManifest.xml中定義。

         程式的主視窗必須在清單檔案中的<intent-filter>标簽下包含action為MAIN和category為LAUNCHER,比如:

<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>

           

注:當你通過AndroidSDK工具建立一個工程時,預設會在清單檔案中定義一個程式的主入口Activity。

         當然啦,如果你不在清單檔案中的任何Activity的<intent-filter>标簽下包含action為MAIN和category為LAUNCHER屬性,那麼你的應用将不會在桌面顯示。

建立一個新執行個體

         由于涉及到很多操作,很多程式都包含了若幹個不同的視窗,無論是程式主入口還是使用者通過觸發某些事件進入的視窗也好,每個界面都是通過Activity的onCreate()方法來執行個體化的。

         每個視窗你都需要實作隻被系統調用一次的onCreate()回調方法來初始化視窗,比如你通過實作onCreate()回調來布局互動界面和初始化某些成員變量。

         下例所示的代碼中,通過onCreate()回調來布局視窗的互動、初始化成員變量和配置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的原因是阻止程式在API版本為5之前的系統中運作,舊版本的系統會報運作時異常。

         一旦onCreate()方法執行完成後,系統會立即調用onStart()和onResume()方法,你的視窗永遠不會是處于Created或者Started狀态的,從技術角度來講,系統在調用onStart()方法後程式就處于可見狀态了,但緊接着就會繼續調用onResume()并一直處于該狀态直到類似于電話接入、跳轉到其他程式或者待機等情況的出現。

注意:onCreate()方法中的參數savedInstanceState将會在後續“重新建立視窗”的課程中介紹。

Android Develop Training——管理Activity的生命周期(Managing the Activity Lifecycle)

銷毀視窗

         當你的視窗執行個體完全從系統記憶體中移除時系統會觸發onDestory()回調方法。

         很多程式根本不需要實作onDestory()回調,因為在系統調用onPause()或者onStop()方法時已經将視窗相關資源清理幹淨了。但是你的程式中涉及到一些通過線程加載資源的東西,你就應該實作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()方法,比如這個界面是一個選擇跳轉到其它視窗的界面,在這種情況下,系統在銷毀視窗時是不會調用除onDestory()方法以外的任何方法的。

暫停和恢複視窗

         在應用運作的過程中,可能有各種可視元件跳到前台使目前活動視窗處于暫停狀态,比如,當一個半透明的視窗被打開(比如一個對話框風格的視窗)時,之前的活動視窗就處于暫停狀态了,在它擷取到使用者焦點之前它都一直處于暫停狀态。

         然後,一旦視窗被完全阻塞并且不可見,它将會停止(這個問題将在下面的章節中讨論)。

         一旦你的視窗進入暫停狀态,系統便會調用Activity的onPause()方法來讓你停止正在運作但在暫停後就不會繼續的動作(比如視訊播放)或者儲存一些需要持久化的資料。當再次傳回到這個視窗時,你可以通過系統調用的onResume()方法來恢複之前儲存的内容。

注意:當你的視窗收到onPause()回調時,可能預示者隻是短暫的暫停一會,一會又會獲得使用者焦點。

Android Develop Training——管理Activity的生命周期(Managing the Activity Lifecycle)

暫停視窗

         當系統調用你視窗的onPause()方法時,從技術角度上來講你的視窗仍然部分可見,但更多時候預示者使用者離開了你的視窗,系統将立即進入Stopped狀态,在onPause()方法中你應該做如下事情:

1、  停止動畫或者其它消耗CPU且正在運作的動作;

2、  送出使用者希望儲存但是還沒儲存的改變;

3、  釋放系統資源,比如廣播接收,傳感器句柄或則其它耗電的東西。

比如,如果你的應用中使用到了相機,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密集型工作,比如儲存資料庫之類的,因為它可能影響你進入另一個視窗的速度,進而影響使用者體驗(你應該在onStop()方法中執行這些費時的操作)。

注意:當你的視窗處于暫停狀态時,Activity執行個體仍然是在記憶體中駐留的,這樣在視窗恢複時你就不需要重新初始化視窗的相關元件了。

恢複視窗

         當視窗從暫停狀态恢複時,系統将調用onResume()方法。

        在你的視窗每次恢複到前台時都是要調用onResume()方法的,包括視窗被執行個體化的時候。是以說呢,你應該在onResume()方法中初始化在onPause()方法中釋放掉的元件資訊(比如啟動動畫或者初始化隻有當視窗擷取使用者焦點時才需要使用的元件)。

         下面這個例子就是與在視窗暫停執行個體中相對應的例子,在onResume()方法裡重新初始化在onPause()方法中釋放掉的相機資源。

@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
    }
}

           

停止和重新開機視窗

         暫停和重新開機視窗在視窗生命周期的管理中是一個非常重要的過程,在使用這兩個方法的過程中有幾點關鍵事項:

1、  當使用者打開最近打開應用并且從你的應用跳轉到其它應用的時候,你應用目前的活動視窗将會停止掉,當使用者點選你應用的桌面圖示或者通過最近打開應用傳回你的應用時,這個視窗會被重新開機;

2、  當使用者通過某種操作在目前視窗中啟動了另一個視窗時,在下一個視窗被建立完成後目前視窗會被停止掉,當使用者點選傳回按鈕傳回到方才停止的視窗時,這個視窗将會被重新開機。

3、  在使用你應用的過程中收到來電時你應用的目前活動視窗也會被停止。

Activity針對停止和重新開機視窗提供了onStop()和onRestart()這兩個回調方法,以便在視窗處于這兩種狀态時你做出相應的回報。與暫停狀态可以讓部分UI元件可見不同的是,停止狀态是在視窗處于不可見且使用者焦點已經在另外一個視窗時才會被調用。

Android Develop Training——管理Activity的生命周期(Managing the Activity Lifecycle)

當使用者離開目前視窗時,系統會調用onStop()方法來停止目前活動視窗(1)。當使用者從停止狀态再次傳回到這個視窗時,系統會調用onRestart()(2)方法,緊接着會調用onStart()(3)和onResume()(4)。不管是什麼情況使得視窗停止,系統都會在會掉onStop()方法之前會掉onPause()方法的。

停止活動視窗

         當視窗接收到系統回調的onStop()方法時,它将處于不可見狀态并且應該釋放掉程式不再使用的資源,一旦你的視窗被停止,系統可能會在記憶體不足的情況下銷毀它,這種情況下系統是不會調用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.
            );
}

           

當你的視窗被停止後,它仍在在記憶體中,一旦有某種操作将其喚醒,它又會從記憶體中恢複,這種情況下你就不需要再次初始化視窗的相關資訊了,系統通常情況下都會在視窗被喚醒時恢複視圖的布局和使用者輸入的相關資訊。

注意:即使你的視窗在停止狀态下被系統銷毀掉,當它由于某種操作被系統喚醒時,系統還是能夠通過儲存到Bundle中的相關資訊來恢複視窗的相關屬性的。

啟動/重新開機 視窗

         當你的視窗從停止狀态恢複時,系統将會回調onRestart()方法,不僅如此,在你的視窗從不可視狀态變為可視狀态時,系統都是會調用onRestart()的(不管你的重新建立視窗也好,第一次建立也好)。它是恢複視窗暫停狀态的最好地方,但可不包含停止掉的東西額。

         每個應用的具體情況都不相同,是以對于在onRestart()方法中做如何操作是沒有統一的規範的。但是由于不管你是從停止狀态恢複也好,或者說是第一次被建立也好(當系統不存在視窗的執行個體的時候),系統都是會調用onStart()方法的。哈,你也想到了是吧,onStart()方法就該幹與onStop()方法相對應的事情,onStop()方法中停止掉什麼,onStart()方法中就應該恢複什麼。

         比如說哈,有可能出現系統在離開你的應用很久後會再次傳回的情況,此時onRestart()方法是一個非常适合于驗證系統是否可以使能該視窗的地方。

@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    
}

           

        當你的視窗被銷毀是,系統會調用你視窗的onDestory()方法。因為在調用onDestory()方法之前會調用onStop()方法來釋放掉你程式中的大部分資源,是以沒有多少程式是需要實作onDestory()方法的,不過onDestory()方法是處理記憶體釋放的好地方,是以關于結束線程或者其它持久運作的東西都應該在onDestory()方法中來處理。

重新建立視窗

         當你主動點選視窗的傳回按鈕或者調用finish()方法時,系統會在前台需要很多系統資源而系統記憶體又不足的情況下銷毀掉你的視窗。

         當使用者點選傳回按鈕或者是視窗自身被結束時系統都會銷毀目前活動視窗,此時視窗就會由于被銷毀而消失。但如果是由于某種異常導緻視窗消失時,這種情況下系統會通過Bundle來儲存視窗的相關屬性以便重新開機視窗時恢複資料資訊的。

注意:視窗會在橫豎屏轉換時銷毀和重建,當涉及到切換橫豎屏時,系統會銷毀并重建目前活動視窗,一是由于螢幕方向改變了,還有就是有可能你需要重新加載視窗的相關資源(比如布局之類的)。

         預設情況下,系統會通過Bundle執行個體來儲存視窗中每個視圖的資訊,是以你不需要擔心視窗恢複時會有資訊丢失的情況。

注意:為了能夠保證系統儲存每個視圖的資訊,你需要為他們指定獨一無二的ID。

         如果你需要在視窗停止時儲存上面介紹之外的資訊,你就必須要自己實作onSaveInstanceState()回調方法了。系統會在使用者離開你的視窗時通過Bundle來儲存你需要儲存的資訊,在這個方法裡面儲存後系統會在視窗被恢複時調用onRestoreInstanceState()以便你取出之前儲存的資訊。

Android Develop Training——管理Activity的生命周期(Managing the Activity Lifecycle)

儲存視窗的狀态

         當你的視窗停止後,系統會調用onSaveInstanceState()方法通過鍵值對的方式來儲存你需要儲存的資訊,該方法預設會儲存視窗中所有視圖的資訊,比如編輯框中的輸入資訊或者是清單的滾動位置等。

         為了能夠儲存除視窗預設會儲存的資訊外,你必須自己在onSaveInstanceState()方法中來儲存你的資料,eg:。

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);
}

           

恢複視窗狀态

         當視窗被銷毀後再次進入時,系統會重建視窗,你可以在onRestoreInstanceState()方法中恢複之前儲存的資訊,然後通過onCreate()方法的savedInstance參數來判斷是視窗是重建還是建立,并且可以通過saveInstance來擷取之前儲存的資訊。

         不管是建立還是重建視窗系統都會調用onCreate()方法,是以你需要通過onCreate()方法中的參數saveInstance來判斷到底是建立還是重建。

         比如,你可以照着下面這樣來處理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
    }
    ...
}

           

       你同樣可以通過實作onRestoreInstanceState()來恢複之前你儲存的資訊,它将會在onStart()方法之後緊接着被調用,系統隻會在之前有過儲存視窗資訊操作的情況下才會調用該方法,是以你就不需要去判斷它的參數是否為空啦。

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);
}

           

原文連結:Mainging the Activity LifeCycle