目錄:
1. ViewModel是解決什么問題的?
2. ViewModel的特點(diǎn)
3. ViewModel的特點(diǎn)1生命周期
4. ViewModel的特點(diǎn)2不持有UI層引用
5. ViewModel的特點(diǎn)3.Fragment間數(shù)據(jù)共享
6. ViewModel的使用
7. ViewModel原理源碼分析(ViewModel為什么在旋轉(zhuǎn)屏幕后不會(huì)丟失狀態(tài))
8. ViewModel對(duì)比onSaveInstanceState()
9. ViewModel內(nèi)存泄露

ViewModel相關(guān)的問 題:
1.ViewModel為什么在旋轉(zhuǎn)屏幕后不會(huì)丟失狀態(tài)
2.ViewModel的原理,為什么可以在Activity銷毀后保存數(shù)據(jù)
3.ViewModel怎么實(shí)現(xiàn)自動(dòng)處理生命周期?ViewModel的生命周期是怎么樣的?
4.為什么不同的Fragment使用相同的Activity對(duì)象來獲取ViewModel,可以輕易的實(shí)現(xiàn)ViewModel共享?
5.ViewModel在Activity初始化與在Fragment中初始化,有什么區(qū)別?
6.ViewModel是如何創(chuàng)建出來的?
7.ViewModel是怎么實(shí)現(xiàn)雙向數(shù)據(jù)綁定的。這個(gè)vm指的是mvvm里面的?
1. ViewModel是解決什么問題的?
在詳細(xì)介紹ViewModel前,先來看下背景和問題點(diǎn)。
1). 數(shù)據(jù)持久保持: Activity可能會(huì)在某些場景(例如屏幕旋轉(zhuǎn))銷毀和重新創(chuàng)建界面,那么存儲(chǔ)在其中的界面相關(guān)數(shù)據(jù)都會(huì)丟失。例如,界面含用戶信息列表,因配置更改而重新創(chuàng)建 Activity 后,新 Activity 必須重新請(qǐng)求用戶列表,這會(huì)造成資源的浪費(fèi)。能否直接恢復(fù)之前的數(shù)據(jù)呢?對(duì)于簡單的數(shù)據(jù),Activity 可以使用 onSaveInstanceState() 方法保存 然后從 onCreate() 中的Bundle恢復(fù)數(shù)據(jù),但此方法僅適合可以序列化再反序列化的少量數(shù)據(jù)(IPC對(duì)Bundle有1M的限制),而不適合數(shù)量可能較大的數(shù)據(jù),如用戶信息列表或位圖。 那么如何做到 因配置更改而新建Activity后的數(shù)據(jù)恢復(fù)呢?
2). 內(nèi)存泄露: UI層(如 Activity 和 Fragment)經(jīng)常需要通過邏輯層(如MVP中的Presenter)進(jìn)行異步請(qǐng)求,可能需要一些時(shí)間才能返回結(jié)果,如果邏輯層持有UI層應(yīng)用(如context),那么UI層需要管理這些請(qǐng)求,確保界面銷毀后清理這些調(diào)用以避免潛在的內(nèi)存泄露,但此項(xiàng)管理需要大量的維護(hù)工作。 那么如何更好的避免因異步請(qǐng)求帶來的內(nèi)存泄漏呢?
這時(shí)候ViewModel就閃亮出場了——ViewModel用于代替MVP中的Presenter,為UI層準(zhǔn)備數(shù)據(jù),用于解決上面兩個(gè)問題。
-
ViewModel: 從界面控制中分離出視圖數(shù)據(jù)所有權(quán)的操作更容易且更高效。 - Activity 的
onSaveInstanceState()方法從onCreate()捆綁包恢復(fù)其數(shù)據(jù),僅限支持序列化的少量數(shù)據(jù)。 -
ViewModel非常適合在用戶正活躍地使用應(yīng)用時(shí)存儲(chǔ)和管理界面相關(guān)數(shù)據(jù)。它支持快速訪問界面數(shù)據(jù),并且有助于避免在發(fā)生旋轉(zhuǎn)、窗口大小調(diào)整和其他常見的配置變更后從網(wǎng)絡(luò)或磁盤中重新獲取數(shù)據(jù)。
2. ViewModel的特點(diǎn)
ViewModel可以這么理解: 它是介于View(視圖)和Model(數(shù)據(jù)模型)之間的一個(gè)東西。它起到了橋梁的作用,使視圖和數(shù)據(jù)既能分離,也能保持通信
1). 保存數(shù)據(jù) ! ViewModel 主要用于存儲(chǔ) UI 數(shù)據(jù)
2). 自動(dòng)管理: 生命周期感知的數(shù)據(jù)。
3). **共享 ViewModel 定義, **Fragment 共享數(shù)據(jù)
3.生命周期長于Activity
Android中的ViewModel是一個(gè)可以用來存儲(chǔ)UI相關(guān)的數(shù)據(jù)的類。ViewModel的生命周期會(huì)比創(chuàng)建它的Activity、Fragment的生命周期長
看到在因屏幕旋轉(zhuǎn)而重新創(chuàng)建Activity后,ViewModel對(duì)象依然會(huì)保留。 只有Activity真正Finish的時(shí)ViewModel才會(huì)被清除。
也就是說,因系統(tǒng)配置變更Activity銷毀重建,ViewModel對(duì)象會(huì)保留并關(guān)聯(lián)到新的Activity。而Activity的正常銷毀(系統(tǒng)不會(huì)重建Activity)時(shí),ViewModel對(duì)象是會(huì)清除的。

