Android開發(fā)指南之Activity

Activity是一個(gè)應(yīng)用組件,用戶可與其提供的屏幕進(jìn)行交互。以執(zhí)行撥打電話,拍攝照片,發(fā)送電子郵件或查看地圖等操作。 每個(gè) Activity 都會(huì)獲得一個(gè)用于繪制其用戶界面的窗口(window)。窗口通常會(huì)充滿屏幕,但也可小于屏幕并浮動(dòng)在其他窗口之上。
??一個(gè)應(yīng)用通常由多個(gè)彼此松散聯(lián)系的 Activity 組成。 一般會(huì)指定應(yīng)用中的某個(gè) Activity 為“主”Activity(Main Activity),即首次啟動(dòng)應(yīng)用時(shí)呈現(xiàn)給用戶的那個(gè) Activity。 而且每個(gè) Activity 均可啟動(dòng)另一個(gè) Activity,以便執(zhí)行不同的操作。 每次新 Activity 啟動(dòng)時(shí),前一 Activity 便會(huì)停止,但系統(tǒng)會(huì)在堆棧(“返回棧”)中保留該 Activity。 當(dāng)新 Activity 啟動(dòng)時(shí),系統(tǒng)會(huì)將其推送到返回棧上,并取得用戶焦點(diǎn)。 返回棧遵循“后進(jìn)先出”堆棧機(jī)制,因此,當(dāng)用戶完成當(dāng)前 Activity 并按“返回” 按鈕時(shí),系統(tǒng)會(huì)從堆棧中將其彈出(并銷毀),然后恢復(fù)前一 Activity。(任務(wù)和返回棧文檔中對(duì)返回棧有更詳細(xì)的闡述。)
??當(dāng)一個(gè) Activity 因某個(gè)新 Activity 啟動(dòng)而停止時(shí),系統(tǒng)會(huì)通過該 Activity 的生命周期回調(diào)方法通知其這一狀態(tài)變化。Activity 因狀態(tài)變化(系統(tǒng)是創(chuàng)建 Activity、停止 Activity、恢復(fù) Activity 還是銷毀 Activity)而收到的回調(diào)方法可能有若干種,每一種回調(diào)方法都會(huì)為您提供執(zhí)行與該狀態(tài)變化相應(yīng)的特定操作的機(jī)會(huì)。 例如,停止時(shí),您的 Activity 應(yīng)釋放任何大型對(duì)象,例如網(wǎng)絡(luò)或數(shù)據(jù)庫(kù)連接。 當(dāng) Activity 恢復(fù)時(shí),您可以重新獲取所需資源,并恢復(fù)執(zhí)行中斷的操作。 這些狀態(tài)轉(zhuǎn)變都是 Activity 生命周期的一部分。
??本文的其余部分闡述有關(guān)如何創(chuàng)建和使用 Activity 的基礎(chǔ)知識(shí)(包括對(duì) Activity 生命周期工作方式的全面闡述),以便您正確管理各種 Activity 狀態(tài)之間的轉(zhuǎn)變。

創(chuàng)建Activity

要?jiǎng)?chuàng)建 Activity,您必須創(chuàng)建 Activity 的子類(或使用其現(xiàn)有子類)。您需要在子類中實(shí)現(xiàn) Activity 在其生命周期的各種狀態(tài)之間轉(zhuǎn)變時(shí)(例如創(chuàng)建 Activity、停止 Activity、恢復(fù) Activity 或銷毀 Activity 時(shí))系統(tǒng)調(diào)用的回調(diào)方法。 兩個(gè)最重要的回調(diào)方法是:

onCreate()

您必須實(shí)現(xiàn)此方法。系統(tǒng)會(huì)在創(chuàng)建您的 Activity 時(shí)調(diào)用此方法。您應(yīng)該在實(shí)現(xiàn)內(nèi)初始化 Activity 的必需組件。 最重要的是,您必須在此方法內(nèi)調(diào)用 setContentView(),以定義 Activity 用戶界面的布局。

onPause()

系統(tǒng)將此方法作為用戶離開 Activity 的第一個(gè)信號(hào)(但并不總是意味著 Activity 會(huì)被銷毀)進(jìn)行調(diào)用。 您通常應(yīng)該在此方法內(nèi)確認(rèn)在當(dāng)前用戶會(huì)話結(jié)束后仍然有效的任何更改(因?yàn)橛脩艨赡懿粫?huì)返回)。

您還應(yīng)使用幾種其他生命周期回調(diào)方法,以便提供流暢的 Activity 間用戶體驗(yàn),以及處理導(dǎo)致您的 Activity 停止甚至被銷毀的意外中斷。 后文的管理 Activity 生命周期部分對(duì)所有生命周期回調(diào)方法進(jìn)行了闡述。

實(shí)現(xiàn)用戶界面

