Android Activity啟動模式與狀態(tài)保存及恢復詳解

一.簡介

???????Activity是Android組件中最基本也是最為常見用的四大組件(Activity废菱,Service服務蜂筹,Content Provider內(nèi)容提供者力细,BroadcastReceiver廣播接收器)之一 纪铺。
???????Activity是一個應用程序組件戈鲁,提供一個屏幕料按,用戶可以用來交互為了完成某項任務篡殷。
???????Activity中所有操作都與用戶密切相關,是一個負責與用戶交互的組件藻治,可以通過setContentView(View)來顯示指定控件碘勉。
???????在一個android應用中,一個Activity通常就是一個單獨的屏幕桩卵,它上面可以顯示一些控件也可以監(jiān)聽并處理用戶的事件做出響應验靡。Activity之間通過Intent進行通信倍宾。
???????關于Activity啟動流程請參考之前的文章Android activity啟動流程分析

二.啟動模式

???????activity有四種啟動模式,分別為standard胜嗓,singleTop高职,singleTask,singleInstance辞州。如果要使用這四種啟動模式怔锌,必須在manifest文件中<activity>標簽中的launchMode屬性中配置。

a.standard

???????標準的默認啟動模式变过,這種模式下activity可以被多次實例化埃元,即在一個task中可以存在多個activity,每一個activity會處理一個intent對象媚狰,(在A中再次啟動A岛杀,會存在后面的A在前面的A上面,當前task會存在兩個activity的實例對象)

b.singleTop

???????如果一個singleTop模式啟動的activity實例已經(jīng)存在于棧頂崭孤,那么再次啟動這個activity的時候类嗤,不會重新創(chuàng)建實例,而是重用位于棧頂?shù)哪莻€實例裳瘪,并且會調(diào)用實例的onNewIntent()方法將Intent對象傳遞到這個實例中土浸,如果實例不位于棧頂罪针,會創(chuàng)建新的實例彭羹。

c.singleTask:

???????啟動模式設置為singleTask,framework在啟動該activity時只會把它標示為可在一個新任務中啟動泪酱,至于是否在一個新任務中啟動派殷,還要受其他條件的限制,即taskAffinity屬性墓阀。
???????taskAffinity:默認情況下毡惜,一個應用中的所有activity具有相同的taskAffinity,即應用程序的包名斯撮。我們可以通過設置不同的taskAffinity屬性給應用中的activity分組经伙,也可以把不同的應用中的activity的taskAffinity設置成相同的值,當兩個不同應用中的activity設置成相同的taskAffinity時勿锅,則兩個activity會屬于同一個TaskRecord帕膜。
???????在啟動一個singleTask的Activity實例時,如果系統(tǒng)中已經(jīng)存在這樣一個實例溢十,就會將這個實例調(diào)度到任務棧的棧頂垮刹,并清除它當前所在任務中位于它上面的所有的activity;如果這個已存在的任務中不存在一個要啟動的Activity的實例张弛,則在這個任務的頂端啟動一個實例荒典;若這個任務不存在酪劫,則會啟動一個新的任務,在這個新的任務中啟動這個singleTask模式的Activity的一個實例寺董。

d.singleInstance:

???????以singleInstance模式啟動的Activity具有全局唯一性覆糟,即整個系統(tǒng)中只會存在一個這樣的實例,如果在啟動這樣的Activiyt時遮咖,已經(jīng)存在了一個實例搪桂,那么會把它所在的任務調(diào)度到前臺,重用這個實例盯滚。
???????以singleInstance模式啟動的Activity具有獨占性踢械,即它會獨自占用一個任務,被他開啟的任何activity都會運行在其他任務中(官方文檔上的描述為魄藕,singleInstance模式的Activity不允許其他Activity和它共存在一個任務中)内列。
???????被singleInstance模式的Activity開啟的其他activity,能夠開啟一個新任務背率,但不一定開啟新的任務话瞧,也可能在已有的一個任務中開啟,受條件的限制寝姿,這個條件是:當前系統(tǒng)中是不是已經(jīng)有了一個activity B的taskAffinity屬性指定的任務交排。

三.Activity管理

???????涉及到Activity啟動,就不得不說一下Activity的管理饵筑,Activity是以什么方式及被什么類來進行管理的埃篓,涉及的類主要如下:

a.ActivityRecord:

???????歷史棧中的一個條目,代表一個activity根资。ActivityRecord中的成員變量task表示其所在的TaskRecord架专,ActivityRecord與TaskRecord建立了聯(lián)系。

b.TaskRecord:

???????內(nèi)部維護一個 ArrayList<ActivityRecord> 用來保存ActivityRecord玄帕,TaskRecord中的mStack表示其所在的ActivityStack部脚,TaskRecord與ActivityStack建立了聯(lián)系。

c.ActivityStack:

???????內(nèi)部維護了一個 ArrayList<TaskRecord> 裤纹,用來管理TaskRecord委刘,ActivityStack中持有ActivityStackSupervisor對象,由ActivityStackSupervisor創(chuàng)建鹰椒。

d.ActivityStackSupervisor:

???????負責所有ActivityStack的管理锡移。內(nèi)部管理了mHomeStack、mFocusedStack和mLastFocusedStack三個Activity棧吹零。其中罩抗,mHomeStack管理的是Launcher相關的Activity棧;mFocusedStack管理的是當前顯示在前臺Activity的Activity棧灿椅;mLastFocusedStack管理的是上一次顯示在前臺Activity的Activity棧套蒂。

e.ActivityThread:

???????ActivityThread 運行在UI線程(主線程)钞支,App的真正入口。

f.ApplicationThread:

???????用來實現(xiàn)AMS和ActivityThread之間的交互操刀。

g.Instrumentation:

???????負責調(diào)用Activity和Application生命周期烁挟。

四.Activity狀態(tài)保存

???????當一個Activity未被主動關閉,即“被動關閉”時骨坑,可能需要系統(tǒng)給用戶提供保持一些狀態(tài)的入口撼嗓。

a.調(diào)用入口

???????前面說的入口就是:Activity提供了onSaveInstanceState()方法,該方法是Activity在關閉前保存狀態(tài)的核心方法欢唾。

b.調(diào)用場景

???????前面提到“被動關閉”且警,如果是主動關閉那么就不會調(diào)用,比如:按back鍵礁遣、調(diào)用finish()等斑芜,那么"被動關閉"的場景有哪些呢?下面給列舉一下:

場景 具體描述
從當前activity啟動新activity后 當前activity會調(diào)用onSaveInstanceState()
屏幕切換祟霍,橫屏切豎屏及豎屏切橫屏 切換時杏头,系統(tǒng)會先銷毀activity,切換后系統(tǒng)再創(chuàng)建新的activity沸呐,
所以會調(diào)用onSaveInstanceState()
按HOME鍵后 按下home鍵醇王,會啟動新的應用,當前activity會調(diào)用onSaveInstanceState()
關閉屏幕顯示 進入熄屏界面崭添,當前activity會調(diào)用onSaveInstanceState()
切換白天黑夜模式 當未配置configChange時寓娩,切換白天黑夜模式,當前activity會調(diào)用onSaveInstanceState()
c.調(diào)用時機

???????肯定在調(diào)用onStop()前被調(diào)用滥朱,但不保證在onPause()前 / 后根暑,一般是在onPause()后調(diào)用。

d.使用方式

???????當需要保持狀態(tài)時徙邻,在onSaveInstanceState()內(nèi)執(zhí)行以下邏輯:

@Override
protected void onSaveInstanceState(Bundle outState) {
    //調(diào)用父類方法幫助UI存儲狀態(tài)
    super.onSaveInstanceState(outState);
    outState.putInt(ActionUtils.SRCDISPLAY, mSrcDisplay);
    outState.putInt("Control_State", mControlState);
}

???????當需要恢復時,在onCreate()內(nèi)部執(zhí)行以下邏輯:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //首次啟動時畸裳,獲取Intent內(nèi)部傳入的bundle
    if (savedInstanceState == null) {
        Intent intent = getIntent();
        Bundle bundle = intent.getExtras();
        mSrcDisplay = bundle.getInt(ActionUtils.SRCDISPLAY);
    //savedInstanceState不為空缰犁,說明執(zhí)行了onSaveInstanceState(),直接獲取對應的狀態(tài)
    } else {
        mSrcDisplay = savedInstanceState.getInt(ActionUtils.SRCDISPLAY);
        mControlState = savedInstanceState.getInt("Control_State");
    }
}

???????布局每個View默認實現(xiàn):onSaveInstanceState()怖糊,即UI的任何改變都會自動的存儲和在activity重新創(chuàng)建的時候自動的恢復(只有在為該UI提供了唯一ID后才起作用)帅容;
???????若需復寫該方法從而存儲額外的狀態(tài)信息時,應先調(diào)用父類的onSaveInstanceState()(因為默認的onSaveInstanceState()幫助UI存儲它的狀態(tài))伍伤;
???????只使用該方法記錄Activity的瞬間狀態(tài)(UI的狀態(tài))并徘,而不是去存儲持久化數(shù)據(jù),因為onSaveInstanceState()調(diào)用時機不確定性扰魂;可使用 onPause()[一定會執(zhí)行]存儲持久化數(shù)據(jù)麦乞;

四.Activity狀態(tài)恢復

a.調(diào)用入口

???????Activity提供了onRestoreInstanceState()方法蕴茴,該方法是Activity在重新創(chuàng)建后恢復之前保存狀態(tài)的核心方法。

b.調(diào)用場景

???????若被動關閉了Activity姐直,即調(diào)用了onSaveInstanceState()倦淀,那么下次啟動時會調(diào)用onRestoreInstanceState()。

c.調(diào)用時機

???????onCreate()--->onStart()--->onRestoreInstanceState()--->onResume()

d.使用方式
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    mSrcDisplay = savedInstanceState.getInt(ActionUtils.SRCDISPLAY);
    mControlState = savedInstanceState.getInt("Control_State");
}

???????注意:onSaveInstanceState()声畏、onRestoreInstanceState()不一定 成對被調(diào)用
???????如:當正在顯示Activity A時撞叽,用戶按下HOME鍵回到主界面,然后用戶緊接著又返回到Activity A插龄,此時Activity A一般不會因為內(nèi)存的原因被系統(tǒng)銷毀愿棋,故Activity A的onRestoreInstanceState()不會被執(zhí)行;
???????針對以上情況均牢,onSaveInstanceState保持的參數(shù)可以選擇在onCreate()內(nèi)部進行解析使用初斑,因為onSaveInstanceState的bundle參數(shù)會傳遞到onCreate方法中,可選擇在onCreate()中做數(shù)據(jù)還原膨处。
???????至此:Activity的啟動模式及Activity的狀態(tài)保持及恢復介紹完畢见秤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市真椿,隨后出現(xiàn)的幾起案子鹃答,更是在濱河造成了極大的恐慌,老刑警劉巖突硝,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件测摔,死亡現(xiàn)場離奇詭異,居然都是意外死亡解恰,警方通過查閱死者的電腦和手機锋八,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來护盈,“玉大人挟纱,你說我怎么就攤上這事「危” “怎么了紊服?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長胸竞。 經(jīng)常有香客問我欺嗤,道長,這世上最難降的妖魔是什么卫枝? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任煎饼,我火速辦了婚禮,結(jié)果婚禮上校赤,老公的妹妹穿的比我還像新娘吆玖。我一直安慰自己筒溃,他們只是感情好,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布衰伯。 她就那樣靜靜地躺著铡羡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪意鲸。 梳的紋絲不亂的頭發(fā)上烦周,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天,我揣著相機與錄音怎顾,去河邊找鬼读慎。 笑死,一個胖子當著我的面吹牛槐雾,可吹牛的內(nèi)容都是我干的夭委。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼募强,長吁一口氣:“原來是場噩夢啊……” “哼株灸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起擎值,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤慌烧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鸠儿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屹蚊,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年进每,在試婚紗的時候發(fā)現(xiàn)自己被綠了汹粤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡田晚,死狀恐怖嘱兼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肉瓦,我是刑警寧澤遭京,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站泞莉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏船殉。R本人自食惡果不足惜鲫趁,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望利虫。 院中可真熱鬧挨厚,春花似錦堡僻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至巢价,卻和暖如春牲阁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背壤躲。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工城菊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人碉克。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓凌唬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親漏麦。 傳聞我的和親對象是個殘疾皇子客税,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

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