一号胚、Activity創(chuàng)建籽慢、啟動和關(guān)閉
1.創(chuàng)建 Activity
2.在清單文件中聲明
3.啟動
4.結(jié)束
二、Activity生命周期
三猫胁、Activity處理配置變更等問題
Activity創(chuàng)建箱亿、啟動和關(guān)閉
Activity是一個應(yīng)用組件,用戶可與其提供的屏幕進行交互弃秆,以執(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)該方法時土居,應(yīng)該初始化Activity的必需組件枣购。 最重要的是,必須在此方法內(nèi)調(diào)用setContentView()擦耀,以定義Activity用戶界面的布局棉圈。
- onPause(),系統(tǒng)將此方法作為用戶離開Activity的第一個信號(但并不總是意味著 Activity 會被銷毀)進行調(diào)用埂奈。 在此方法內(nèi)迄损,通常應(yīng)該確認當前用戶會話結(jié)束后仍然有效的任何更改(因為用戶可能不會返回)定躏。
2.在清單文件中聲明Activity
(1)在清單文件中聲明了Activity账磺,系統(tǒng)才能訪問它。 要聲明Activity痊远,則需打開清單文件垮抗,并將 <activity/> 元素添加為<application/> 元素的子項。例如:
<manifest ... >
<application ... >
<activity android:name=".ExampleActivity" />
...
</application ... >
...
</manifest >
您還可以在此元素中加入幾個其他特性碧聪,以定義Activity標簽冒版、Activity圖標或風格主題等用于設(shè)置Activity UI風格的屬性。android:name特性是唯一的必需特性—它指定逞姿。
(2)使用 Intent 過濾器
元素 <activity/> 還可指定各種Intent過濾器—使用<Intent-filter/>元素—以聲明其他應(yīng)用組件激活它的方法辞嗡。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)用的“主”入口點捆等。<category/>元素指定此 Activity應(yīng)列入系統(tǒng)的應(yīng)用啟動器內(nèi)(以便用戶啟動該 Activity)。
如果打算讓應(yīng)用成為獨立應(yīng)用续室,不允許其他應(yīng)用激活其Activity栋烤,則不需要任何其他Intent過濾器。 正如前例所示挺狰,只應(yīng)有一個Activity具有“main”和“l(fā)auncher”
類別明郭。 如果不想提供給其他應(yīng)用的Activity不應(yīng)有任何Intent過濾器,可以利用顯式 Intent 自行啟動它們丰泊。不過薯定,如果想讓 Activity對衍生自其他應(yīng)用(以及您的自有應(yīng)用)的隱式Intent作出響應(yīng),則必須為Activity定義其他 Intent 過濾器瞳购。 對于您想要作出響應(yīng)的每一個 Intent類型话侄,都必須加入相應(yīng)的<Intent-filter/>,其中包括一個<action/>元素苛败,還可選擇性地包括一個<category/>元素和/或一個<data/>元素满葛。這些元素指定您的Activity可以響應(yīng)的Intent類型。
3.啟動 Activity
(1)調(diào)用startActivity()罢屈,并將其傳給想啟動Activity的Intent來啟動另一個Activity
Intent對象會指定您想啟動的具體Activity或描述您想執(zhí)行的操作類型(系統(tǒng)會為您選擇合適的Activity嘀韧,甚至是來自其他應(yīng)用的Activity)。 Intent 對象還可能攜帶少量供所啟動Activity使用的數(shù)據(jù)缠捌。
例如锄贷,可以通過以下代碼讓一個Activity啟動另一個已知為SignInActivity:
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);
不過,您的應(yīng)用可能還需要利用您的Activity數(shù)據(jù)執(zhí)行某項操作曼月,例如發(fā)送電子郵件谊却、短信或狀態(tài)更新。 在這種情況下哑芹,您的應(yīng)用自身可能不具有執(zhí)行此類操作所需的Activity炎辨,因此您可以改為利用設(shè)備上其他應(yīng)用提供的Activity為您執(zhí)行這些操作。 這便是Intent對象的真正價值所在—您可以創(chuàng)建一個Intent對象聪姿,對您想執(zhí)行的操作進行描述碴萧,系統(tǒng)會從其他應(yīng)用啟動相應(yī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ù)組曹质,其中包含應(yīng)將電子郵件發(fā)送到的電子郵件地址。 當電子郵件應(yīng)用響應(yīng)此Intent時,它會讀取extra中提供的字符串數(shù)組羽德,并將它們放入電子郵件撰寫窗體的“收件人”字段几莽。 在這種情況下,電子郵件應(yīng)用的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é)果時應(yīng)該在onActivityResult() 方法中使用的基本邏輯够颠。第一個條件檢查請求是否成功(如果成功熙侍,則resultCode 將為 RESULT_OK)以及此結(jié)果響應(yīng)的請求是否已知 — 在此情況下,requestCode與隨startActivityForResult() 發(fā)送的第二個參數(shù)匹配履磨。 代碼通過查詢 Intent 中返回的數(shù)據(jù)(data 參數(shù))從該處開始處理Activity結(jié)果蛉抓。
實際情況是,ContentResolver 對一個內(nèi)容提供程序執(zhí)行查詢剃诅,后者返回一個 Cursor巷送,讓查詢的數(shù)據(jù)能夠被讀取。如需了解詳細信息矛辕,參閱內(nèi)容提供程序笑跛。
(3)由以上兩種方式可看到啟動Activity時都會傳入相應(yīng)的Itent,根據(jù)Intent的不同又可以將Activity的啟動方式分為顯示啟動和隱式啟動
- 顯示啟動:按名稱(完全限定類名)指定要啟動的組件聊品。通常在自己的應(yīng)用中使用顯式Intent來啟動組件飞蹂,這是因為此時知道要啟動的 Activity 或服務(wù)的類名。例如翻屈,啟動新Activity以響應(yīng)用戶操作陈哑,或者啟動服務(wù)以在后臺下載文件。如果您沒有為Activity聲明任何Intent過濾器伸眶,則Activity只能通過顯式Intent啟動惊窖。
- 隱式啟動:不會指定特定的組件,而是聲明要執(zhí)行的常規(guī)操作赚抡,配置Intent的action爬坑、data纠屋、category等涂臣,從而允許其他應(yīng)用中的組件來處理它。例如,如需在地圖上向用戶顯示位置赁遗,則可以使用隱式Intent署辉,請求另一具有此功能的應(yīng)用在地圖上顯示指定的位置。
隱式Intent相對于顯示Intent具有以下優(yōu)勢: - 可以通過url傳web數(shù)據(jù)(存在數(shù)據(jù)泄漏)
- 可以實現(xiàn)解耦(如Activity之前的依賴)
- 可以在一個Application啟動另一個Application
- 可以通過配置Intent過濾器岩四,使多個應(yīng)用響應(yīng)哭尝,然后根據(jù)用戶選擇啟動具體的應(yīng)用
4.結(jié)束 Activity
調(diào)用Activity的finish()方法來結(jié)束該Activity,還可以通過調(diào)用finishActivity()結(jié)束在之前啟動的另一個Activity剖煌。
注意:在大多數(shù)情況下材鹦,不應(yīng)使用這些方法顯式結(jié)束 Activity,Android系統(tǒng)會為您管理Activity的生命周期耕姊,因此您無需完成自己的 Activity桶唐。 調(diào)用這些方法可能對預期的用戶體驗產(chǎn)生不良影響,因此只應(yīng)在您確實不想讓用戶返回此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)森缠,系統(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)與生命周期各個方法對應(yīng)關(guān)系如圖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暫驮阈瑁或停止時屉佳,Activity的狀態(tài)會得到保留。 因為當Activity暫椭扪海或停止時武花,Activity對象仍保留在內(nèi)存中 — 有關(guān)其成員和當前狀態(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)方法對有關(guān) Activity 狀態(tài)的信息進行保存玛荞,以確保有關(guān) Activity 狀態(tài)的重要信息得到保留。系統(tǒng)會先調(diào)用 onSaveInstanceState()呕寝,然后再使 Activity 變得易于銷毀冲泥,系統(tǒng)會向該方法傳遞一個 Bundle,您可以在其中使用 putString() 和putInt() 等方法以名稱-值對形式保存有關(guān) Activity 狀態(tài)的信息壁涎。然后凡恍,如果系統(tǒng)終止您的應(yī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)信息而重寫該方法洼冻,應(yīng)始終先調(diào)用 onSaveInstanceState 的超類實現(xiàn),然后再執(zhí)行任何操作隅很。同樣撞牢,如果您替代onRestoreInstanceState 方法,也應(yīng)調(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掉程序的情況下,你再回到剛關(guān)閉的界面景描,你也會感覺剛才調(diào)用onSaveInstanceState方法保存的數(shù)據(jù)沒什么作用十办,只有在系統(tǒng)kill掉程序的情況下,再回到剛關(guān)閉的界面超棺,回調(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時赡突,因為用戶的行為是在顯式關(guān)閉 Activity)对扶。 如果系統(tǒng)調(diào)用 onSaveInstanceState()区赵,它會在調(diào)用 onStop() 之前,并且可能會在調(diào)用onPause() 之前進行調(diào)用浪南。
2.Activity數(shù)據(jù)保存問題
(1)由于無法保證系統(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ù))络凿。如果您必須在第一個Activity停止時向數(shù)據(jù)庫寫入數(shù)據(jù)骡送,以便下一個 Activity 能夠讀取該數(shù)據(jù),則應(yīng)在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處理配置變更等問題
程序在運行時开伏,一些設(shè)備的配置可能會改變膀跌,如:橫豎屏的切換、鍵盤的可用性或語言的切換等固灵,這樣的事情已發(fā)生淹父,Activity會重新啟動。其中的過程是:在銷毀之前會先調(diào)用onSaveInstancestate()去保存應(yīng)用中的一些數(shù)據(jù)怎虫,然后調(diào)用 onDestory()暑认,最后才會去調(diào)用onCreate()或者onRestoreInstanceState方法重新啟動Activiy困介。當在Android的Manifest.xml定義了android:configchange屬性之后就不會去重新啟動Activity,而是通知程序去調(diào)用onConfigurationChange()函數(shù)蘸际。例如座哩,在切換語言之后會重新啟動Activity,定義這個屬性之后就不會重新啟動Activity粮彤。其可以設(shè)置多個屬性根穷,中間用“|”隔開。
對android:configChanges屬性导坟,一般認為有以下幾點(橫豎屏切換):
- 不設(shè)置Activity的android:configChanges時屿良,切屏會重新調(diào)用各個生命周期,切橫屏時會執(zhí)行一次惫周,切豎屏時會執(zhí)行兩次尘惧;
- 設(shè)置Activity的android:configChanges="orientation"時,切屏還是會重新調(diào)用各個生命周期递递,切橫喷橙、豎屏時只會執(zhí)行一次;
- 設(shè)置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