一、Activity創(chuàng)建、啟動和關閉
1.創(chuàng)建 Activity
2.在清單文件中聲明
3.啟動
4.結(jié)束
二、Activity生命周期
三、Activity處理配置變更等問題
Activity創(chuàng)建、啟動和關閉
Activity是一個應用組件,用戶可與其提供的屏幕進行交互,以執(zhí)行撥打電話、拍攝照片、發(fā)送電子郵件或查看地圖等操作。 每個Activity都會獲得一個用于繪制其用戶界面的窗口。窗口通常會充滿屏幕,但也可小于屏幕并浮動在其他窗口之上。
1.創(chuàng)建Activity
要創(chuàng)建Activity,必須先創(chuàng)建Activity的子類(或使用其現(xiàn)有子類)。需要在子類中實現(xiàn)Activity在其生命周期的各種狀態(tài)之間轉(zhuǎn)變時(例如創(chuàng)建Activity、停止Activity、恢復Activity或銷毀Activity時)系統(tǒng)調(diào)用的回調(diào)方法。 兩個最重要的回調(diào)方法是:
- onCreate(),必須實現(xiàn)此方法,系統(tǒng)會在創(chuàng)建Activity時調(diào)用此方法。在實現(xiàn)該方法時,應該初始化Activity的必需組件。 最重要的是,必須在此方法內(nèi)調(diào)用setContentView(),以定義Activity用戶界面的布局。
- onPause(),系統(tǒng)將此方法作為用戶離開Activity的第一個信號(但并不總是意味著 Activity 會被銷毀)進行調(diào)用。 在此方法內(nèi),通常應該確認當前用戶會話結(jié)束后仍然有效的任何更改(因為用戶可能不會返回)。
2.在清單文件中聲明Activity
(1)在清單文件中聲明了Activity,系統(tǒng)才能訪問它。 要聲明Activity,則需打開清單文件,并將 <activity/> 元素添加為<application/> 元素的子項。例如:
<manifest ... >
<application ... >
<activity android:name=".ExampleActivity" />
...
</application ... >
...
</manifest >
您還可以在此元素中加入幾個其他特性,以定義Activity標簽、Activity圖標或風格主題等用于設置Activity UI風格的屬性。android:name特性是唯一的必需特性—它指定。
(2)使用 Intent 過濾器
元素 <activity/> 還可指定各種Intent過濾器—使用<Intent-filter/>元素—以聲明其他應用組件激活它的方法。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>元素指定這是應用的“主”入口點。<category/>元素指定此 Activity應列入系統(tǒng)的應用啟動器內(nèi)(以便用戶啟動該 Activity)。
如果打算讓應用成為獨立應用,不允許其他應用激活其Activity,則不需要任何其他Intent過濾器。 正如前例所示,只應有一個Activity具有“main”和“l(fā)auncher”
類別。 如果不想提供給其他應用的Activity不應有任何Intent過濾器,可以利用顯式 Intent 自行啟動它們。不過,如果想讓 Activity對衍生自其他應用(以及您的自有應用)的隱式Intent作出響應,則必須為Activity定義其他 Intent 過濾器。 對于您想要作出響應的每一個 Intent類型,都必須加入相應的<Intent-filter/>,其中包括一個<action/>元素,還可選擇性地包括一個<category/>元素和/或一個<data/>元素。這些元素指定您的Activity可以響應的Intent類型。
3.啟動 Activity
(1)調(diào)用startActivity(),并將其傳給想啟動Activity的Intent來啟動另一個Activity
Intent對象會指定您想啟動的具體Activity或描述您想執(zhí)行的操作類型(系統(tǒng)會為您選擇合適的Activity,甚至是來自其他應用的Activity)。 Intent 對象還可能攜帶少量供所啟動Activity使用的數(shù)據(jù)。
例如,可以通過以下代碼讓一個Activity啟動另一個已知為SignInActivity:
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);
不過,您的應用可能還需要利用您的Activity數(shù)據(jù)執(zhí)行某項操作,例如發(fā)送電子郵件、短信或狀態(tài)更新。 在這種情況下,您的應用自身可能不具有執(zhí)行此類操作所需的Activity,因此您可以改為利用設備上其他應用提供的Activity為您執(zhí)行這些操作。 這便是Intent對象的真正價值所在—您可以創(chuàng)建一個Intent對象,對您想執(zhí)行的操作進行描述,系統(tǒng)會從其他應用啟動相應的Activity。 如果有多個Activity可以處理Intent,則用戶可以選擇要使用哪一個。 例如,如果您想允許用戶發(fā)送電子郵件,可以創(chuàng)建以下Intent對象:
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);
添加到Intent中的EXTRA_EMAIL extra是一個字符串數(shù)組,其中包含應將電子郵件發(fā)送到的電子郵件地址。 當電子郵件應用響應此Intent時,它會讀取extra中提供的字符串數(shù)組,并將它們放入電子郵件撰寫窗體的“收件人”字段。 在這種情況下,電子郵件應用的Activity啟動,并且當用戶完成操作時,您的Activity會恢復執(zhí)行。
(2)調(diào)用 [startActivityForResult()](https://developer.android.com/reference/android/app/Activity.html#startActivityForResult(android.content.Intent, int)),啟動 Activity 以獲得結(jié)果,重寫 [onActivityResult()](https://developer.android.com/reference/android/app/Activity.html#onActivityResult(int, int, android.content.Intent))方法,就可以得到被啟動Activity的返回結(jié)果。
干貨:徹底搞懂 startActivityForResult 在 FragmentActivity和 Fragment中的異同
例:您可能希望用戶選取其中一位聯(lián)系人,以便您的Activity對該聯(lián)系人中的信息執(zhí)行某項操作。 代碼實例如下:
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é)果時應該在onActivityResult() 方法中使用的基本邏輯。第一個條件檢查請求是否成功(如果成功,則resultCode 將為 RESULT_OK)以及此結(jié)果響應的請求是否已知 — 在此情況下,requestCode與隨startActivityForResult() 發(fā)送的第二個參數(shù)匹配。 代碼通過查詢 Intent 中返回的數(shù)據(jù)(data 參數(shù))從該處開始處理Activity結(jié)果。
實際情況是,ContentResolver 對一個內(nèi)容提供程序執(zhí)行查詢,后者返回一個 Cursor,讓查詢的數(shù)據(jù)能夠被讀取。如需了解詳細信息,參閱內(nèi)容提供程序。
(3)由以上兩種方式可看到啟動Activity時都會傳入相應的Itent,根據(jù)Intent的不同又可以將Activity的啟動方式分為顯示啟動和隱式啟動
- 顯示啟動:按名稱(完全限定類名)指定要啟動的組件。通常在自己的應用中使用顯式Intent來啟動組件,這是因為此時知道要啟動的 Activity 或服務的類名。例如,啟動新Activity以響應用戶操作,或者啟動服務以在后臺下載文件。如果您沒有為Activity聲明任何Intent過濾器,則Activity只能通過顯式Intent啟動。
- 隱式啟動:不會指定特定的組件,而是聲明要執(zhí)行的常規(guī)操作,配置Intent的action、data、category等,從而允許其他應用中的組件來處理它。例如,如需在地圖上向用戶顯示位置,則可以使用隱式Intent,請求另一具有此功能的應用在地圖上顯示指定的位置。
隱式Intent相對于顯示Intent具有以下優(yōu)勢: - 可以通過url傳web數(shù)據(jù)(存在數(shù)據(jù)泄漏)
- 可以實現(xiàn)解耦(如Activity之前的依賴)
- 可以在一個Application啟動另一個Application
- 可以通過配置Intent過濾器,使多個應用響應,然后根據(jù)用戶選擇啟動具體的應用
4.結(jié)束 Activity
調(diào)用Activity的finish()方法來結(jié)束該Activity,還可以通過調(diào)用finishActivity()結(jié)束在之前啟動的另一個Activity。
注意:在大多數(shù)情況下,不應使用這些方法顯式結(jié)束 Activity,Android系統(tǒng)會為您管理Activity的生命周期,因此您無需完成自己的 Activity。 調(diào)用這些方法可能對預期的用戶體驗產(chǎn)生不良影響,因此只應在您確實不想讓用戶返回此Activity實例時使用。
Activity生命周期
1.Activity的三種存在狀態(tài)
- 已繼續(xù):此Activity位于屏幕前臺并具有用戶焦點。(有時也將此狀態(tài)稱作“運行中”)
- 已暫停:此Activity失去焦點,另一個Activity位于屏幕前臺并具有用戶焦點,但此Activity仍可見。也就是說,另一個Activity顯示在此Activity上方,該Activity部分透明或未覆蓋整個屏幕(例如跳轉(zhuǎn)到一個Dialog Activity,非Dialog)。已暫停的Activity處于完全Activity 狀態(tài)(Activity 對象保留在內(nèi)存中,它保留了所有狀態(tài)和成員信息,并與窗口管理器保持連接),但在內(nèi)存極度不足的情況下,可能會被系統(tǒng)終止。
-
已停止:該 Activity被另一個Activity完全遮蓋(該Activity目前位于“后臺”)。 已停止的Activity同樣仍處于Activity狀態(tài)(Activity對象保留在內(nèi)存中,它保留了所有狀態(tài)和成員信息,但未與窗口管理器連接)。 不過,它對用戶不再可見,在他處需要內(nèi)存時可能會被系統(tǒng)終止。
Activity 的存在狀態(tài)
如果Activity處于暫?;蛲V範顟B(tài),系統(tǒng)可調(diào)用其 finish() 方法或直接終止其進程,將其從內(nèi)存中刪除。(將其結(jié)束或終止后)再次打開Activity時,必須重建。
2.Activity生命周期圖