Activity 的用戶界面是由層級(jí)式視圖(衍生自 View 類的對(duì)象)提供的。每個(gè)視圖都控制 Activity 窗口內(nèi)的特定矩形空間,可對(duì)用戶交互作出響應(yīng)。 例如,視圖可以是在用戶觸摸時(shí)啟動(dòng)某項(xiàng)操作的按鈕。
??您可以利用 Android 提供的許多現(xiàn)成視圖設(shè)計(jì)和組織您的布局。“小工具”(Widget)是提供按鈕、文本字段、復(fù)選框或僅僅是一幅圖像等屏幕視覺(交互式)元素的視圖。 “布局”(Layout)是衍生自 ViewGroup 的視圖,為其子視圖提供唯一布局模型,例如線性布局(Linear Layout)、網(wǎng)格布局(Grid Layout)或相對(duì)布局(Relative Layout)。您還可以為 View 類和 ViewGroup 類創(chuàng)建子類(或使用其現(xiàn)有子類)來自行創(chuàng)建小工具和布局,然后將它們應(yīng)用于您的 Activity 布局。
??利用視圖定義布局的最常見方法是借助保存在您的應(yīng)用資源內(nèi)的 XML 布局文件。這樣一來,您就可以將用戶界面的設(shè)計(jì)與定義 Activity 行為的源代碼分開維護(hù)。 您可以通過 setContentView()將布局設(shè)置為 Activity 的 UI,從而傳遞布局的資源 ID。不過,您也可以在 Activity 代碼中創(chuàng)建新 View,并通過將新 View 插入 ViewGroup 來創(chuàng)建視圖層次,然后通過將根 ViewGroup 傳遞到 setContentView()來使用該布局。
??如需了解有關(guān)創(chuàng)建用戶界面的信息,請(qǐng)參閱用戶界面文檔。

在清單文件中聲明 Activity

必須在清單文件中聲明您的 Activity,這樣系統(tǒng)才能訪問它。 要聲明您的 Activity,請(qǐng)打開您的清單文件,并將<activity>元素添加為 <application> 元素的子項(xiàng)。例如:

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

您還可以在此元素中加入幾個(gè)其他特性,以定義 Activity 標(biāo)簽、Activity 圖標(biāo)或風(fēng)格主題等用于設(shè)置 Activity UI 風(fēng)格的屬性。android:name 特性是唯一的必需特性—它指定 Activity 的類名。應(yīng)用一旦發(fā)布,即不應(yīng)更改此類名,否則,可能會(huì)破壞諸如應(yīng)用快捷方式等一些功能(請(qǐng)閱讀博客文章 Things That Cannot Change [不能更改的內(nèi)容])。
請(qǐng)參閱 <activity> 元素參考文檔,了解有關(guān)在清單文件中聲明 Activity 的詳細(xì)信息。

使用 Intent 過濾器

<activity> 元素還可指定各種 Intent 過濾器——使用 <Intent-filter>元素——以聲明其他應(yīng)用組件激活它的方法。當(dāng)您使用 Android SDK 工具創(chuàng)建新應(yīng)用時(shí),系統(tǒng)自動(dòng)為您創(chuàng)建的存根 Activity 包含一個(gè) Intent 過濾器,其中聲明了該 Activity 響應(yīng)“主”操作且應(yīng)置于“l(fā)auncher”類別內(nèi)。 Intent 過濾器的內(nèi)容與以下所示類似:

<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>元素指定這是應(yīng)用的“主”入口點(diǎn)。<category> 元素指定此 Activity 應(yīng)列入系統(tǒng)的應(yīng)用啟動(dòng)器內(nèi)(以便用戶啟動(dòng)該 Activity)。
??如果您打算讓應(yīng)用成為獨(dú)立應(yīng)用,不允許其他應(yīng)用激活其 Activity,則您不需要任何其他 Intent 過濾器。 正如前例所示,只應(yīng)有一個(gè) Activity 具有“主”操作和“l(fā)auncher”類別。 您不想提供給其他應(yīng)用的 Activity 不應(yīng)有任何 Intent 過濾器,您可以利用顯式 Intent 自行啟動(dòng)它們(下文對(duì)此做了闡述)。
??不過,如果您想讓 Activity 對(duì)衍生自其他應(yīng)用(以及您的自有應(yīng)用)的隱式 Intent 作出響應(yīng),則必須為 Activity 定義其他 Intent 過濾器。 對(duì)于您想要作出響應(yīng)的每一個(gè) Intent 類型,您都必須加入相應(yīng)的 <Intent-filter>,其中包括一個(gè) <action>元素,還可選擇性地包括一個(gè) <category> 元素和/或一個(gè) <data> 元素。這些元素指定您的 Activity 可以響應(yīng)的 Intent 類型。

如需了解有關(guān)您的 Activity 如何響應(yīng) Intent 的詳細(xì)信息,請(qǐng)參閱 Intent 和 Intent 過濾器文檔。