4. 不持有UI層引用
問題: viewModel怎么實(shí)現(xiàn)自動(dòng)處理生命周期?
我們知道,在MVP的Presenter中需要持有IView接口來回調(diào)結(jié)果給界面。
而ViewModel是不需要持有UI層引用的,那結(jié)果怎么給到UI層呢?答案就是使用上一篇中介紹的基于觀察者模式的LiveData。 并且,ViewModel也不能持有UI層引用,因?yàn)閂iewModel的生命周期更長。
所以,ViewModel不需要也不能 持有UI層引用,那么就避免了可能的內(nèi)存泄漏,同時(shí)實(shí)現(xiàn)了解耦。這就解決了第二個(gè)問題。
5. ViewModel的特點(diǎn)3之Fragment間數(shù)據(jù)共享
Activity 中的多個(gè)Fragment需要相互通信是一種很常見的情況。假設(shè)有一個(gè)ListFragment,用戶從列表中選擇一項(xiàng),會(huì)有另一個(gè)DetailFragment顯示選定項(xiàng)的詳情內(nèi)容。在之前 你可能會(huì)定義接口或者使用EventBus來實(shí)現(xiàn)數(shù)據(jù)的傳遞共享。
在上面ViewMoel的生命周期也提到了 ViewModel只會(huì)在Activity存活時(shí),只會(huì)創(chuàng)建一次,因此在同一個(gè)Activity中可以在多個(gè)Fragment中共享ViewModel中數(shù)據(jù)。
現(xiàn)在就可以使用 ViewModel 來實(shí)現(xiàn)。這兩個(gè) Fragment 可以使用其 Activity 范圍共享 ViewModel 來處理此類通信,如以下示例代碼所示:
//ViewModel
public class SharedViewModel extends ViewModel {
//被選中的Item
private final MutableLiveData<UserContent.UserItem> selected = new MutableLiveData<UserContent.UserItem>();
public void select(UserContent.UserItem user) {
selected.setValue(user);
}
public LiveData<UserContent.UserItem> getSelected() {
return selected;
}
}
//ListFragment
public class MyListFragment extends Fragment {
...
private SharedViewModel model;
...
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//獲取ViewModel,注意ViewModelProvider實(shí)例傳入的是宿主Activity
model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
adapter.setListner(new MyItemRecyclerViewAdapter.ItemCLickListner(){
@Override
public void onClickItem(UserContent.UserItem userItem) {
model.select(userItem);
}
});
}
}
//DetailFragment
public class DetailFragment extends Fragment {
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
TextView detail = view.findViewById(R.id.tv_detail);
//獲取ViewModel,觀察被選中的Item
SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
model.getSelected().observe(getViewLifecycleOwner(), new Observer<UserContent.UserItem>() {
@Override
public void onChanged(UserContent.UserItem userItem) {
//展示詳情
detail.setText(userItem.toString());
}
});
}
}
問題: ViewModel在Activity初始化與在Fragment中初始化,有什么區(qū)別?
共享的基礎(chǔ):
Activity和Fragment創(chuàng)建ViewModel時(shí)的區(qū)別在于:
分別會(huì)創(chuàng)建一個(gè)ViewModelProvider對(duì)象,這個(gè)不同的ViewModelProvider對(duì)象中又封裝了相同的ViewModelStore對(duì)象和factory對(duì)象,
通過相同的ViewModelStore,又會(huì)獲取到相同的ViewModel對(duì)象,這也是Activity和Fragment通過ViewModel通訊的基礎(chǔ)。
生命周期不一樣,ViewModel和Activity綁定過程:
ViewModel和Fragment綁定過程:
問題: ViewModel 如何實(shí)現(xiàn)不同的作用域
ViewModel 內(nèi)部會(huì)為不同的 ViewModel 宿主分配不同的 ViewModelStore 映射表,不同宿主是從不同的數(shù)據(jù)源來獲取 ViewModel 的實(shí)例,因而得以區(qū)分作用域。
6. ViewModel的使用
6.1 ViewModel 的創(chuàng)建三種方式
方法 1: ViewModelProvider 是創(chuàng)建 ViewModel 的工具類
方法 2: 使用 Kotlin by 委托屬性,本質(zhì)上是間接使用了 ViewModelProvider:
方法 3: Hilt 提供了注入部分 Jetpack 架構(gòu)組件的支持
** ViewModel 定義,組合了 LiveData**
class NameViewModel : ViewModel() {
val currentName: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
}
class MainActivity : AppCompatActivity() {
private val model: NameViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// LiveData 觀察者
val nameObserver = Observer<String> { newName ->
// 更新視圖
nameTextView.text = newName
}
// 注冊 LiveData 觀察者,this 為生命周期宿主
model.currentName.observe(this, nameObserver)
// 修改 LiveData 數(shù)據(jù)
button.setOnClickListener {
val anotherName = "John Doe"
model.currentName.value = anotherName
}
}
}
問題: 4.viewModel是怎么實(shí)現(xiàn)雙向數(shù)據(jù)綁定的?
一般是databing實(shí)現(xiàn)雙向數(shù)據(jù)
ViewModel里面存放MutableLiveData(model)
然后更新view:post。然后Acrtivity通過觀察者模式監(jiān)聽!??!
7. ViewModel源碼分析
7.1 ViewModel 結(jié)構(gòu)
-
ViewModel:抽象類,有
onClear()方法以供重寫,自定義數(shù)據(jù)清空。 -
ViewModelStore: 持有管理
ViewModel的 HashMap。 -
ViewModelStoreOwner:
ComponentActivity和Fragment實(shí)現(xiàn)此接口,約束ViewModelStore的作用域。 -
ViewModelProvider: 創(chuàng)建
ViewModel。
獲取ViewModel:
ActivityThread
ActivityClientRecord
ViewModelProvider
NonConfigurationInstance
ViewModelStore
HashMap
7.2 Activity 流程
7.2.1 ?Activity 關(guān)聯(lián) ViewModel 類圖

Activity 重建 ViewModel 依然存在
深度的原理:
ActivityThread 中的 ActivityClientRecord 的屬性 lastNonConfigurationInstances 不受重建影響,在新建 activity 時(shí),將 lastNonConfigurationInstances 入?yún)⒌?attach() 方法中,所以 activity 重建后,依然能獲取到之前的配置緩存 lastNonConfigurationInstances ,拿到其中的緩存 ViewModelStore
總結(jié): ActivityThread 中的 ActivityClientRecord 是不受 activity 重建的影響,那么ActivityClientRecord中l(wèi)astNonConfigurationInstances也不受影響,那么其中的Object activity也不受影響,那么ComponentActivity中的NonConfigurationInstances的viewModelStore不受影響,那么viewModel也就不受影響了。
問題: 修改系統(tǒng)語言會(huì)發(fā)生什么?
進(jìn)程會(huì)被殺死**
問題: 為什么說旋轉(zhuǎn)屏幕ViewModel 不會(huì)丟數(shù)據(jù)。因?yàn)殡m然走了onDestroy 但是內(nèi)部判斷了是否旋轉(zhuǎn)屏幕
在Android中,當(dāng)設(shè)備的屏幕方向(橫屏與豎屏)發(fā)生變化時(shí),當(dāng)前的Activity會(huì)經(jīng)歷一系列的生命周期回調(diào)。這主要是因?yàn)槠聊恍D(zhuǎn)通常會(huì)導(dǎo)致當(dāng)前的Activity被銷毀并重新創(chuàng)建,以適應(yīng)新的屏幕配置
- 為啥旋轉(zhuǎn)還能保存數(shù)據(jù)?
判斷了是配置變化,如旋轉(zhuǎn)屏幕等,等到真正銷毀才清空
答案:這里我們又學(xué)到了Activity的兩個(gè)跟生命周期相關(guān)的函數(shù)調(diào)用:onRetainNonConfigurationInstance和getLastNonConfigurationInstance。
- Activity實(shí)現(xiàn)了ViewModelStoreOwner接口,創(chuàng)建了ViewModelStore對(duì)象。
- 當(dāng)Activity意外銷毀時(shí),onRetainNonConfigurationInstance函數(shù)被回調(diào),在此函數(shù)中對(duì)ViewModelStore對(duì)象進(jìn)行了保存。
- 當(dāng)Activity重建時(shí),onCreate方法中會(huì)先獲取getLastNonConfigurationInstance,如果其中的ViewModelStore對(duì)象不為空,就直接引用,不再重新創(chuàng)****建ViewModelStore對(duì)象了。
橫豎平:能夠保存數(shù)據(jù)的原理:ViewModelStore,里面用的hashmap

=============================================================
問題: 旋轉(zhuǎn)屏幕會(huì)執(zhí)行ondestory方法嗎?會(huì)執(zhí)行
根本原因:AMS有關(guān),因?yàn)樵趫?zhí)行onDestroy時(shí),ActivityClientRecord的lastNonConfigurationInstances從哪里來,答案是在onDestroy時(shí),會(huì)保存Activity的retainNonConfigurationInstances()方法返回值。
從ActivityClientRecord持有一條到ViewModelStore引用鏈****,所以當(dāng)Activity被銷毀時(shí),ViewModelStore不會(huì)被銷毀,
而Activity的reLaunch并不會(huì)銷毀對(duì)應(yīng)的ActivityClientRecord,下次仍然會(huì)復(fù)用ActivityClientRecord,進(jìn)而復(fù)用保存的ViewModelStore,這樣就解釋通了;
Activity是不同的實(shí)例。但是獲取到的ViewModelStore是同一個(gè)。
答案總結(jié):1).進(jìn)來創(chuàng)建了ViewModel,如果ViewModelStore還在,用同一個(gè)
2).onstop的時(shí)候,viewModelStore保存了數(shù)據(jù),通過一個(gè)新方法
3).destory的時(shí)候,viewModelstore是保存在ActivityClientRecord里面,viewModelStore在橫豎平的時(shí)候不會(huì)銷毀,但是在正常destory的時(shí)候會(huì)清除
和上面一樣,AMS的ActivityClientRecord
setRetainInstance (true)
那我有個(gè)問題,Actiivty銷毀之后,一直用的是第一次的VIewModel。那么它們的數(shù)據(jù)豈不是用舊數(shù)據(jù).
只要在切換橫豎屏才保持,否則ondestory的時(shí)候clear。把VIewModerStore清除了。
FragmentManager類的destroy()方法里面清除
Activity在onDestroy會(huì)嘗試對(duì)ViewModelStore清空。如果是由于ConfigurationChanged帶來的Destroy則不進(jìn)行清空
當(dāng) Activity 處于前臺(tái)的時(shí)候被銷毀了,那么得到的 ViewModel 是之前實(shí)例過的 ViewModel;
如果 Activity 處于后臺(tái)時(shí)被銷毀了,那么得到的 ViewModel 不是同一個(gè)。舉例說,
如果 Activity 因?yàn)榕渲冒l(fā)生變化而被重建了,那么當(dāng)重建的時(shí)候,ViewModel 是之前的實(shí)例;如果因?yàn)殚L期處于后臺(tái)而被銷毀了,那么重建的時(shí)候,ViewModel 就不是之前的實(shí)例了。

問題: 怎么自動(dòng)管理的?
-
ComponentActivity監(jiān)聽onDestroy(),清理 -
Fragment在FragmentActivity的onDestroy()會(huì)清理。
Fragment能拿 Activity 的ViewModel么?
- 能 ,畢竟
FragmentManger那向上管理,其實(shí)取的就是上層FragmentActivity的ViewModelStore
問題: ViewModelStore是個(gè)啥?
-
ViewModelStore知道么?- 知道 ,一個(gè)
map就是干。
- 知道 ,一個(gè)
-
知道怎么創(chuàng)建的么?
- 內(nèi)部
factory反射就是干。
- 內(nèi)部
8. ViewModel對(duì)比onSaveInstanceState()

9.ViewModel 的內(nèi)存泄漏問題
ViewModel 的內(nèi)存泄漏是指 Activity 已經(jīng)銷毀,但是 ViewModel 卻被其他組件引用。這往往是因?yàn)閿?shù)據(jù)層是通過回調(diào)監(jiān)聽器的方式返回?cái)?shù)據(jù),并且數(shù)據(jù)層是單例對(duì)象或者屬于全局生命周期,所以導(dǎo)致 Activity 銷毀了,但是數(shù)據(jù)層依然間接持有 ViewModel 的引用。
如果 ViewModel 是輕量級(jí)的或者可以保證數(shù)據(jù)層操作快速完成,這個(gè)泄漏影響不大可以忽略。但如果數(shù)據(jù)層操作并不能快速完成,或者 ViewModel 存儲(chǔ)了重量級(jí)數(shù)據(jù),就有必要采取措施。例如:
- 方法 1: 在 ViewModel#onCleared() 中通知數(shù)據(jù)層丟棄對(duì) ViewModel 回調(diào)監(jiān)聽器的引用;
- 方法 2: 在數(shù)據(jù)層使用對(duì) ViewModel 回調(diào)監(jiān)聽器的弱引用(這要求 ViewModel 必須持有回調(diào)監(jiān)聽器的強(qiáng)引用,而不能使用匿名內(nèi)部類,這會(huì)帶來編碼復(fù)雜性);
- 方法 3: 使用 EventBus 代替回調(diào)監(jiān)聽器(這會(huì)帶來編碼復(fù)雜性);
- 方法 4: 使用 LiveData 的 Transformations.switchMap() API 包裝數(shù)據(jù)層的請(qǐng)求方法,這相當(dāng)于在 ViewModel 和數(shù)據(jù)層中間使用 LiveData 進(jìn)行通信
第二種泄露的情況:
使用ViewModel的時(shí)候,需要注意的是ViewModel不能夠持有View、Lifecycle、Acitivity引用,而且不能夠包含任何包含前面內(nèi)容的類。因?yàn)檫@樣很有可能會(huì)造成內(nèi)存泄漏。
這張圖也解釋了為什么ViewModel中不能持有Activity、Fragment、view的引用。因?yàn)锳ctivity在重建后是一個(gè)新的對(duì)象,如果ViewModel中持有舊對(duì)象的引用,這個(gè)舊對(duì)象可能就等不到釋放,造成泄漏。
如果確實(shí)需要,應(yīng)該使用applicationcontext,或者使用含有上下文的AndroidViewModel。
產(chǎn)生的原因:數(shù)據(jù)永久保存,shareperfece用的話要context.所以產(chǎn)生了AndroidViewModel
那如果需要使用Context對(duì)象改怎么辦。這時(shí)候我們可以給ViewModel一個(gè)Application。Application是一個(gè)Context,而且一個(gè)應(yīng)用也只會(huì)有Application。
我們自己添加Application?其實(shí)沒必要Google還有一個(gè)AndroidViewModel。這是一個(gè)包含Application的ViewModel。