對 Activity生命周期圖中的主要方法介紹如下:
(1)onCreate()
- 初始化,準備數(shù)據(jù),創(chuàng)建view setContentView、findViewById
- 一生一次
- 不可見
- 不適合動畫,不適合耗時,影響用戶看到界面的時間
(2)onStart()
- 初始化或恢復中,onCreate或onRestart之后,和onStop是一對兒
- 多次調(diào)用,Activity即將可見
(3)onResume()
- 初始化或恢復中,恢復activity時一定會調(diào)用,和onPause是一對兒
- 多次調(diào)用,可以交互,Activitty 進入active 狀態(tài)
- 執(zhí)行動畫、喚起相機、刷新數(shù)據(jù)
(4)onPause()
- 失去焦點、不可觸摸,所以和onResume是一對兒
- 可見
- 下一個Activity可以進行初始化工作
(5)onStop()
- 完全不可見,進入后臺
(6)onDestory()
- go die
(7)onPause() vs onStop()
- 不可操作 vs 不可見
其中,onCreate()、onStart()、onResume()可用于初始化,onPause()、onStop()、onDestory()不可用于初始化。
(8)Activity存在狀態(tài)與生命周期各個方法對應關系如圖3所示:

例:當一個 Activity A啟動另一個 ActivityB時,各自的生命周期轉(zhuǎn)變情況
- Activity A的onPause()方法執(zhí)行
- Activity B的onCreate()、onStart() 和 onResume()方法依次執(zhí)行(Activity B現(xiàn)在具有用戶焦點)
- 然后,如果Activity A在屏幕上不再可見,則其onStop()方法執(zhí)行Activity A暫停并停止(但如果它在后臺仍然可見,則不會停止)時,系統(tǒng)會創(chuàng)建 ActivityB。 如果這些Activity共用保存到磁盤或其他地方的數(shù)據(jù),必須了解的是,在創(chuàng)建第Activity B前,Activity A不會完全停止。更確切地說,啟動Activity B的過程與停止Activity A的過程存在重疊。
Activity A暫停并停止(但如果它在后臺仍然可見,則不會停止)時,系統(tǒng)會創(chuàng)建Activity B。 如果這些Activity共用保存到磁盤或其他地方的數(shù)據(jù),必須了解的是,在創(chuàng)建第Activity B前,Activity A不會完全停止。更確切地說,啟動 Activity B的過程與停止 Activity A的過程存在重疊。
Activity狀態(tài)保存與數(shù)據(jù)保存
1.Activity狀態(tài)保存
當Activity暫?;蛲V箷r,Activity的狀態(tài)會得到保留。 因為當Activity暫?;蛲V箷r,Activity對象仍保留在內(nèi)存中 — 有關其成員和當前狀態(tài)的所有信息仍處于Activity狀態(tài)。 因此,用戶在Activity內(nèi)所做的任何更改都會得到保留,這樣一來,當Activity返回前臺(當它“繼續(xù)”)時,這些更改仍然存在。不過,當系統(tǒng)為了恢復內(nèi)存而銷毀某項Activity時,Activity對象也會被銷毀,因此系統(tǒng)在繼續(xù)Activity時根本無法讓其狀態(tài)保持完好,而是必須在用戶返回Activity時重建Activity對象。但用戶并不知道系統(tǒng)銷毀 Activity 后又對其進行了重建,因此他們很可能認為Activity狀態(tài)毫無變化。
在這種情況下,您可以使用onSaveInstanceState()回調(diào)方法對有關 Activity 狀態(tài)的信息進行保存,以確保有關 Activity 狀態(tài)的重要信息得到保留。系統(tǒng)會先調(diào)用 onSaveInstanceState(),然后再使 Activity 變得易于銷毀,系統(tǒng)會向該方法傳遞一個 Bundle,您可以在其中使用 putString() 和putInt() 等方法以名稱-值對形式保存有關 Activity 狀態(tài)的信息。然后,如果系統(tǒng)終止您的應用進程,并且用戶返回您的 Activity,則系統(tǒng)會重建該 Activity,并將 Bundle 同時傳遞給 onCreate() 和 onRestoreInstanceState()。您可以使用上述任一方法從 Bundle 提取您保存的狀態(tài)并恢復該 Activity 狀態(tài)。如果沒有狀態(tài)信息需要恢復,則傳遞給您的 Bundle 是空值(如果是首次創(chuàng)建該 Activity,就會出現(xiàn)這種情況)。
由于onSaveInstanceState 的默認實現(xiàn)有助于保存 UI 的狀態(tài), 因此如果您為了保存更多狀態(tài)信息而重寫該方法,應始終先調(diào)用 onSaveInstanceState 的超類實現(xiàn),然后再執(zhí)行任何操作。同樣,如果您替代onRestoreInstanceState 方法,也應調(diào)用它的超類實現(xiàn),以便默認實現(xiàn)能夠恢復視圖狀態(tài)。
例:模擬調(diào)用onSaveInstanceState()、onRestoreInstanceState()場景
(1)運行你的程序,當程序打開時,按HOME鍵,這時系統(tǒng)會調(diào)用onSaveInstanceState方法,注意:這個方法的調(diào)用是系統(tǒng)決定的,不是軟件或其他什么因素,系統(tǒng)覺得有可能在某個時間因內(nèi)存不足等因素而Kill掉你,所以給你個機會讓你現(xiàn)在先利用這個方法保存下數(shù)據(jù),所以調(diào)用onSaveInstanceState()。
(2)一般情況下,即使你在onSaveInstanceState保存了數(shù)據(jù),在系統(tǒng)沒Kill掉程序的情況下,你再回到剛關閉的界面,你也會感覺剛才調(diào)用onSaveInstanceState方法保存的數(shù)據(jù)沒什么作用,只有在系統(tǒng)kill掉程序的情況下,再回到剛關閉的界面,回調(diào)了onRestoreInstanceState方法,這時onSaveInstanceState方法保存的數(shù)據(jù),才發(fā)揮真正的作用,如何重現(xiàn)這種場景呢,利用DDMS替系統(tǒng)干這件壞事,kill掉你的程序:在按下HOME鍵后,系統(tǒng)已經(jīng)調(diào)用你的onSaveInstanceState方法,打開DDMS找到你的程序進程,stop你的進程,再打開程序!
注意:無法保證系統(tǒng)會在銷毀您的Activity前調(diào)用 onSaveInstanceState(),因為存在不需要保存狀態(tài)的情況(例如用戶使用“返回” 按鈕離開您的Activity時,因為用戶的行為是在顯式關閉 Activity)。 如果系統(tǒng)調(diào)用 onSaveInstanceState(),它會在調(diào)用 onStop() 之前,并且可能會在調(diào)用onPause() 之前進行調(diào)用。
2.Activity數(shù)據(jù)保存問題
(1)由于無法保證系統(tǒng)會調(diào)用 onSaveInstanceState(),因此您只應利用它來記錄Activity的瞬態(tài)(UI 的狀態(tài))—切勿使用它來存儲持久性數(shù)據(jù),而應使用onPause()在用戶離開Activity后保存持久性數(shù)據(jù)(如應保存到數(shù)據(jù)庫的數(shù)據(jù))。如果您必須在第一個Activity停止時向數(shù)據(jù)庫寫入數(shù)據(jù),以便下一個 Activity 能夠讀取該數(shù)據(jù),則應在onPause()而不是onStop() 執(zhí)行期間向數(shù)據(jù)庫寫入數(shù)據(jù)。
(2)如果從Activity A通過Itent攜帶數(shù)據(jù)打開Activity B,Activity B界面被系統(tǒng)kill后,重新創(chuàng)建Activity B之后,之前攜帶的數(shù)據(jù)能被還原,但該界面被kill之前對傳遞過來的數(shù)據(jù)作任何修改都作廢;
(3)B界面→C界面,C界面 finish后,系統(tǒng)重建B界面,依然能得到C界面回傳的數(shù)據(jù)。例如Activity B打開Activity C,用 startActivityForResult()方法要求Activity C finish時回傳數(shù)據(jù)到Activity B,跳轉(zhuǎn)到Activity C后Activity B被kill了,當系統(tǒng)在Activity C finish后重建Activity B,onActivityResult()方法依然能收到Activity C傳回的數(shù)據(jù);
(4)從A界面-->B界面-->C界面,用A界面通過Intent傳過來的數(shù)據(jù),即使B界面到C界面后B界面被殺了,再回到B界面,B界面還是拿得到A界面?zhèn)鬟^來的數(shù)據(jù),但還是原始Intent的數(shù)據(jù),如果B界面在被殺之前對Intent數(shù)據(jù)加工,通過onSaveInstanceState存儲,通過onRestoreInstanceState統(tǒng)一處理了,那么就要繞過之前A界面通過Intent傳過來的原始數(shù)據(jù)的干擾。
Activity處理配置變更等問題
程序在運行時,一些設備的配置可能會改變,如:橫豎屏的切換、鍵盤的可用性或語言的切換等,這樣的事情已發(fā)生,Activity會重新啟動。其中的過程是:在銷毀之前會先調(diào)用onSaveInstancestate()去保存應用中的一些數(shù)據(jù),然后調(diào)用 onDestory(),最后才會去調(diào)用onCreate()或者onRestoreInstanceState方法重新啟動Activiy。當在Android的Manifest.xml定義了android:configchange屬性之后就不會去重新啟動Activity,而是通知程序去調(diào)用onConfigurationChange()函數(shù)。例如,在切換語言之后會重新啟動Activity,定義這個屬性之后就不會重新啟動Activity。其可以設置多個屬性,中間用“|”隔開。
對android:configChanges屬性,一般認為有以下幾點(橫豎屏切換):
- 不設置Activity的android:configChanges時,切屏會重新調(diào)用各個生命周期,切橫屏時會執(zhí)行一次,切豎屏時會執(zhí)行兩次;
- 設置Activity的android:configChanges="orientation"時,切屏還是會重新調(diào)用各個生命周期,切橫、豎屏時只會執(zhí)行一次;
- 設置Activity的android:configChanges="orientation|keyboardHidden"時,切屏不會重新調(diào)用各個生命周期,只會執(zhí)行onConfigurationChanged方法,在其中可獲取切換后的橫豎屏參數(shù)或者進行一些其他操作。
參考資料:
http://blog.csdn.net/liuhe688/article/details/6754323
http://droidyue.com/blog/2015/08/16/dive-into-android-activity-launchmode/index.html