啟動(dòng) Activity

您可以通過調(diào)用 startActivity(),并將其傳遞給描述您想啟動(dòng)的 Activity 的 Intent 來啟動(dòng)另一個(gè) Activity。Intent 對(duì)象會(huì)指定您想啟動(dòng)的具體 Activity 或描述您想執(zhí)行的操作類型(系統(tǒng)會(huì)為您選擇合適的 Activity,甚至是來自其他應(yīng)用的 Activity)。 Intent 對(duì)象還可能攜帶少量供所啟動(dòng) Activity 使用的數(shù)據(jù)。
??在您的自有應(yīng)用內(nèi)工作時(shí),您經(jīng)常只需要啟動(dòng)某個(gè)已知 Activity。 您可以通過使用類名創(chuàng)建一個(gè)顯式定義您想啟動(dòng)的 Activity 的 Intent 對(duì)象來實(shí)現(xiàn)此目的。 例如,可以通過以下代碼讓一個(gè) Activity 啟動(dòng)另一個(gè)名為SignInActivity 的 Activity:

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

不過,您的應(yīng)用可能還需要利用您的 Activity 數(shù)據(jù)執(zhí)行某項(xiàng)操作,例如發(fā)送電子郵件、短信或狀態(tài)更新。 在這種情況下,您的應(yīng)用自身可能不具有執(zhí)行此類操作所需的 Activity,因此您可以改為利用設(shè)備上其他應(yīng)用提供的 Activity 為您執(zhí)行這些操作。 這便是 Intent 對(duì)象的真正價(jià)值所在——您可以創(chuàng)建一個(gè) Intent 對(duì)象,對(duì)您想執(zhí)行的操作進(jìn)行描述,系統(tǒng)會(huì)從其他應(yīng)用啟動(dòng)相應(yīng)的 Activity。 如果有多個(gè) Activity 可以處理 Intent,則用戶可以選擇要使用哪一個(gè)。 例如,如果您想允許用戶發(fā)送電子郵件,可以創(chuàng)建以下 Intent 對(duì)象:

 Intent intent = new Intent(Intent.ACTION_SEND);
 intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
 startActivity(intent);

添加到 Intent 中的 EXTRA_EMAIL extra 是一個(gè)字符串?dāng)?shù)組,其中包含應(yīng)將電子郵件發(fā)送到的電子郵件地址。 當(dāng)電子郵件應(yīng)用響應(yīng)此 Intent 時(shí),它會(huì)讀取 extra 中提供的字符串?dāng)?shù)組,并將它們放入電子郵件撰寫窗體的“收件人”字段。 在這種情況下,電子郵件應(yīng)用的 Activity 啟動(dòng),并且當(dāng)用戶完成操作時(shí),您的 Activity 會(huì)恢復(fù)執(zhí)行。

啟動(dòng) Activity 以獲得結(jié)果

有時(shí),您可能需要從啟動(dòng)的 Activity 獲得結(jié)果。在這種情況下,請(qǐng)通過調(diào)用 startActivityForResult()(而非startActivity())來啟動(dòng) Activity。 要想在隨后收到后續(xù) Activity 的結(jié)果,請(qǐng)實(shí)現(xiàn) onActivityResult()回調(diào)方法。 當(dāng)后續(xù) Activity 完成時(shí),它會(huì)使用 Intent 向您的 onActivityResult() 方法返回結(jié)果。
??例如,您可能希望用戶選取其中一位聯(lián)系人,以便您的 Activity 對(duì)該聯(lián)系人中的信息執(zhí)行某項(xiàng)操作。 您可以通過以下代碼創(chuàng)建此類 Intent 并處理結(jié)果:

private void pickContact() {
        // Create an intent to "pick" a contact, as defined by the content provider URI
        Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
        startActivityForResult(intent, PICK_CONTACT_REQUEST);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // If the request went well (OK) and the request was PICK_CONTACT_REQUEST
    if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
        // Perform a query to the contact's content provider for the contact's name
        Cursor cursor = getContentResolver().query(data.getData(),
        new String[] {Contacts.DISPLAY_NAME}, null, null, null);
        if (cursor.moveToFirst()) { // True if the cursor is not empty
            int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
            String name = cursor.getString(columnIndex);
            // Do something with the selected contact's name...
        }    
    }
}

