以文科生視角解讀Activity的相關(guān)知識,確定不來看下?

Activity系列知識-Activity的生命周期方法


????????大師兄剛學(xué)Android那會兒,有個(gè)比較要好的同事,一位廣東的小伙子(后來不曾想,我孩子居然會和他同名,哈哈,感覺占了大便宜),平時(shí)我們在一起,除了開開車,吃吃瓜,也會探討Android相關(guān)的知識。當(dāng)時(shí)我們討論到Activity時(shí),他說這和Android的硬件啥啥的有關(guān),作為一個(gè)個(gè)文科生,根本就不知道該怎么接話,只能一個(gè)勁兒的點(diǎn)頭說是,其實(shí)內(nèi)心對自己是鄙視的,硬件都不知道,電阻都忘了,還來做Android開發(fā),這不是鄙視廣大的Androider嗎?

????當(dāng)時(shí)想想也是,根本沒有深入思考過,為什么Activity會有生命周期,或者說根本就沒有發(fā)揮自己作為一個(gè)文科生的本色,講好Activity相關(guān)的事兒。多年以后,當(dāng)大師兄再去思考這個(gè)問題時(shí),我們已經(jīng)各安天涯了,當(dāng)對android的那份炙熱的心,卻還是一樣的火熱!




????????首先,Activity之所以會有生命周期,這個(gè)和Android的生態(tài)有關(guān),或者說和手機(jī)相關(guān)的移動設(shè)備是相關(guān),可以說這是移動設(shè)備的出身?xiàng)l件啊,這個(gè)無法改變。Android作為移動設(shè)備的一員,它存在自身的一些特點(diǎn),比如那么是6寸的屏幕,也不可能一下子全部展示出一個(gè)有幾百個(gè)條目的列表數(shù)據(jù)。當(dāng)用戶在切換不同的App時(shí),注定有些App在當(dāng)前的情況下是可見的,有些App只能退到后臺,由可見變成不可見,在切換應(yīng)用的時(shí)候,也就是在各種可見和不可見之間在切換。如果每次去切換,都需要重新的喚醒Activity,換句話時(shí),每次切換的時(shí)候,都需要系統(tǒng)來創(chuàng)建一個(gè)新的組件嗎?

????????如果是這樣的,估計(jì)用戶得炸了。頻繁的切換,就意味著等待,這種體驗(yàn)該是多么的糟心,估計(jì)千萬個(gè)草泥馬都涌上了心頭。那么這個(gè)時(shí)候?yàn)榱藨?yīng)對這種狀況(從開發(fā)的角度來看,這是活生生的改變不了的需求?。?,如果不實(shí)現(xiàn)它,產(chǎn)品經(jīng)理會放過廣大的Android人么?(唉,又借機(jī)黑了一把產(chǎn)品,這個(gè)不是天敵的天敵!)Activity的生命周期方法,就是在諸如此類的客觀條件下誕生了,這是現(xiàn)實(shí)的需要,不是產(chǎn)品瞎BB的結(jié)果!那么一起來看看Activity都有哪些可愛的生命周期方法。


1.當(dāng)頁面變得可見時(shí)的生命周期方法--onStart()


????????比如一個(gè)App在啟動時(shí),即當(dāng)前的Activity對于用戶來說,是可以被看到的,那么這個(gè)時(shí)候,Activity的?onStart()就會被調(diào)用,它用來告訴系統(tǒng),當(dāng)前的Activity已經(jīng)可以被用戶看到了,如果要準(zhǔn)備做點(diǎn)啥,就可以準(zhǔn)備做了,至于是不是真的要做點(diǎn)啥,這個(gè)得用戶說了算。


2.用戶在頁面上點(diǎn)點(diǎn),會調(diào)用的生命周期方法--onResume()


????????當(dāng)用戶真的在Activity上點(diǎn)點(diǎn)點(diǎn)的時(shí)候,就和Activity中的元素發(fā)生了交互,這個(gè)時(shí)候會調(diào)用一個(gè)新的方法onResume(),這個(gè)方法會告訴系統(tǒng),用戶正在和頁面發(fā)生交互。


3.當(dāng)頁面只是可見,不能交互,調(diào)用的生命周期方法-onPause()


?????????onPause()被調(diào)用時(shí)Activity可能依然對用戶全部可見,如多窗口模式下沒有獲得焦點(diǎn)時(shí),可見一個(gè)View只所以能獲取到焦點(diǎn),是因?yàn)榻裹c(diǎn)已經(jīng)在當(dāng)前的Activity中時(shí),才有可能獲取到。如果是在onResume()中申請資源在onPause()中釋放資源的是不可取的。比如上面提到的多窗口模式下,假如用戶操作一番后,有回到當(dāng)前的Activity,并且在頁面可見時(shí),獲取的資源,就是頁面中要展示的東西,那就尷尬了,這就產(chǎn)生了一個(gè)大BUG。


4.頁面完全不可見時(shí),會調(diào)用的生命周期方法-onStop()


????????停停停,別演了!這里有問題,我不想看了,這就好比在演戲,突然被導(dǎo)演喊停,不想看到演員,那么這個(gè)時(shí)候就得停止演戲了,得Stop。在Activity中,當(dāng)onStop()被調(diào)用時(shí),表示用戶(其實(shí)也是一個(gè)導(dǎo)演,不想看到當(dāng)前的頁面,或者說有了新的操作)做了新的操作,?Activity已經(jīng)完全不可見了。那么這個(gè)時(shí)候就應(yīng)該少做消耗資源的事情了。比如停止包含動畫在內(nèi)的UI 更新,盡量釋放暫時(shí)不用的資源,停止視頻、音頻的播放等。對于stopped 的Activity,系統(tǒng)隨時(shí)可能殺掉包含這個(gè)Activity的進(jìn)程,如果沒有合適的機(jī)會可以在onStop()中保存一些數(shù)據(jù)。

