Activity
https://developer.android.com/guide/components/activities.html
本文內(nèi)容
關(guān)鍵類
另請參閱
Activity是一個(gè)應(yīng)用組件,用戶可與其提供的屏幕進(jìn)行交互,以執(zhí)行撥打電話、拍攝照片、發(fā)送電子郵件或查看地圖等操作。 每個(gè) Activity 都會獲得一個(gè)用于繪制其用戶界面的窗口。窗口通常會充滿屏幕,但也可小于屏幕并浮動在其他窗口之上。
一個(gè)應(yīng)用通常由多個(gè)彼此松散聯(lián)系的 Activity 組成。 一般會指定應(yīng)用中的某個(gè) Activity 為“主”Activity,即首次啟動應(yīng)用時(shí)呈現(xiàn)給用戶的那個(gè) Activity。 而且每個(gè) Activity 均可啟動另一個(gè) Activity,以便執(zhí)行不同的操作。 每次新 Activity 啟動時(shí),前一 Activity 便會停止,但系統(tǒng)會在堆棧(“返回?!保┲斜A粼?Activity。 當(dāng)新 Activity 啟動時(shí),系統(tǒng)會將其推送到返回棧上,并取得用戶焦點(diǎn)。 返回棧遵循基本的“后進(jìn)先出”堆棧機(jī)制,因此,當(dāng)用戶完成當(dāng)前 Activity 并按“返回”按鈕時(shí),系統(tǒng)會從堆棧中將其彈出(并銷毀),然后恢復(fù)前一 Activity。(任務(wù)和返回棧文檔中對返回棧有更詳細(xì)的闡述。)
當(dāng)一個(gè) Activity 因某個(gè)新 Activity 啟動而停止時(shí),系統(tǒng)會通過該 Activity 的生命周期回調(diào)方法通知其這一狀態(tài)變化。Activity 因狀態(tài)變化—系統(tǒng)是創(chuàng)建 Activity、停止 Activity、恢復(fù) Activity 還是銷毀 Activity— 而收到的回調(diào)方法可能有若干種,每一種回調(diào)都會為您提供執(zhí)行與該狀態(tài)變化相應(yīng)的特定操作的機(jī)會。 例如,停止時(shí),您的 Activity 應(yīng)釋放任何大型對象,例如網(wǎng)絡(luò)或數(shù)據(jù)庫連接。 當(dāng) Activity 恢復(fù)時(shí),您可以重新獲取所需資源,并恢復(fù)執(zhí)行中斷的操作。 這些狀態(tài)轉(zhuǎn)變都是 Activity 生命周期的一部分。
本文的其余部分闡述有關(guān)如何創(chuàng)建和使用 Activity 的基礎(chǔ)知識(包括對 Activity 生命周期工作方式的全面闡述),以便您正確管理各種 Activity 狀態(tài)之間的轉(zhuǎn)變。
創(chuàng)建 Activity
要創(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)方法是:
您必須實(shí)現(xiàn)此方法。系統(tǒng)會在創(chuàng)建您的 Activity 時(shí)調(diào)用此方法。您應(yīng)該在實(shí)現(xiàn)內(nèi)初始化 Activity 的必需組件。 最重要的是,您必須在此方法內(nèi)調(diào)用setContentView(),以定義 Activity 用戶界面的布局。
系統(tǒng)將此方法作為用戶離開 Activity 的第一個(gè)信號(但并不總是意味著 Activity 會被銷毀)進(jìn)行調(diào)用。 您通常應(yīng)該在此方法內(nèi)確認(rèn)在當(dāng)前用戶會話結(jié)束后仍然有效的任何更改(因?yàn)橛脩艨赡懿粫祷兀?/p>
您還應(yīng)使用幾種其他生命周期回調(diào)方法,以便提供流暢的 Activity 間用戶體驗(yàn),以及處理導(dǎo)致您的 Activity 停止甚至被銷毀的意外中斷。 后文的管理 Activity 生命周期部分對所有生命周期回調(diào)方法進(jìn)行了闡述。
實(shí)現(xiàn)用戶界面
Activity 的用戶界面是由層級式視圖 — 衍生自View類的對象 — 提供的。每個(gè)視圖都控制 Activity 窗口內(nèi)的特定矩形空間,可對用戶交互作出響應(yīng)。 例如,視圖可以是在用戶觸摸時(shí)啟動某項(xiàng)操作的按鈕。
您可以利用 Android 提供的許多現(xiàn)成視圖設(shè)計(jì)和組織您的布局?!靶〔考笔翘峁┌粹o、文本字段、復(fù)選框或僅僅是一幅圖像等屏幕視覺(交互式)元素的視圖。 “布局”是衍生自ViewGroup的視圖,為其子視圖提供唯一布局模型,例如線性布局、網(wǎng)格布局或相對布局。 您還可以為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)建用戶界面的信息,請參閱用戶界面文檔。
在清單文件中聲明 Activity
您必須在清單文件中聲明您的 Activity,這樣系統(tǒng)才能訪問它。 要聲明您的 Activity,請打開您的清單文件,并將元素添加為元素的子項(xiàng)。例如:
...
...
您還可以在此元素中加入幾個(gè)其他特性,以定義 Activity 標(biāo)簽、Activity 圖標(biāo)或風(fēng)格主題等用于設(shè)置 Activity UI 風(fēng)格的屬性。android:name屬性是唯一必需的屬性—它指定 Activity 的類名。應(yīng)用一旦發(fā)布,即不應(yīng)更改此類名,否則,可能會破壞諸如應(yīng)用快捷方式等一些功能(請閱讀博客文章Things That Cannot Change[不能更改的內(nèi)容])。
請參閱元素參考文檔,了解有關(guān)在清單文件中聲明 Activity 的詳細(xì)信息。
使用 Intent 過濾器
元素還可指定各種 Intent 過濾器—使用元素—以聲明其他應(yīng)用組件激活它的方法。
當(dāng)您使用 Android SDK 工具創(chuàng)建新應(yīng)用時(shí),系統(tǒng)自動為您創(chuàng)建的存根 Activity 包含一個(gè) Intent 過濾器,其中聲明了該 Activity 響應(yīng)“主”操作且應(yīng)置于“l(fā)auncher”類別內(nèi)。 Intent 過濾器的內(nèi)容如下所示:
元素指定這是應(yīng)用的“主”入口點(diǎn)。元素指定此 Activity 應(yīng)列入系統(tǒng)的應(yīng)用啟動器內(nèi)(以便用戶啟動該 Activity)。
如果您打算讓應(yīng)用成為獨(dú)立應(yīng)用,不允許其他應(yīng)用激活其 Activity,則您不需要任何其他 Intent 過濾器。 正如前例所示,只應(yīng)有一個(gè) Activity 具有“主”操作和“l(fā)auncher”類別。 您不想提供給其他應(yīng)用的 Activity 不應(yīng)有任何 Intent 過濾器,您可以利用顯式 Intent 自行啟動它們(下文對此做了闡述)。
不過,如果您想讓 Activity 對衍生自其他應(yīng)用(以及您的自有應(yīng)用)的隱式 Intent 作出響應(yīng),則必須為 Activity 定義其他 Intent 過濾器。 對于您想要作出響應(yīng)的每一個(gè) Intent 類型,您都必須加入相應(yīng)的,其中包括一個(gè)元素,還可選擇性地包括一個(gè)元素和/或一個(gè)元素。這些元素指定您的 Activity 可以響應(yīng)的 Intent 類型。
如需了解有關(guān)您的 Activity 如何響應(yīng) Intent 的詳細(xì)信息,請參閱Intent 和 Intent 過濾器文檔。
啟動 Activity
您可以通過調(diào)用startActivity(),并將其傳遞給描述您想啟動的 Activity 的Intent來啟動另一個(gè) Activity。Intent 對象會指定您想啟動的具體 Activity 或描述您想執(zhí)行的操作類型(系統(tǒng)會為您選擇合適的 Activity,甚至是來自其他應(yīng)用的 Activity)。 Intent 對象還可能攜帶少量供所啟動 Activity 使用的數(shù)據(jù)。
在您的自有應(yīng)用內(nèi)工作時(shí),您經(jīng)常只需要啟動某個(gè)已知 Activity。 您可以通過使用類名創(chuàng)建一個(gè)顯式定義您想啟動的 Activity 的 Intent 對象來實(shí)現(xiàn)此目的。 例如,可以通過以下代碼讓一個(gè) Activity 啟動另一個(gè)名為SignInActivity的 Activity:
Intentintent=newIntent(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 對象的真正價(jià)值所在 — 您可以創(chuàng)建一個(gè) Intent 對象,對您想執(zhí)行的操作進(jìn)行描述,系統(tǒng)會從其他應(yīng)用啟動相應(yīng)的 Activity。 如果有多個(gè) Activity 可以處理 Intent,則用戶可以選擇要使用哪一個(gè)。 例如,如果您想允許用戶發(fā)送電子郵件,可以創(chuàng)建以下 Intent:
Intentintent=newIntent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL,recipientArray);
startActivity(intent);
添加到 Intent 中的EXTRA_EMAILextra 是一個(gè)字符串?dāng)?shù)組,其中包含應(yīng)將電子郵件發(fā)送到的電子郵件地址。 當(dāng)電子郵件應(yīng)用響應(yīng)此 Intent 時(shí),它會讀取 extra 中提供的字符串?dāng)?shù)組,并將它們放入電子郵件撰寫窗體的“收件人”字段。 在這種情況下,電子郵件應(yīng)用的 Activity 啟動,并且當(dāng)用戶完成操作時(shí),您的 Activity 會恢復(fù)執(zhí)行。
啟動 Activity 以獲得結(jié)果
有時(shí),您可能需要從啟動的 Activity 獲得結(jié)果。在這種情況下,請通過調(diào)用startActivityForResult()(而非startActivity())來啟動 Activity。 要想在隨后收到后續(xù) Activity 的結(jié)果,請實(shí)現(xiàn)onActivityResult()回調(diào)方法。 當(dāng)后續(xù) Activity 完成時(shí),它會使用Intent向您的onActivityResult()方法返回結(jié)果。
例如,您可能希望用戶選取其中一位聯(lián)系人,以便您的 Activity 對該聯(lián)系人中的信息執(zhí)行某項(xiàng)操作。 您可以通過以下代碼創(chuàng)建此類 Intent 并處理結(jié)果:
privatevoidpickContact(){
// Create an intent to "pick" a contact, as defined by the content provider URI
Intentintent=newIntent(Intent.ACTION_PICK,Contacts.CONTENT_URI);
startActivityForResult(intent,PICK_CONTACT_REQUEST);
}
@Override
protectedvoidonActivityResult(intrequestCode,intresultCode,Intentdata){
// 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
Cursorcursor=getContentResolver().query(data.getData(),
newString[]{Contacts.DISPLAY_NAME},null,null,null);
if(cursor.moveToFirst()){// True if the cursor is not empty
intcolumnIndex=cursor.getColumnIndex(Contacts.DISPLAY_NAME);
Stringname=cursor.getString(columnIndex);
// Do something with the selected contact's name...
}
}
}
上例顯示的是,您在處理 Activity 結(jié)果時(shí)應(yīng)該在onActivityResult()方法中使用的基本邏輯。 第一個(gè)條件檢查請求是否成功(如果成功,則resultCode將為RESULT_OK)以及此結(jié)果響應(yīng)的請求是否已知 — 在此情況下,requestCode與隨startActivityForResult()發(fā)送的第二個(gè)參數(shù)匹配。 代碼通過查詢Intent中返回的數(shù)據(jù)(data參數(shù))從該處開始處理 Activity 結(jié)果。
實(shí)際情況是,ContentResolver對一個(gè)內(nèi)容提供程序執(zhí)行查詢,后者返回一個(gè)Cursor,讓查詢的數(shù)據(jù)能夠被讀取。如需了解詳細(xì)信息,請參閱內(nèi)容提供程序文檔。
如需了解有關(guān) Intent 用法的詳細(xì)信息,請參閱Intent 和 Intent 過濾器文檔。
結(jié)束 Activity
您可以通過調(diào)用 Activity 的finish()方法來結(jié)束該 Activity。您還可以通過調(diào)用finishActivity()結(jié)束您之前啟動的另一個(gè) Activity。
注:在大多數(shù)情況下,您不應(yīng)使用這些方法顯式結(jié)束 Activity。 正如下文有關(guān) Activity 生命周期的部分所述,Android 系統(tǒng)會為您管理 Activity 的生命周期,因此您無需結(jié)束自己的 Activity。 調(diào)用這些方法可能對預(yù)期的用戶體驗(yàn)產(chǎn)生不良影響,因此只應(yīng)在您確實(shí)不想讓用戶返回此 Activity 實(shí)例時(shí)使用。
管理 Activity 生命周期
通過實(shí)現(xiàn)回調(diào)方法管理 Activity 的生命周期對開發(fā)強(qiáng)大而又靈活的應(yīng)用至關(guān)重要。 Activity 的生命周期會直接受到 Activity 與其他 Activity、其任務(wù)及返回棧的關(guān)聯(lián)性的影響。
Activity 基本上以三種狀態(tài)存在:
繼續(xù)
此 Activity 位于屏幕前臺并具有用戶焦點(diǎn)。(有時(shí)也將此狀態(tài)稱作“運(yùn)行中”。)
暫停
另一個(gè) Activity 位于屏幕前臺并具有用戶焦點(diǎn),但此 Activity 仍可見。也就是說,另一個(gè) Activity 顯示在此 Activity 上方,并且該 Activity 部分透明或未覆蓋整個(gè)屏幕。 暫停的 Activity 處于完全活動狀態(tài)(Activity對象保留在內(nèi)存中,它保留了所有狀態(tài)和成員信息,并與窗口管理器保持連接),但在內(nèi)存極度不足的情況下,可能會被系統(tǒng)終止。
停止
該 Activity 被另一個(gè) Activity 完全遮蓋(該 Activity 目前位于“后臺”)。 已停止的 Activity 同樣仍處于活動狀態(tài)(Activity對象保留在內(nèi)存中,它保留了所有狀態(tài)和成員信息,但未與窗口管理器連接)。 不過,它對用戶不再可見,在他處需要內(nèi)存時(shí)可能會被系統(tǒng)終止。
如果 Activity 處于暫?;蛲V?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)會通過各種回調(diào)方法向其發(fā)出通知。 所有回調(diào)方法都是掛鉤,您可以在 Activity 狀態(tài)發(fā)生變化時(shí)替代這些掛鉤來執(zhí)行相應(yīng)操作。 以下框架 Activity 包括每一個(gè)基本生命周期方法:
publicclassExampleActivityextendsActivity{
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
// The activity is being created.
}
@Override
protectedvoidonStart(){
super.onStart();
// The activity is about to become visible.
}
@Override
protectedvoidonResume(){
super.onResume();
// The activity has become visible (it is now "resumed").
}
@Override
protectedvoidonPause(){
super.onPause();
// Another activity is taking focus (this activity is about to be "paused").
}
@Override
protectedvoidonStop(){
super.onStop();
// The activity is no longer visible (it is now "stopped")
}
@Override
protectedvoidonDestroy(){
super.onDestroy();
// The activity is about to be destroyed.
}
}
注:正如以上示例所示,您在實(shí)現(xiàn)這些生命周期方法時(shí)必須始終先調(diào)用超類實(shí)現(xiàn),然后再執(zhí)行任何操作。
這些方法共同定義 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è)在后臺運(yùn)行的線程,用于從網(wǎng)絡(luò)上下載數(shù)據(jù),它可能會在onCreate()中創(chuàng)建該線程,然后在onDestroy()中停止該線程。
Activity 的可見生命周期發(fā)生在onStart()調(diào)用與onStop()調(diào)用之間。在這段時(shí)間,用戶可以在屏幕上看到 Activity 并與其交互。 例如,當(dāng)一個(gè)新 Activity 啟動,并且此 Activity 不再可見時(shí),系統(tǒng)會調(diào)用onStop()。您可以在調(diào)用這兩個(gè)方法之間保留向用戶顯示 Activity 所需的資源。 例如,您可以在onStart()中注冊一個(gè)BroadcastReceiver以監(jiān)控影響 UI 的變化,并在用戶無法再看到您顯示的內(nèi)容時(shí)在onStop()中將其取消注冊。在 Activity 的整個(gè)生命周期,當(dāng) Activity 在對用戶可見和隱藏兩種狀態(tài)中交替變化時(shí),系統(tǒng)可能會多次調(diào)用onStart()和onStop()。
Activity 的前臺生命周期發(fā)生在onResume()調(diào)用與onPause()調(diào)用之間。在這段時(shí)間,Activity 位于屏幕上的所有其他 Activity 之前,并具有用戶輸入焦點(diǎn)。 Activity 可頻繁轉(zhuǎn)入和轉(zhuǎn)出前臺 — 例如,當(dāng)設(shè)備轉(zhuǎn)入休眠狀態(tài)或出現(xiàn)對話框時(shí),系統(tǒng)會調(diào)用onPause()。 由于此狀態(tài)可能經(jīng)常發(fā)生轉(zhuǎn)變,因此這兩個(gè)方法中應(yīng)采用適度輕量級的代碼,以避免因轉(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)方法,其中對每一種回調(diào)方法做了更詳細(xì)的描述,并說明了每一種方法在 Activity 整個(gè)生命周期內(nèi)的位置,包括在回調(diào)方法完成后系統(tǒng)能否終止 Activity。
表 1.Activity 生命周期回調(diào)方法匯總表。
方法說明是否能事后終止?后接
onCreate()首次創(chuàng)建 Activity 時(shí)調(diào)用。 您應(yīng)該在此方法中執(zhí)行所有正常的靜態(tài)設(shè)置 — 創(chuàng)建視圖、將數(shù)據(jù)綁定到列表等等。 系統(tǒng)向此方法傳遞一個(gè) Bundle 對象,其中包含 Activity 的上一狀態(tài),不過前提是捕獲了該狀態(tài)(請參閱后文的保存 Activity 狀態(tài))。
始終后接onStart()。否onStart()
onRestart()在 Activity 已停止并即將再次啟動前調(diào)用。
始終后接onStart()否onStart()
onStart()在 Activity 即將對用戶可見之前調(diào)用。
如果 Activity 轉(zhuǎn)入前臺,則后接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)對持久性數(shù)據(jù)的未保存更改、停止動畫以及其他可能消耗 CPU 的內(nèi)容,諸如此類。 它應(yīng)該非常迅速地執(zhí)行所需操作,因?yàn)樗祷睾?,下一個(gè) Activity 才能繼續(xù)執(zhí)行。
如果 Activity 返回前臺,則后接onResume(),如果 Activity 轉(zhuǎn)入對用戶不可見狀態(tài),則后接onStop()。是onResume()
或
onStop()
onStop()在 Activity 對用戶不再可見時(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é)束(有人對 Activity 調(diào)用了finish()),或系統(tǒng)為節(jié)省空間而暫時(shí)銷毀該 Activity 實(shí)例時(shí),可能會調(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)存,則可能不會調(diào)用onStop()和onDestroy()。因此,您應(yīng)該使用onPause()向存儲設(shè)備寫入至關(guān)重要的持久性數(shù)據(jù)(例如用戶編輯)。不過,您應(yīng)該對onPause()調(diào)用期間必須保留的信息有所選擇,因?yàn)樵摲椒ㄖ械娜魏巫柚惯^程都會妨礙向下一個(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)終止 — 但這種情況只有在無任何其他資源的極端情況下才會發(fā)生。進(jìn)程和線程處理文檔對可能會終止 Activity 的情況做了更詳盡的闡述。
保存 Activity 狀態(tài)
管理 Activity 生命周期的引言部分簡要提及,當(dāng) Activity 暫?;蛲V箷r(shí),Activity 的狀態(tài)會得到保留。 確實(shí)如此,因?yàn)楫?dāng) Activity 暫?;蛲V箷r(shí),Activity對象仍保留在內(nèi)存中 — 有關(guān)其成員和當(dāng)前狀態(tài)的所有信息仍處于活動狀態(tài)。 因此,用戶在 Activity 內(nèi)所做的任何更改都會得到保留,這樣一來,當(dāng) Activity 返回前臺(當(dāng)它“繼續(xù)”)時(shí),這些更改仍然存在。
不過,當(dāng)系統(tǒng)為了恢復(fù)內(nèi)存而銷毀某項(xiàng) Activity 時(shí),Activity對象也會被銷毀,因此系統(tǒng)在繼續(xù) Activity 時(shí)根本無法讓其狀態(tài)保持完好,而是必須在用戶返回 Activity 時(shí)重建Activity對象。但用戶并不知道系統(tǒng)銷毀 Activity 后又對其進(jìn)行了重建,因此他們很可能認(rèn)為 Activity 狀態(tài)毫無變化。 在這種情況下,您可以實(shí)現(xiàn)另一個(gè)回調(diào)方法對有關(guān) Activity 狀態(tài)的信息進(jìn)行保存,以確保有關(guān) Activity 狀態(tài)的重要信息得到保留:onSaveInstanceState()。
系統(tǒng)會先調(diào)用onSaveInstanceState(),然后再使 Activity 變得易于銷毀。系統(tǒng)會向該方法傳遞一個(gè)Bundle,您可以在其中使用putString()和putInt()等方法以名稱-值對形式保存有關(guān) Activity 狀態(tài)的信息。然后,如果系統(tǒng)終止您的應(yīng)用進(jìn)程,并且用戶返回您的 Activity,則系統(tǒng)會重建該 Activity,并將Bundle同時(shí)傳遞給onCreate()和onRestoreInstanceState()。您可以使用上述任一方法從Bundle提取您保存的狀態(tài)并恢復(fù)該 Activity 狀態(tài)。如果沒有狀態(tài)信息需要恢復(fù),則傳遞給您的Bundle是空值(如果是首次創(chuàng)建該 Activity,就會出現(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)會在銷毀您的 Activity 前調(diào)用onSaveInstanceState(),因?yàn)榇嬖诓恍枰4鏍顟B(tài)的情況(例如用戶使用“返回”按鈕離開您的 Activity 時(shí),因?yàn)橛脩舻男袨槭窃陲@式關(guān)閉 Activity)。如果系統(tǒng)調(diào)用onSaveInstanceState(),它會在調(diào)用onStop()之前,并且可能會在調(diào)用onPause()之前進(jìn)行調(diào)用。
不過,即使您什么都不做,也不實(shí)現(xiàn)onSaveInstanceState(),Activity類的onSaveInstanceState()默認(rèn)實(shí)現(xiàn)也會恢復(fù)部分 Activity 狀態(tài)。具體地講,默認(rèn)實(shí)現(xiàn)會為布局中的每個(gè)View調(diào)用相應(yīng)的onSaveInstanceState()方法,讓每個(gè)視圖都能提供有關(guān)自身的應(yīng)保存信息。Android 框架中幾乎每個(gè)小部件都會根據(jù)需要實(shí)現(xiàn)此方法,以便在重建 Activity 時(shí)自動保存和恢復(fù)對 UI 所做的任何可見更改。例如,EditText小部件保存用戶輸入的任何文本,CheckBox小部件保存復(fù)選框的選中或未選中狀態(tài)。您只需為想要保存其狀態(tài)的每個(gè)小部件提供一個(gè)唯一的 ID(通過android:id屬性)。如果小部件沒有 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)會保存有關(guān)您的Activity UI 的有用信息,您可能仍需替換它以保存更多信息。例如,您可能需要保存在 Activity 生命周期內(nèi)發(fā)生了變化的成員值(它們可能與 UI 中恢復(fù)的值有關(guān)聯(lián),但默認(rèn)情況下系統(tǒng)不會恢復(fù)儲存這些 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)會調(diào)用onSaveInstanceState(),因此您只應(yīng)利用它來記錄 Activity 的瞬態(tài)(UI 的狀態(tài))— 切勿使用它來存儲持久性數(shù)據(jù),而應(yīng)使用onPause()在用戶離開 Activity 后存儲持久性數(shù)據(jù)(例如應(yīng)保存到數(shù)據(jù)庫的數(shù)據(jù))。
您只需旋轉(zhuǎn)設(shè)備,讓屏幕方向發(fā)生變化,就能有效地測試您的應(yīng)用的狀態(tài)恢復(fù)能力。 當(dāng)屏幕方向變化時(shí),系統(tǒng)會銷毀并重建 Activity,以便應(yīng)用可供新屏幕配置使用的備用資源。 單憑這一理由,您的 Activity 在重建時(shí)能否完全恢復(fù)其狀態(tài)就顯得非常重要,因?yàn)橛脩粼谑褂脩?yīng)用時(shí)經(jīng)常需要旋轉(zhuǎn)屏幕。
處理配置變更
有些設(shè)備配置可能會在運(yùn)行時(shí)發(fā)生變化(例如屏幕方向、鍵盤可用性及語言)。 發(fā)生此類變化時(shí),Android 會重建運(yùn)行中的 Activity(系統(tǒng)調(diào)用onDestroy(),然后立即調(diào)用onCreate())。此行為旨在通過利用您提供的備用資源(例如適用于不同屏幕方向和屏幕尺寸的不同布局)自動重新加載您的應(yīng)用來幫助它適應(yīng)新配置。
如果您對 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)對方法的詳細(xì)信息,請閱讀處理運(yùn)行時(shí)變更指南。
協(xié)調(diào) Activity
當(dāng)一個(gè) Activity 啟動另一個(gè) Activity 時(shí),它們都會體驗(yàn)到生命周期轉(zhuǎn)變。第一個(gè) Activity 暫停并停止(但如果它在后臺仍然可見,則不會停止)時(shí),同時(shí)系統(tǒng)會創(chuàng)建另一個(gè) Activity。 如果這些 Activity 共用保存到磁盤或其他地方的數(shù)據(jù),必須了解的是,在創(chuàng)建第二個(gè) Activity 前,第一個(gè) Activity 不會完全停止。更確切地說,啟動第二個(gè) Activity 的過程與停止第一個(gè) Activity 的過程存在重疊。
生命周期回調(diào)的順序經(jīng)過明確定義,當(dāng)兩個(gè) Activity 位于同一進(jìn)程,并且由一個(gè) Activity 啟動另一個(gè) Activity 時(shí),其定義尤其明確。 以下是當(dāng) Activity A 啟動 Activity B 時(shí)一系列操作的發(fā)生順序:
Activity A 的onPause()方法執(zhí)行。
Activity B 的onCreate()、onStart()和onResume()方法依次執(zhí)行。(Activity B 現(xiàn)在具有用戶焦點(diǎn)。)
然后,如果 Activity A 在屏幕上不再可見,則其onStop()方法執(zhí)行。
您可以利用這種可預(yù)測的生命周期回調(diào)順序管理從一個(gè) Activity 到另一個(gè) Activity 的信息轉(zhuǎn)變。 例如,如果您必須在第一個(gè) Activity 停止時(shí)向數(shù)據(jù)庫寫入數(shù)據(jù),以便下一個(gè) Activity 能夠讀取該數(shù)據(jù),則應(yīng)在onPause()而不是onStop()執(zhí)行期間向數(shù)據(jù)庫寫入數(shù)據(jù)。