上例顯示的是,您在處理 Activity 結(jié)果時(shí)應(yīng)該在 onActivityResult() 方法中使用的基本邏輯。第一個(gè)條件檢查請(qǐng)求是否成功(如果成功,則resultCode 將為RESULT_OK以及此結(jié)果響應(yīng)的請(qǐng)求是否已知 — 在此情況下,requestCode與隨 startActivityForResult() 發(fā)送的第二個(gè)參數(shù)匹配。 代碼通過查詢 Intent中返回的數(shù)據(jù)(data 參數(shù))從該處開始處理 Activity 結(jié)果。
??實(shí)際情況是,ContentResolver 對(duì)一個(gè)內(nèi)容提供程序執(zhí)行查詢,后者返回一個(gè) Cursor,讓查詢的數(shù)據(jù)能夠被讀取。如需了解詳細(xì)信息,請(qǐng)參閱內(nèi)容提供程序文檔。

如需了解有關(guān) Intent 用法的詳細(xì)信息,請(qǐng)參閱 Intent 和 Intent 過濾器文檔。

結(jié)束 Activity

您可以通過調(diào)用 Activity 的 finish() 方法來結(jié)束該 Activity。您還可以通過調(diào)用 finishActivity() 結(jié)束您之前啟動(dòng)的另一個(gè) Activity。

注:在大多數(shù)情況下,您不應(yīng)使用這些方法顯式結(jié)束 Activity。 正如下文有關(guān) Activity 生命周期的部分所述,Android 系統(tǒng)會(huì)為您管理 Activity 的生命周期,因此您無需完成自己的 Activity。 調(diào)用這些方法可能對(duì)預(yù)期的用戶體驗(yàn)產(chǎn)生不良影響,因此只應(yīng)在您確實(shí)不想讓用戶返回此 Activity 實(shí)例時(shí)使用。

管理 Activity 生命周期

通過實(shí)現(xiàn)回調(diào)方法管理 Activity 的生命周期對(duì)開發(fā)強(qiáng)大而又靈活的應(yīng)用至關(guān)重要。 Activity 的生命周期會(huì)直接受到 Activity 與其他 Activity、其任務(wù)及返回棧的關(guān)聯(lián)性的影響。

Activity 基本上以三種狀態(tài)存在:

  • Resumed

此 Activity 位于屏幕前臺(tái)并具有用戶焦點(diǎn)。(有時(shí)也將此狀態(tài)稱作“運(yùn)行中”。)

  • Paused

另一個(gè) Activity 位于屏幕前臺(tái)并具有用戶焦點(diǎn),但此 Activity 仍可見。也就是說,另一個(gè) Activity 顯示在此 Activity 上方,并且該 Activity 部分透明或未覆蓋整個(gè)屏幕。 已暫停的 Activity 處于完全 Activity 狀態(tài)(Activity 對(duì)象保留在內(nèi)存中,它保留了所有狀態(tài)和成員信息,并與窗口管理器保持連接),但在內(nèi)存極度不足的情況下,可能會(huì)被系統(tǒng)終止。

  • Stopped

該 Activity 被另一個(gè) Activity 完全遮蓋(該 Activity 目前位于“后臺(tái)”)。 已停止的 Activity 同樣仍處于 Activity 狀態(tài)(Activity 對(duì)象保留在內(nèi)存中,它保留了所有狀態(tài)和成員信息,但未與窗口管理器連接)。 不過,它對(duì)用戶不再可見,在他處需要內(nèi)存時(shí)可能會(huì)被系統(tǒng)終止。

如果 Activity 處于暫停或停止?fàn)顟B(tài),系統(tǒng)可通過要求其結(jié)束(調(diào)用其 finish() 方法)或直接終止其進(jìn)程,將其從內(nèi)存中刪除。(將其結(jié)束或終止后)再次打開 Activity 時(shí),必須重建。

實(shí)現(xiàn)生命周期回調(diào)

當(dāng)一個(gè) Activity 轉(zhuǎn)入和轉(zhuǎn)出上述不同狀態(tài)時(shí),系統(tǒng)會(huì)通過各種回調(diào)方法向其發(fā)出通知。 所有回調(diào)方法都是掛鉤,您可以在 Activity 狀態(tài)發(fā)生變化時(shí)替代這些掛鉤來執(zhí)行相應(yīng)操作。 以下框架 Activity 包括每一個(gè)基本生命周期方法:

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 的整個(gè)生命周期。您可以通過實(shí)現(xiàn)這些方法監(jiān)控 Activity 生命周期中的三個(gè)嵌套循環(huán):

  • Activity 的整個(gè)生命周期發(fā)生在 onCreate() 調(diào)用與 onDestroy() 調(diào)用之間。您的 Activity 應(yīng)在onCreate() 中執(zhí)行“全局”狀態(tài)設(shè)置(例如定義布局),并釋放 onDestroy() 中的所有其余資源。例如,如果您的 Activity 有一個(gè)在后臺(tái)運(yùn)行的線程,用于從網(wǎng)絡(luò)上下載數(shù)據(jù),它可能會(huì)在 onCreate() 中創(chuàng)建該線程,然后在 onDestroy() 中停止該線程。
  • Activity 的可見生命周期發(fā)生在 onStart() 調(diào)用與 onStop() 調(diào)用之間。在這段時(shí)間,用戶可以在屏幕上看到 Activity 并與其交互。 例如,當(dāng)一個(gè)新 Activity 啟動(dòng),并且此 Activity 不再可見時(shí),系統(tǒng)會(huì)調(diào)用 onStop()。您可以在調(diào)用這兩個(gè)方法之間保留向用戶顯示 Activity 所需的資源。 例如,您可以在 onStart() 中注冊(cè)一個(gè) BroadcastReceiver 以監(jiān)控影響 UI 的變化,并在用戶無法再看到您顯示的內(nèi)容時(shí)在 onStop() 中將其取消注冊(cè)。在 Activity 的整個(gè)生命周期,當(dāng) Activity 在對(duì)用戶可見和隱藏兩種狀態(tài)中交替變化時(shí),系統(tǒng)可能會(huì)多次調(diào)用 onStart()onStop()
  • Activity 的前臺(tái)生命周期發(fā)生在 onResume() 調(diào)用與 onPause() 調(diào)用之間。在這段時(shí)間,Activity 位于屏幕上的所有其他 Activity 之前,并具有用戶輸入焦點(diǎn)。 Activity 可頻繁轉(zhuǎn)入和轉(zhuǎn)出前臺(tái)—例如,當(dāng)設(shè)備轉(zhuǎn)入休眠狀態(tài)或出現(xiàn)對(duì)話框時(shí),系統(tǒng)會(huì)調(diào)用 onPause()。 由于此狀態(tài)可能經(jīng)常發(fā)生轉(zhuǎn)變,因此這兩個(gè)方法中應(yīng)采用適度輕量級(jí)的代碼,以避免因轉(zhuǎn)變速度慢而讓用戶等待。

圖 1 說明了這些循環(huán)以及 Activity 在狀態(tài)轉(zhuǎn)變期間可能經(jīng)過的路徑。矩形表示回調(diào)方法,當(dāng) Activity 在不同狀態(tài)之間轉(zhuǎn)變時(shí),您可以實(shí)現(xiàn)這些方法來執(zhí)行操作。

圖1. Activity生命周期

表 1 列出了相同的生命周期回調(diào)方法,其中對(duì)每一種回調(diào)方法做了更詳細(xì)的描述,并說明了每一種方法在 Activity 整個(gè)生命周期內(nèi)的位置,包括在回調(diào)方法完成后系統(tǒng)能否終止 Activity。

方法 描述 是否能事后終止 后接
onCreate 首次創(chuàng)建 Activity 時(shí)調(diào)用。 您應(yīng)該在此方法中執(zhí)行所有正常的靜態(tài)設(shè)置— 創(chuàng)建視圖、將數(shù)據(jù)綁定到列表等等。系統(tǒng)向此方法傳遞一個(gè) Bundle 對(duì)象,其中包含 Activity 的上一狀態(tài),不過前提是捕獲了該狀態(tài)(請(qǐng)參閱后文的保存 Activity 狀態(tài))。始終后接 onStart() onStart()
onRestart 在 Activity 已停止并即將再次啟動(dòng)前調(diào)用。始終后接 onStart() onStart()
onStart 在 Activity 即將對(duì)用戶可見之前調(diào)用。如果 Activity 轉(zhuǎn)入前臺(tái),則后接 onResume(),如果 Activity 轉(zhuǎn)入隱藏狀態(tài),則后接 onStop()。 onResume()onStop()
onResume 在 Activity 即將開始與用戶進(jìn)行交互之前調(diào)用。 此時(shí),Activity 處于 Activity 堆棧的頂層,并具有用戶輸入焦點(diǎn)。始終后接 onPause()。 onPause()
onPause 當(dāng)系統(tǒng)即將開始繼續(xù)另一個(gè) Activity 時(shí)調(diào)用。 此方法通常用于確認(rèn)對(duì)持久性數(shù)據(jù)的未保存更改、停止動(dòng)畫以及其他可能消耗 CPU 的內(nèi)容,諸如此類。 它應(yīng)該非常迅速地執(zhí)行所需操作,因?yàn)樗祷睾螅乱粋€(gè) Activity 才能繼續(xù)執(zhí)行。如果 Activity 返回前臺(tái),則后接 onResume(),如果 Activity 轉(zhuǎn)入對(duì)用戶不可見狀態(tài),則后接onStop()。 onResume()onStop()
onStop Activity 對(duì)用戶不再可見時(shí)調(diào)用。如果 Activity 被銷毀,或另一個(gè) Activity(一個(gè)現(xiàn)有 Activity 或新 Activity)繼續(xù)執(zhí)行并將其覆蓋,就可能發(fā)生這種情況。如果 Activity 恢復(fù)與用戶的交互,則后接onRestart(),如果 Activity 被銷毀,則后接onDestroy()。 onRestart()onDestroy()
onDestroy 在 Activity 被銷毀前調(diào)用。這是 Activity 將收到的最后調(diào)用。 當(dāng) Activity 結(jié)束(有人調(diào)用 Activity 上的 finish()),或系統(tǒng)為節(jié)省空間而暫時(shí)銷毀該 Activity 實(shí)例時(shí),可能會(huì)調(diào)用它。 您可以通過 isFinishing() 方法區(qū)分這兩種情形。

名為“是否能事后終止?”的列表示系統(tǒng)是否能在不執(zhí)行另一行 Activity 代碼的情況下,在方法返回后隨時(shí)終止承載 Activity 的進(jìn)程。 有三個(gè)方法帶有“是”標(biāo)記:(onPause()onStop()onDestroy())。由于 onPause() 是這三個(gè)方法中的第一個(gè),因此 Activity 創(chuàng)建后,onPause() 必定成為最后調(diào)用的方法,然后才能終止進(jìn)程——如果系統(tǒng)在緊急情況下必須恢復(fù)內(nèi)存,則可能不會(huì)調(diào)用 onStop()onDestroy()。因此,您應(yīng)該使用 onPause() 向存儲(chǔ)設(shè)備寫入至關(guān)重要的持久性數(shù)據(jù)(例如用戶編輯)。不過,您應(yīng)該對(duì) onPause() 調(diào)用期間必須保留的信息有所選擇,因?yàn)樵摲椒ㄖ械娜魏巫柚惯^程都會(huì)妨礙向下一個(gè) Activity 的轉(zhuǎn)變并拖慢用戶體驗(yàn)。
??在是否能在事后終止?列中標(biāo)記為“否”的方法可從系統(tǒng)調(diào)用它們的一刻起防止承載 Activity 的進(jìn)程被終止。 因此,在從 onPause() 返回的時(shí)間到 onResume()被調(diào)用的時(shí)間,系統(tǒng)可以終止 Activity。在 onPause() 被再次調(diào)用并返回前,將無法再次終止 Activity。

注:根據(jù)表 1 中的定義屬于技術(shù)上無法“終止”的 Activity 仍可能被系統(tǒng)終止——但這種情況只有在無任何其他資源的極端情況下才會(huì)發(fā)生。進(jìn)程和線程處理文檔對(duì)可能會(huì)終止 Activity 的情況做了更詳盡的闡述。

保存 Activity 狀態(tài)

管理 Activity 生命周期的引言部分簡(jiǎn)要提及,當(dāng) Activity 暫停或停止時(shí),Activity 的狀態(tài)會(huì)得到保留。 確實(shí)如此,因?yàn)楫?dāng) Activity 暫停或停止時(shí),Activity 對(duì)象仍保留在內(nèi)存中 — 有關(guān)其成員和當(dāng)前狀態(tài)的所有信息仍處于 Activity 狀態(tài)。 因此,用戶在 Activity 內(nèi)所做的任何更改都會(huì)得到保留,這樣一來,當(dāng) Activity 返回前臺(tái)(當(dāng)它“繼續(xù)”)時(shí),這些更改仍然存在。
??不過,當(dāng)系統(tǒng)為了恢復(fù)內(nèi)存而銷毀某項(xiàng) Activity 時(shí),Activity 對(duì)象也會(huì)被銷毀,因此系統(tǒng)在繼續(xù) Activity 時(shí)根本無法讓其狀態(tài)保持完好,而是必須在用戶返回Activity時(shí)重建 Activity 對(duì)象。但用戶并不知道系統(tǒng)銷毀 Activity 后又對(duì)其進(jìn)行了重建,因此他們很可能認(rèn)為 Activity 狀態(tài)毫無變化。 在這種情況下,您可以實(shí)現(xiàn)另一個(gè)回調(diào)方法對(duì)有關(guān) Activity 狀態(tài)的信息進(jìn)行保存,以確保有關(guān) Activity 狀態(tài)的重要信息得到保留:onSaveInstanceState()
??系統(tǒng)會(huì)先調(diào)用 onSaveInstanceState(),然后再使 Activity 變得易于銷毀。系統(tǒng)會(huì)向該方法傳遞一個(gè) Bundle,您可以在其中使用 putString()putInt() 等方法以 名稱-值(key-value)對(duì)形式保存有關(guān) Activity 狀態(tài)的信息。然后,如果系統(tǒng)終止您的應(yīng)用進(jìn)程,并且用戶返回您的 Activity,則系統(tǒng)會(huì)重建該 Activity,并將 Bundle 同時(shí)傳遞給 onCreate()onRestoreInstanceState()。您可以使用上述任一方法從 Bundle 提取您保存的狀態(tài)并恢復(fù)該 Activity 狀態(tài)。如果沒有狀態(tài)信息需要恢復(fù),則傳遞給您的 Bundle 是空值(如果是首次創(chuàng)建該 Activity,就會(huì)出現(xiàn)這種情況)。

**圖 2. **在兩種情況下,Activity 重獲用戶焦點(diǎn)時(shí)可保持狀態(tài)完好:系統(tǒng)在銷毀 Activity 后重建 Activity,Activity 必須恢復(fù)之前保存的狀態(tài);系統(tǒng)停止 Activity 后繼續(xù)執(zhí)行 Activity,并且 Activity 狀態(tài)保持完好。

注:無法保證系統(tǒng)會(huì)在銷毀您的 Activity 前調(diào)用 onSaveInstanceState(),因?yàn)榇嬖诓恍枰4鏍顟B(tài)的情況(例如用戶使用“返回” 按鈕離開您的 Activity 時(shí),因?yàn)橛脩舻男袨槭窃陲@式關(guān)閉 Activity)。 如果系統(tǒng)調(diào)用 onSaveInstanceState(),它會(huì)在調(diào)用 onStop() 之前,并且可能會(huì)在調(diào)用 onPause() 之前進(jìn)行調(diào)用。

不過,即使您什么都不做,也不實(shí)現(xiàn) onSaveInstanceState(),Activity 類的 onSaveInstanceState() 默認(rèn)實(shí)現(xiàn)也會(huì)恢復(fù)部分 Activity 狀態(tài)。具體地講,默認(rèn)實(shí)現(xiàn)會(huì)為布局中的每個(gè) View 調(diào)用相應(yīng)的 onSaveInstanceState() 方法,讓每個(gè)視圖都能提供有關(guān)自身的應(yīng)保存信息。Android 框架中幾乎每個(gè)Widget都會(huì)根據(jù)需要實(shí)現(xiàn)此方法,以便在重建 Activity 時(shí)自動(dòng)保存和恢復(fù)對(duì) UI 所做的任何可見更改。例如,EditText Widget保存用戶輸入的任何文本,CheckBox Widget保存復(fù)選框的選中或未選中狀態(tài)。您只需為想要保存其狀態(tài)的每個(gè)Widget提供一個(gè)唯一的 ID(通過 android:id 屬性)。如果Widget沒有 ID,則系統(tǒng)無法保存其狀態(tài)。

您還可以通過將 android:saveEnabled 屬性設(shè)置為 "false" 或通過調(diào)用 setSaveEnabled() 方法顯式阻止布局內(nèi)的視圖保存其狀態(tài)。您通常不應(yīng)將該屬性禁用,但如果您想以不同方式恢復(fù) Activity UI 的狀態(tài),就可能需要這樣做。

盡管 onSaveInstanceState() 的默認(rèn)實(shí)現(xiàn)會(huì)保存有關(guān)您的Activity UI 的有用信息,您可能仍需替代它以保存更多信息。例如,您可能需要保存在 Activity 生命周期內(nèi)發(fā)生了變化的成員值(它們可能與 UI 中恢復(fù)的值有關(guān)聯(lián),但默認(rèn)情況下系統(tǒng)不會(huì)恢復(fù)儲(chǔ)存這些 UI 值的成員)。

由于 onSaveInstanceState() 的默認(rèn)實(shí)現(xiàn)有助于保存 UI 的狀態(tài), 因此如果您為了保存更多狀態(tài)信息而重寫該方法,應(yīng)始終先調(diào)用 onSaveInstanceState() 的超類實(shí)現(xiàn),然后再執(zhí)行任何操作。同樣,如果您替代 onRestoreInstanceState() 方法,也應(yīng)調(diào)用它的超類實(shí)現(xiàn),以便默認(rèn)實(shí)現(xiàn)能夠恢復(fù)視圖狀態(tài)。

注:由于無法保證系統(tǒng)會(huì)調(diào)用 onSaveInstanceState(),因此您只應(yīng)利用它來記錄 Activity 的瞬態(tài)(UI 的狀態(tài))—切勿使用它來存儲(chǔ)持久性數(shù)據(jù),而應(yīng)使用 onPause() 在用戶離開 Activity 后存儲(chǔ)持久性數(shù)據(jù)(例如應(yīng)保存到數(shù)據(jù)庫(kù)的數(shù)據(jù))。

您只需旋轉(zhuǎn)設(shè)備,讓屏幕方向發(fā)生變化,就能有效地測(cè)試您的應(yīng)用的狀態(tài)恢復(fù)能力。 當(dāng)屏幕方向變化時(shí),系統(tǒng)會(huì)銷毀并重建 Activity,以便應(yīng)用可供新屏幕配置使用的備用資源。 單憑這一理由,您的 Activity 在重建時(shí)能否完全恢復(fù)其狀態(tài)就顯得非常重要,因?yàn)橛脩粼谑褂脩?yīng)用時(shí)經(jīng)常需要旋轉(zhuǎn)屏幕。