????上面說的是用戶的操作讓頁面完全不可見,并且最終被銷毀了。但如果是因?yàn)锳ndroid自身的原因呢,比如Android的內(nèi)存不夠,當(dāng)前的Activity由于不可見被殺死了,那該如何應(yīng)對?

????這個(gè)時(shí)候,系統(tǒng)就有專門的方法,記錄這個(gè)實(shí)例曾經(jīng)存在過,在用戶重新回到這個(gè)Activity時(shí),第一步是重新創(chuàng)建一個(gè)新的實(shí)例,然后將之前保存好的實(shí)例狀態(tài)傳遞給這個(gè)新的實(shí)例。記住這是保存實(shí)例狀態(tài)的,也就是保存Activity在被殺死前,是處在那種生命周期的方法中。


4.1恢復(fù)Activity的實(shí)例狀態(tài)


????這個(gè)系統(tǒng)之前保存好的用來恢復(fù)Activity狀態(tài)的數(shù)據(jù)被稱為實(shí)例狀態(tài)(Instance state)。

????實(shí)例狀態(tài)如何存儲數(shù)據(jù)呢?是以鍵值對的形式存儲在Bundle 對象中的,實(shí)際上在上節(jié)談到Intent時(shí)就提到過Bundle,它本質(zhì)上是一個(gè)Map。

????這個(gè)實(shí)例狀態(tài)對象,會恢復(fù)新創(chuàng)建的(實(shí)際上存在過的Activity)Activity的一些值,那么會恢復(fù)哪些?

????很可惜,默認(rèn)系統(tǒng)只能自動存儲和恢復(fù)有ID 的 View 的簡單狀態(tài)(如輸入框的文本,滾動控件的滾動位置,實(shí)際上,也只能恢復(fù)這些帶有ID的對象),對于那些復(fù)雜的數(shù)據(jù),即使能存儲起來,要恢復(fù)的話,也會出問題,記住,這些數(shù)據(jù)的操作是在主線程中的,如果在主線程中序列化或反序列化Bundle對象既消耗時(shí)間又消耗系統(tǒng)進(jìn)程內(nèi)存,搞不好還會發(fā)生程序無響應(yīng)。因此最好只用它保存簡單、輕量的數(shù)據(jù)。

4.2.onSaveInstanceState()被調(diào)用的時(shí)機(jī)


????對于Build.VERSION_CODES.P及之后的系統(tǒng)該方法會在onStop()之后隨時(shí)可能被調(diào)用,對于之前的系統(tǒng)該方法會在onStop()之前隨時(shí)被調(diào)用。


4.3?onRestoreInstanceState()被調(diào)用的時(shí)機(jī)

如果有實(shí)例狀態(tài)要恢復(fù)那么一定會在onStart()之后被調(diào)用。


4.4onActivityResult()被調(diào)用時(shí)機(jī)

?onResume()之前。目標(biāo)Activity沒有顯式返回任何結(jié)果或者崩潰,那么resultCode 就會是RESULT_CANCELED。


5.一些要特別注意的點(diǎn)

????????在保存實(shí)例狀態(tài)之后恢復(fù)實(shí)例狀態(tài)之前的一些操作(如Fragment 的事務(wù)提交)是不允許的,Android 系統(tǒng)會不惜一切代價(jià)避免狀態(tài)丟失。


????????Activity#onCreate()方法中提交事務(wù)是沒問題的,因?yàn)槟憧梢栽诶锩娓鶕?jù)保存的狀態(tài)重建,但是在其他生命周期回調(diào)中提交事務(wù)就可能會出現(xiàn)問題了。FragmentActivity#onPostResume()方法中調(diào)用了FragmentActivity#onResumeFragments()方法完成其關(guān)聯(lián)的所有的Fragment 的 resume 事件的分發(fā),執(zhí)行完這兩個(gè)方法 Activity 和它關(guān)聯(lián)的所有 Fragment 才算真正的 resumed,才算恢復(fù)了狀態(tài),才可以提交事務(wù),所以如果非要在Activity#onCreate()之外的回調(diào)中提交事務(wù)那么FragmentActivity#onPostResume()和FragmentActivity#onResumeFragments()是最好的選擇。避免在異步的回調(diào)中提交事務(wù): 因?yàn)樵谶@些回調(diào)執(zhí)行的時(shí)候很難確定當(dāng)前 Activity 正處于什么生命周期狀態(tài),而且突然地提交事務(wù)更改大量 UI 會產(chǎn)生糟糕的用戶體驗(yàn),所以如果遇到這樣的場景可以考慮換一種實(shí)現(xiàn)思路,不要隨便使用commitAllowingStateLoss()方法

????????如非必須,避免使用多層嵌套的Fragment,否則容易出現(xiàn) Bug。原來多層嵌套Fragment是不被提倡的,這種嵌套在Activity被意外回收時(shí),要恢復(fù)到之前的狀態(tài),是非常困難的,這些并不是程序無法控制,而是Android本身所存在的客觀事實(shí)!

????Activity的生命周期方法,遠(yuǎn)不止這些,特別是依賴于Activity的Fragment,就更不一樣了。但大師兄相信,上面所說到的一些方法,才是一些特定情況下會發(fā)生的,也是必須注意的點(diǎn)。那么Android系統(tǒng),又是如何管理Activity的,特別是一個(gè)App中會有很多Activity的情況,為了避免意外的發(fā)生,或者說為了更好的管理Activity。下節(jié)中,大師兄同樣會以一個(gè)文科生的視角來解讀系統(tǒng)是如何管理Activity的知識!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容