第二部分:開發簡要指南-第四章 Activity的生命周期


第4章 Activity的生命周期

當我們在屏幕上發生一些事件,比如點擊某個按鈕用來切換Activity或來電情況等,都會觸發Activity的生命周期事件,為什么我們要了解Activity的生命周期呢?因為了解了它的生命周期我們才能根據不同的情況來處理我們的應用,讓我們的應用程序更好用,更人性化。例如,如果你建立一個流媒體視頻播放器,當用戶切換到另一個應用程序,你可能會暫停視頻和終止網絡連接。當用戶返回時,你可以重新連接到網絡,並允許用戶恢復從同一地點的錄像。這些行為都可以直接在Activity的生命周期方法中處理它們。可能有的讀者以前在網上看過關於生命周期的解釋。這里我會透徹的,詳細的講述它的原理。讓你的“角色等級”在上一個檔次。下面是內容預覽:

1.啟動一個Activity

學習了解Activity生命周期的基本知識,用戶創建和執行Activity是怎樣調用生命周期的方法的

2.暫停和恢復一個Activity

了解Activity暫停(paused)和恢復(resumed)時發生了什么,在這些狀態改變期間我們應該做什么

3.停止和重新啟動Activity

當activity完成它的工作后發生了什么

4.重新創建Activity

當你的Activity被destroyed(摧毀)后發生了什么,並且學習如何能重新創建這個Activity

4.1 啟動一個Activity

不同於其他程序范例用main()方法啟動,Android系統啟動Activity通過在不同的生命周期階段調用指定的回調方法來完成初始化。 

4.1.1了解生命周期的回調函數

在一個Activity的生命周期中,系統會根據一定順序來調用一套核心的生命周期方法,就像階梯金字塔一樣,也就是說,Activity生命周期的每一個階段都是一個階梯,由於系統創建了一個新的Activity實例,每一個回調方法會讓Activity移向金字塔的頂端,當到達頂端時,用戶就可以與之交互了。當用戶離開這個Activity時,會向下調用部分生命周期的方法。接下來我們看下圖4-1:

 

圖4-1 像金字塔一樣的圖,一些回調方法調用后會發生什么事,可以從圖中看到

根據Activity的復雜性,你可能不需要實現所有的生命周期方法,然后你必須清楚了解每一個方法會在什么樣情況下被調用,這樣你才能正確處理一些用戶的行為。你需要考慮以下情況來靈活實現一些生命周期的方法:

  1. 如果用戶接到一個電話,或者切換到另一外程序,當繼續使用你的應用程序時,它不會崩潰。
  2. 當用戶沒有積極的使用它時,不消耗寶貴的系統資源
  3. 如果用戶離開你的應用程序並在稍后返回,不要丟失用戶的進度
  4. 當屏幕橫向或縱向旋轉時,不會崩潰或丟失用戶進度

仔細觀察,我們會發現圖4-1中有三個狀態時持續存在一段時間的

1.Resumed(恢復

在這種狀態下,Activity在前台並且可以與用戶交互。(有時也可稱為running狀態)

2.Paused(暫停)

在這種狀態下,Activity被另一個Activity部分遮擋。暫停狀態下的Activity不接受用戶輸入,並不能執行任何代碼。

3.Stopped(停止)

在這種狀態下,Activity完全隱藏對用戶不可見,它被放入后台,雖然它停止了,但所有成員變量都被保留,這種狀態下不可見,也不能執行代碼。

其他狀態(創建和啟動)是短暫的,也就是說當系統調用onCreate(),onStart(),onRestart()時就瞬間的行為。這就是Activity的生命周期,現在讓我們學習一些具體的生命周期行為。

4.1.2 指定應用程序啟動的Activity

當用戶從home屏幕選中你的App圖標,系統會為你已經聲明的“launcher(啟動)”Activity調用onCreate()方法,這個Activity為你應用程序的主入口點。你可以在AndroidManifest.xml聲明需要啟動的Activity,如代碼清單4-1所示:

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

 

代碼清單4-1

需要說明的是使用Eclipse創建Android項目時,代碼清單4-1的內容已經被自動創建好了。雖然它幫我們做好了,但我們需要了解這段代碼的意思。只要刪除action.MAIN或category.LAUNCHER其中一個,那么你的應用程序圖標將不會顯示在home屏幕上,就是說這個程序雖然安裝了,但home屏幕上看不到。

4.1.3 創建一個新的實例

大部分App包含幾個不同的Activities用來讓用戶執行不同的操作。當系統創建Activity實例的時候,會調用onCreate()方法。Activity的整個生命只有一次。例如,你在實現的onCreate()方法中應該定義用戶界面,和一些變量實例。以下代碼清單4-2是使用onCreate()的例子。

TextView mTextView; 
 
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 
    // 為這個Activity設置用戶界面布局
    // layout 文件定義在res/layout/main_activity.xml
    setContentView(R.layout.main_activity);
    
    // 初始化成員TextView
    mTextView = (TextView) findViewById(R.id.text_message);
    
    //確保我們執行的機器上使用的系統為3.0或更高,因為只有這樣才能使用ActionBar APIs 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        // 執行一些與ActionBar相關的操作
        ActionBar actionBar = getActionBar();
        actionBar.setHomeButtonEnabled(false);
    }
}

 

