目前項(xiàng)目采用單 Activity 模式,頁(yè)面采用 Jetpack Navigation 導(dǎo)航
布局如下:Splash -> Home -> Detail
之前的設(shè)計(jì)邏輯
Activity
不做任何事情
ViewModel
持有 LiveData 類型變量 hasSDKInit,根據(jù)SDK初始化成功與否設(shè)置 true 或者 false
Splash
- 通過 parentActivity 實(shí)例化一個(gè) ViewModel,并在 ViewModel 的構(gòu)造函數(shù)中 初始化 SDK
- 監(jiān)聽 hasSDKInit, true ==》 跳轉(zhuǎn) Home,false ==》顯示錯(cuò)誤信息
Home
調(diào)用 SDK 實(shí)現(xiàn)相關(guān)功能。
發(fā)現(xiàn)有異常情況后調(diào)查生命周期
模擬APP被強(qiáng)殺可以去開發(fā)者選項(xiàng)打開
不保留活動(dòng)
-
APP 打開
Splash onAttach Splash onCreate Activity onCreate Splash onCreateView Splash onViewStateRestored Splash onStart Activity onStart Activity onResume Splash onResume Splash onPause Splash onStop Home onAttach Home onCreate Home onCreateView Home onViewStateRestored Home onStart Splash onDestroy Splash onDetach Home onResume -
APP 進(jìn)入后臺(tái),相關(guān)生命周期:
Home onPause Activity onPause Home onStop Activity onStop Home onSaveInstanceState Activity onSaveInstanceState -
APP 在后臺(tái)被殺
Home onDestroy Home onDetach Activity onDestroy -
APP在后臺(tái)被殺后再次進(jìn)入前臺(tái)
Home onAttach Home onCreate Activity onCreate Home onCreateView Home onViewStateRestored Home onStart Activity onStart Activity onResume Home onResume
錯(cuò)誤原因分析及生命周期理解
很明顯,被殺后與 Activity 生命周期關(guān)聯(lián)的 ViewModel 也結(jié)束了,與新打開 APP 的區(qū)別是,這時(shí)候是沒有通過 Splash 去 初始化 SDK 的, Home 直接調(diào)用一個(gè)沒有初始化的SDK 實(shí)例當(dāng)然就報(bào)錯(cuò)了。
解決辦法
預(yù)備知識(shí)
ViewModel 是與初始化它的 ViewLifeCycleOwner 唯一綁定的,全局唯一,不會(huì)重復(fù)實(shí)例化
ViewModel 是生命周期感知的,但是在 onDestroy 中做了判斷,如果是因?yàn)轭愃茩M豎屏、黑暗模式、語(yǔ)言等ConfigChanged導(dǎo)致的銷毀,ViewModel 是不會(huì)被銷毀的。另外很重要的一點(diǎn)
無論在什么情況下,Activity 的 onCreate 一定在 Fragment 的 onCreateView 之前。
這樣我們把 SDK 的初始化挪到 Activity::onCreate就行了。
Fragment 中通過``ViewModelProvider(requireActivity()).get(GlobalViewModel::class.java)`獲取。
把 init SDK 放到 ViewModel 的構(gòu)造函數(shù)中,并將 initSDK 方法私有化,不允許從外部調(diào)用
context 通過新建ViewModelFactory類傳入。
Google 官方不推薦 ViewModel 持有任何形式的 Context,如果確實(shí)要用,可以考慮單例或者 AndroidViewModel
Splash 頁(yè)面監(jiān)聽 hasSDKInit 邏輯不變。
Home 頁(yè)面原有邏輯不變,必要時(shí)加上對(duì) hasSDKInit 的監(jiān)聽,true 才可進(jìn)行后續(xù)操作。
而,如果SDK 的 init 不依賴網(wǎng)絡(luò)等其他因素,默認(rèn)情況下因?yàn)樗姓{(diào)用都在主線程,那么 就不用做監(jiān)聽。