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

一.簡介

Activity是Android組件中最基本也是最為常見用的四大組件(Activity,Service服務,Content Provider內容提供者或听,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實例已經存在于棧頂洒嗤,那么再次啟動這個activity的時候箫荡,不會重新創(chuàng)建實例,而是重用位于棧頂的那個實例渔隶,并且會調用實例的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)中已經存在這樣一個實例税课,就會將這個實例調度到任務棧的棧頂,并清除它當前所在任務中位于它上面的所有的activity痊剖;如果這個已存在的任務中不存在一個要啟動的Activity的實例韩玩,則在這個任務的頂端啟動一個實例;若這個任務不存在陆馁,則會啟動一個新的任務找颓,在這個新的任務中啟動這個singleTask模式的Activity的一個實例。

d.singleInstance:

以singleInstance模式啟動的Activity具有全局唯一性叮贩,即整個系統(tǒng)中只會存在一個這樣的實例击狮,如果在啟動這樣的Activiyt時,已經存在了一個實例益老,那么會把它所在的任務調度到前臺彪蓬,重用這個實例。
以singleInstance模式啟動的Activity具有獨占性捺萌,即它會獨自占用一個任務档冬,被他開啟的任何activity都會運行在其他任務中(官方文檔上的描述為,singleInstance模式的Activity不允許其他Activity和它共存在一個任務中)桃纯。
被singleInstance模式的Activity開啟的其他activity酷誓,能夠開啟一個新任務,但不一定開啟新的任務慈参,也可能在已有的一個任務中開啟呛牲,受條件的限制,這個條件是:當前系統(tǒng)中是不是已經有了一個activity B的taskAffinity屬性指定的任務驮配。

三.Activity管理

涉及到Activity啟動娘扩,就不得不說一下Activity的管理,Activity是以什么方式及被什么類來進行管理的壮锻,涉及的類主要如下:

a.ActivityRecord:
   歷史棧中的一個條目琐旁,代表一個activity。ActivityRecord中的成員變量task表示其所在的TaskRecord猜绣,ActivityRecord與TaskRecord建立了聯系灰殴。
b.TaskRecord:

內部維護一個 ArrayList<ActivityRecord> 用來保存ActivityRecord,TaskRecord中的mStack表示其所在的ActivityStack掰邢,TaskRecord與ActivityStack建立了聯系牺陶。

c.ActivityStack:

內部維護了一個 ArrayList<TaskRecord> 伟阔,用來管理TaskRecord,ActivityStack中持有ActivityStackSupervisor對象掰伸,由ActivityStackSupervisor創(chuàng)建皱炉。

d.ActivityStackSupervisor:

負責所有ActivityStack的管理。內部管理了mHomeStack狮鸭、mFocusedStack和mLastFocusedStack三個Activity棧合搅。其中,mHomeStack管理的是Launcher相關的Activity棧歧蕉;mFocusedStack管理的是當前顯示在前臺Activity的Activity棧灾部;mLastFocusedStack管理的是上一次顯示在前臺Activity的Activity棧。

e.ActivityThread:

ActivityThread 運行在UI線程(主線程)惯退,App的真正入口赌髓。

f.ApplicationThread:
用來實現AMS和ActivityThread之間的交互。
g.Instrumentation:

負責調用Activity和Application生命周期蒸痹。

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

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

a.調用入口

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

b.調用場景

前面提到“被動關閉”榛鼎,如果是主動關閉那么就不會調用,比如:按back鍵鳖孤、調用finish()等者娱,那么"被動關閉"的場景有哪些呢?下面給列舉一下:

場景 具體描述
從當前activity啟動新activity后 當前activity會調用onSaveInstanceState()
屏幕切換苏揣,橫屏切豎屏及豎屏切橫屏 切換時黄鳍,系統(tǒng)會先銷毀activity,切換后系統(tǒng)再創(chuàng)建新的activity平匈,
所以會調用onSaveInstanceState()
按HOME鍵后 按下home鍵框沟,會啟動新的應用,當前activity會調用onSaveInstanceState()
關閉屏幕顯示 進入熄屏界面增炭,當前activity會調用onSaveInstanceState()
切換白天黑夜模式 當未配置configChange時忍燥,切換白天黑夜模式,當前activity會調用onSaveInstanceState()
c.調用時機