代碼清單4-2

一旦執行完畢的onCreate()系緊接着調用onStart()和onResume()方法。您的Activity從來沒有停留在創建或啟動狀態,從技術上講Android在onStart()方法調用后就可見了,就由於這種片刻的行為,所以大部分時間都是停留在onResume()狀態下的。注意onCreate()中有一個savedInstanceState參數,這個當重新創建Activity時可以用到。

4.1.4 摧毀Activity

當前我們的Activity起始於onCreate()必然有一個對應的結束方法,那就是onDestroy()。當你的Activity實例需要刪除時,調用此方法來讓虛擬機回收。可能我們並不經常實現這個方法。在調用onDestroy()之前系統會調用onPause()和onStop()。順序就和圖4-1的金字塔一樣,從上至下。

4.2 暫停和恢復一個Activity

在使用某個App的過程中,前台Activity有時會被其他可視化組件阻擋,導致Activity處於暫停狀態。其實當你彈出一個對話框時,底部的Activity不會處於暫停狀態。當系統調用onPause()方法時,Activity進入暫停狀態,例如在播放視頻或音樂時,當由於一個來電而進入暫停狀態的時候,你應該在onPause()中使用代碼暫停正好播放的視頻或音樂。當系統調用onResume()方法后,Activity又回到了resume狀態。

4.2.1 暫停Activity

當系統對你的Activity調用onPause()方法時,專業點來說你的Act仍然部分是可見的,但大多數跡象表明用戶正在離開Act,很快進入停止狀態。以下情況我們需要重寫onPause()方法:

1.停止動畫或其他消耗CPU之類的動作

2.提交未保存的更改,但只有用戶希望這么做時才需要保存,例如正在編寫電子郵件

3.釋放系統資源,例如廣播,傳感器(例如GPS),或任意影響電池壽命的資源

例如, 如果我們正在使用攝像頭,在暫停方法中應該釋放它。如代碼清單4-3所示:

@Override
public void onPause() {
    super.onPause();  // 總是優先調用父類的方法
    // 釋放攝像頭因為當暫停時,我們不需要它並且其他Act可能需要使用它
    if (mCamera != null) {
        mCamera.release()
        mCamera = null;
    }
}

 

代碼清單4-3

我們應該在onPause()方法中執行相對簡單的操作,以便迅速過渡到下一個目的地,例如寫入數據庫這種密集型CPU的操作,我們可以放入onStop()中處理。

4.2.2 恢復Activity

系統系統onResume()方法后當用戶從暫停狀態想回到resume狀態。要知道系統每次調用這個方法,你的Act又會回到前台。某些資源需要你重新啟動它。下面代碼清單4-4就是與4-3對應的處理方式,這里相機需要初始化

@Override
public void onResume() {
    super.onResume();  
    if (mCamera == null) {
        initializeCamera(); //自己編寫的本地方法來處理相機的初始化
    }
}

代碼清單4-4

4.3 停止和重新啟動一個Activity

  假設你有一個正在運行的程序,當用戶點擊home鍵切換到主屏幕時,此時你的Act會依次執行onPause()->onStop()。如果用戶又沖home屏幕點擊你應用程序的圖標的話,這時會重新啟動Act來返回到我們的應用程序。還有一種情況,例如你的一個程序有兩個Act,假設第一個為A,第二個為B,當A跳轉到B時,A會執行onStop()方法,然后進入B執行一些onCreate()等之類的方法,但當你在B中點擊back鍵返回時,這時A會執行reStart()方法來重新啟動。與onPause()不同之處是onStop()會讓你的Act失去焦點並且UI不再可見,下面我們看一下圖4-2:

 

圖4-2 從stop狀態到resume狀態需要經過2,3,4步驟,而反之總是從pause->stop。

4.3.1 停止Activity

當系統調用onStop()方法時,act不再可見並且幾乎釋放掉所有用戶不在使用的資源。一旦你的act停止了,如果需要恢復系統資源的話,系統可能摧毀這個實例。極端的情況下,系統可以直接kill掉你的APP進程並且沒有調用onDestroy()方法,所以onStop()方法很重要,你應該在onStop()中釋放資源,不然可能有內存泄露的風險。雖然onPause()方法在onStop()之前調用,但我們應該使用的onStop()來執行更高,CPU密集型的關閉操作,如信息寫入數據庫。下面代碼清單4-5就是一個例子:

@Override
protected void onStop() {
    super.onStop(); 
    // 保存當前內容的草稿,因為處於stop狀態,我們要確保不會丟失當前內容的進度
    ContentValues values = new ContentValues();
    values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
    values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());
    getContentResolver().update(mUri, values, null, null);
}

 

代碼清單4-5