處理配置變更

有些設(shè)備配置可能會(huì)在運(yùn)行時(shí)發(fā)生變化(例如屏幕方向、鍵盤可用性及語(yǔ)言)。 發(fā)生此類變化時(shí),Android 會(huì)重建運(yùn)行中的 Activity(系統(tǒng)調(diào)用 onDestroy(),然后立即調(diào)用 onCreate())。此行為旨在通過利用您提供的備用資源(例如適用于不同屏幕方向和屏幕尺寸的不同布局)自動(dòng)重新加載您的應(yīng)用來幫助它適應(yīng)新配置。
??如果您對(duì) Activity 進(jìn)行了適當(dāng)設(shè)計(jì),讓它能夠按以上所述處理屏幕方向變化帶來的重啟并恢復(fù) Activity 狀態(tài),那么在遭遇 Activity 生命周期中的其他意外事件時(shí),您的應(yīng)用將具有更強(qiáng)的適應(yīng)性。
正如上文所述,處理此類重啟的最佳方法 是利用 onSaveInstanceState()onRestoreInstanceState()(或onCreate())保存并恢復(fù) Activity 的狀態(tài)。
??如需了解有關(guān)運(yùn)行時(shí)發(fā)生的配置變更以及應(yīng)對(duì)方法的詳細(xì)信息,請(qǐng)閱讀處理運(yùn)行時(shí)變更指南。

協(xié)調(diào) Activity

當(dāng)一個(gè) Activity 啟動(dòng)另一個(gè) Activity 時(shí),它們都會(huì)經(jīng)歷生命周期轉(zhuǎn)變。第一個(gè) Activity 暫停并停止(但如果它在后臺(tái)仍然可見,則不會(huì)停止)時(shí),系統(tǒng)會(huì)創(chuàng)建另一個(gè) Activity。 如果這些 Activity 共用保存到磁盤或其他地方的數(shù)據(jù),必須了解的是,在創(chuàng)建第二個(gè) Activity 前,第一個(gè) Activity 不會(huì)完全停止。更確切地說,啟動(dòng)第二個(gè) Activity 的過程與停止第一個(gè) Activity 的過程存在重疊。
??生命周期回調(diào)的順序經(jīng)過明確定義,當(dāng)兩個(gè) Activity 位于同一進(jìn)程,并且由一個(gè) Activity 啟動(dòng)另一個(gè) Activity 時(shí),其定義尤其明確。 以下是當(dāng) Activity A 啟動(dòng) Activity B 時(shí)一系列操作的發(fā)生順序:

  1. Activity A 的 onPause() 方法執(zhí)行。
  2. Activity B 的 onCreate()onStart()onResume() 方法依次執(zhí)行。(Activity B 現(xiàn)在具有用戶焦點(diǎn)。)
  3. 然后,如果 Activity A 在屏幕上不再可見,則其 onStop() 方法執(zhí)行。

您可以利用這種可預(yù)測(cè)的生命周期回調(diào)順序管理從一個(gè) Activity 到另一個(gè) Activity 的信息轉(zhuǎn)變。 例如,如果您必須在第一個(gè) Activity 停止時(shí)向數(shù)據(jù)庫(kù)寫入數(shù)據(jù),以便下一個(gè) Activity 能夠讀取該數(shù)據(jù),則應(yīng)在 onPause() 而不是 onStop() 執(zhí)行期間向數(shù)據(jù)庫(kù)寫入數(shù)據(jù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,250評(píng)論 6 530
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 97,923評(píng)論 3 413
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,041評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,475評(píng)論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,253評(píng)論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,801評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,882評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,023評(píng)論 0 285
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,530評(píng)論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,494評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,639評(píng)論 1 366
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,177評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,890評(píng)論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,289評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,552評(píng)論 1 281
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,242評(píng)論 3 389
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,626評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容

  • Activity https://developer.android.com/guide/components/a...
    XLsn0w閱讀 710評(píng)論 0 4
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,574評(píng)論 25 707
  • 昨天晚上終于鼓起勇氣在朋友圈發(fā)了自己公眾號(hào)的三篇文章,這三篇文章有兩篇是前幾天寫的,有一篇是當(dāng)天寫的。每篇文章后面...
    甜小丫的芳草園閱讀 367評(píng)論 3 0
  • 太陽(yáng)升起來了 大陽(yáng)落下去了 你擁有的 不過是 這一天 這一刻 這一秒 寫詩(shī)很痛苦 但詩(shī)人并不痛苦 所以詩(shī)人活了很久...
    凡了閱讀 248評(píng)論 0 1
  • 今天是一年中最全新的一天,新的陽(yáng)光,新的浪潮,新的開始。 唯一舊著的,是我喜歡你的這顆心,它沒變,所以這份喜歡你的...
    原味少女閱讀 323評(píng)論 0 1