肯定在調用onStop()前被調用隙姿,但不保證在onPause()前 / 后梅垄,一般是在onPause()后調用。

d.使用方式

當需要保持狀態(tài)時输玷,在onSaveInstanceState()內執(zhí)行以下邏輯:

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

當需要恢復時队丝,在onCreate()內部執(zhí)行以下邏輯:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //首次啟動時靡馁,獲取Intent內部傳入的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默認實現:onSaveInstanceState()奈嘿,即UI的任何改變都會自動的存儲和在activity重新創(chuàng)建的時候自動的恢復(只有在為該UI提供了唯一ID后才起作用);
   若需復寫該方法從而存儲額外的狀態(tài)信息時吞加,應先調用父類的onSaveInstanceState()(因為默認的onSaveInstanceState()幫助UI存儲它的狀態(tài))裙犹;
   只使用該方法記錄Activity的瞬間狀態(tài)(UI的狀態(tài)),而不是去存儲持久化數據衔憨,因為onSaveInstanceState()調用時機不確定性叶圃;可使用 onPause()[一定會執(zhí)行]存儲持久化數據;

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

a.調用入口

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

b.調用場景

若被動關閉了Activity,即調用了onSaveInstanceState()码党,那么下次啟動時會調用onRestoreInstanceState()德崭。

c.調用時機
   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()不一定 成對被調用
如:當正在顯示Activity A時揖盘,用戶按下HOME鍵回到主界面眉厨,然后用戶緊接著又返回到Activity A,此時Activity A一般不會因為內存的原因被系統(tǒng)銷毀兽狭,故Activity A的onRestoreInstanceState()不會被執(zhí)行憾股;
針對以上情況,onSaveInstanceState保持的參數可以選擇在onCreate()內部進行解析使用箕慧,因為onSaveInstanceState的bundle參數會傳遞到onCreate方法中服球,可選擇在onCreate()中做數據還原。
至此:Activity的啟動模式及Activity的狀態(tài)保持及恢復介紹完畢颠焦。

作者:雷濤賽文
鏈接:http://www.reibang.com/p/765c7b6e6bc7
來源:簡書
轉載以備查閱

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末斩熊,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子伐庭,更是在濱河造成了極大的恐慌粉渠,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件似忧,死亡現場離奇詭異渣叛,居然都是意外死亡,警方通過查閱死者的電腦和手機盯捌,發(fā)現死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門淳衙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事箫攀〕ι” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵靴跛,是天一觀的道長缀雳。 經常有香客問我,道長梢睛,這世上最難降的妖魔是什么肥印? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮绝葡,結果婚禮上深碱,老公的妹妹穿的比我還像新娘。我一直安慰自己藏畅,他們只是感情好敷硅,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著愉阎,像睡著了一般绞蹦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上榜旦,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天幽七,我揣著相機與錄音,去河邊找鬼章办。 笑死锉走,一個胖子當著我的面吹牛,可吹牛的內容都是我干的藕届。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼亭饵,長吁一口氣:“原來是場噩夢啊……” “哼休偶!你這毒婦竟也來了?” 一聲冷哼從身側響起辜羊,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤踏兜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后八秃,有當地人在樹林里發(fā)現了一具尸體碱妆,經...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年昔驱,在試婚紗的時候發(fā)現自己被綠了疹尾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖纳本,靈堂內的尸體忽然破棺而出窍蓝,到底是詐尸還是另有隱情,我是刑警寧澤繁成,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布吓笙,位于F島的核電站,受9級特大地震影響巾腕,放射性物質發(fā)生泄漏面睛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一尊搬、第九天 我趴在偏房一處隱蔽的房頂上張望叁鉴。 院中可真熱鬧,春花似錦毁嗦、人聲如沸亲茅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽克锣。三九已至,卻和暖如春腔长,著一層夾襖步出監(jiān)牢的瞬間袭祟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工捞附, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留巾乳,地道東北人。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓鸟召,卻偏偏與公主長得像胆绊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子欧募,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內容