如果用戶在編輯一個EditText文本,該內容被自動保留,我們不需要保存和恢復。因為即使在onStop()狀態下,act實例依舊存在,則正在編寫的EditText文本也在內存中。但是如果你按back鍵,離開程序(調用onDestroy),那么系統不會保存你的View

的狀態(例如在EditText中的文本)。

4.3.2 開始/重新開始Activity

當你的Act從停止狀態回到前台,首先需要調用onRestart()方法,然后調用onStart()。只有在stop狀態到resume狀態才會調用onRestart(),它的特殊性可以讓你在此方法中處理一些特別的操作。下面我們看下代碼清單4-6:

@Override
protected void onStart() {
    super.onStart(); 
    
    // activity要么是重新開始要么第一次開始,所以我們需要確保GPS被打開
    LocationManager locationManager = 
            (LocationManager) getSystemService(Context.LOCATION_SERVICE);
    boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
    if (!gpsEnabled) {
        //如果GPS沒打開,你可以創建一個對話框,請求用戶打開GPS。
    }
}
 
@Override
protected void onRestart() {
    super.onRestart();
    // 這里從stop狀態轉入resume狀態時,才會被調用
}

 

代碼清單4-6

4.4 重新創建一個Activity

當你的act由於用戶按下back鍵或者代碼中調用finish()這種正常行為而destroy的話,act實例就一去不復返了。然而由於系統的限制而destroy的話,雖然act的實例消失,但系統會記住這個實例。當用戶導航回到上次的act,系統會使用Bundle來恢復上一次act的狀態。注意當你每次旋轉屏幕時,act會被destroy然后重新創建,你可以需要重新加載一些資源,或者在AndroidManifest.xml中設置configChange讓act旋轉的時候不重新創建。

默認的系統使用Bundle來保存布局中的各個View對象的信息。所以你的act實例被銷毀並重新創建后,會自動恢復到以前的布局狀態。然而我們的act可能有更多的狀態信息需要恢復,例如像恢復某些成員變量等。為了增加額外需要保存的數據。我們還有一個額外的回調方法onSaveInstanceState(),當用戶離開你的act系統會調用此方法,你可以通過其中的Bundle來保存需要的數據,當系統需要重新創建實例的時候會使用onRestoreInstanceState()方法或者直接在onCreate()中使用參數Bundle來恢復。記住只有當你的act由於系統原因被destroy時使用它們才是有用的,直白來說就是由於系統原因使act實例不存在時才需要考慮使用它們。下面我們看一下圖4-3:

 

圖4-3 保存和恢復act實例狀態的示意圖

4.4.1 保存你的Activity狀態

隨着你的act啟動到停止,系統會調用onSaveInstanceState()方法,因此你的act能使用鍵值對的形式保存狀態信息。默認來說它只保存view的狀態信息,例如EditText中的文本或者ListView滾動的位置。你的act為了保存額外的狀態信息,你必須實現onSaveInstanceState()方法,使用Bundle參數添加鍵值對。例如代碼清單4-7:

static final String STATE_SCORE = "playerScore";

static final String STATE_LEVEL = "playerLevel";

...

 

@Override

public void onSaveInstanceState(Bundle savedInstanceState) {

    // 保存用戶當前的游戲狀態

    savedInstanceState.putInt(STATE_SCORE, mCurrentScore);

    savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);

    // 調用父類的方法用來保存view的狀態

    super.onSaveInstanceState(savedInstanceState);

}

 

代碼清單4-7

4.4.2 恢復你的Activity狀態

當你的act在先前被摧毀又被重新創建后,你能從Bundle中通過系統自動恢復到摧毀之前的狀態。在onCreate()和onRestoreInstanceState()兩個回調方法中的Bundle是同一實例的狀態信息。因為不管系統是創建新的實例還是重新創建先前的實例都需要調用onCreate()方法,在你企圖讀取Bundle之前必須檢查Bundle是否為null。如果為null系統會創建一個新的act實例,否則會恢復先前摧毀的那個act實例:下面是在onCreate()中恢復實例數據的代碼:如代碼清單4-8所示:

@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

  

    //檢查是否是重新創建的先前的實例

    if (savedInstanceState != null) {

        //從保存的狀態中恢復成員變量的值

        mCurrentScore = savedInstanceState.getInt(STATE_SCORE);

        mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);

    } else {

        // 對於一個新的實例可能需要初始化默認值而不是恢復

    }

    ...

}

 

代碼清單4-8

你可以選擇實現onRestoreInstanceState()來代替onCreate()恢復狀態,此方法在onStart()之后調用。系統只有在有保存狀態的時候才會調用onRestoreInstanceState()來恢復狀態,所以我們不需要檢查Bundle是否為null,如代碼清單4-9所示:

public void onRestoreInstanceState(Bundle savedInstanceState) {

    // 調用父類的方法來恢復View的狀態

    super.onRestoreInstanceState(savedInstanceState);

  

    // 恢復保存實例中的成員變量

    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);

    mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);

}

 

代碼清單4-9

 完整項目下載地址:http://url.cn/LtHY7f

本文來自jy02432443,QQ78117253。轉載請保留出處,並保留追究法律責任的權利


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM