介紹
ViewModel屬于ACC框架組件之一,用以解決數(shù)據(jù)持久與共享問(wèn)題,此外,也將數(shù)據(jù)的相關(guān)行為從UI中分離出來(lái)。
前言
對(duì)于ViewModel的使用以及原理,可能需要對(duì)Lifecycle和LiveData有一些理解,不然可能會(huì)影響對(duì)某些內(nèi)容的理解。以下為可參考資料。
正文
案例
public class MyData extends LiveData<String> {
private static final String TAG = "T-MyData";
public MyData(){
setValue("hi");
Log.d(TAG, "create new liveData ");
}
@Override
protected void onActive() {
super.onActive();
Log.d(TAG, "onActive ");
}
@Override
protected void onInactive() {
super.onInactive();
Log.d(TAG, "onInactive ");
}
public void changeValue(String value){
setValue(value);
}
}
public class MainActivity extends AppCompatActivity {
private static final String TAG = "T-MainActivity";
private TabLayout nav;
private Fragment nowFragment;
private Fragment[] fs = new Fragment[]{
new AFragment(),
new BFragment()};
private MViewModel mViewModel;
MyData data;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "activity onCreate ");
nav = findViewById(R.id.nav);
nav.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
if (tab.getText().equals("A")){
nowFragment = fs[0];
}else {
nowFragment = fs[1];
}
getSupportFragmentManager().beginTransaction().replace(R.id.container, nowFragment).commit();
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
nav.addTab(nav.newTab().setText("A"));
nav.addTab(nav.newTab().setText("B"));
mViewModel = ViewModelProviders.of(this).get(MViewModel.class);
findViewById(R.id.attack).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyData data = mViewModel.getLiveData();
data.changeValue(data.getValue() + "~");
}
});
}
}
public class AFragment extends Fragment {
View mainView;
private TextView text;
private MViewModel mViewModel;
public AFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
mainView = inflater.inflate(R.layout.fragment_a, container, false);
text = mainView.findViewById(R.id.A_text);
mViewModel = ViewModelProviders.of(getActivity()).get(MViewModel.class);
mViewModel.getLiveData().observe(this, new Observer<String>() {
@Override
public void onChanged(@Nullable String s) {
text.setText("A--" + s);
}
});
return mainView;
}
}
public class BFragment extends Fragment {
private View mainView;
private TextView text;
private MViewModel mViewModel;
public BFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
mainView = inflater.inflate(R.layout.fragment_b, container, false);
text = mainView.findViewById(R.id.B_text);
mViewModel = ViewModelProviders.of(getActivity()).get(MViewModel.class);
mViewModel.getLiveData().observe(this, new Observer<String>() {
@Override
public void onChanged(@Nullable String s) {
text.setText("B--" + s);
}
});
return mainView;
}
}
public class MViewModel extends AndroidViewModel {
private MyData data;
public MViewModel(Application application) {
super(application);
data = new MyData();
}
public MyData getLiveData(){
return data;
}
}
頁(yè)面如下圖

描述:LiveData持有String數(shù)據(jù)初始為hi,A和Bfragment分別從ViewModel中獲取LiveData并監(jiān)聽其中數(shù)據(jù),在Activity上有一按鈕,每次點(diǎn)擊更新String數(shù)據(jù)為其本身加上"~"。
行為:?jiǎn)螜C(jī)幾次按鈕,來(lái)回切換A和B按鈕,可以看到數(shù)據(jù)在Fragment間都是最新的(圖不貼,懶),翻轉(zhuǎn)屏幕,再次觀察,日志如下圖

從日志圖中可得到的信息如下:
- Activity與Fragment被重建
- LiveData對(duì)Fragment的綁定關(guān)系被重建
- ViewModel沒有被重建,持有原來(lái)的數(shù)據(jù)對(duì)象
從運(yùn)行情況可以得知: - 數(shù)據(jù)保持共享
ViewModel是如何做到數(shù)據(jù)持久化以及數(shù)據(jù)共享的?以下為講解
提醒
由于版本問(wèn)題,ViewModel對(duì)較低版本的SDK做了兼容,因此在實(shí)現(xiàn)原理上分有兩種做法。在此提前點(diǎn)明情況,方便以下的敘述順序流暢。
原理一(此SKD為27)
入口
mViewModel = ViewModelProviders.of(this).get(MViewModel.class);
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
// 獲取當(dāng)前程序所依托的Application
Application application = checkApplication(activity);
if (factory == null) {
// 獲取AndroidViewModelFactory,單例
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
// ViewModelStores.of()返回了ViewModelStore
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
這里只要注意,of()返回了ViewModelProvider,其持有ViewModelStore信息和AndroidViewModelFactory。 而ViewModelStore其實(shí)規(guī)劃了自身將被如何存儲(chǔ)
當(dāng)前位置
ViewModelProviders.of()
- ViewModelProvider()
-- ViewModelStores.of()
public static ViewModelStore of(@NonNull FragmentActivity activity) {
// 當(dāng)前SDK下,運(yùn)行到這里就返回了,證據(jù)在下一張代碼引用圖
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
return holderFragmentFor(activity).getViewModelStore();
}
public class FragmentActivity extends BaseFragmentActivityApi16 implements
ViewModelStoreOwner,
既然FragmentActivity實(shí)現(xiàn)了ViewModelStoreOwner,那么對(duì)應(yīng)的獲取方式如下
當(dāng)前位置
FragmentActivity. getViewModelStore()
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
// 為空新建
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
return mViewModelStore;
}
可見,F(xiàn)ragmentActivity自身是持有ViewModelStore
以上構(gòu)造出了ViewModelProvider,緊接著去獲取具體的ViewModel
當(dāng)前位置ViewModelProvider
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
// 類名
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
// 從ViewModelStore中獲取viewModel
ViewModel viewModel = mViewModelStore.get(key);
// 獲取到,返回
if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
} else {
if (viewModel != null) {
}
}
// 創(chuàng)建viewModel,factory為AndroidViewModelFactory
viewModel = mFactory.create(modelClass);
// 將ViewModel與類名綁定,并保存
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
以上代碼就獲取到了具體的ViewModel,其中AndroidViewModelFactory.create()代碼僅僅是通過(guò)反射創(chuàng)建了ViewModel實(shí)例并捕捉了異常。先做個(gè)小結(jié):
- ViewModelProvider持有ViewModelStore信息
- VIewModelStore規(guī)劃自身的提取方式并持有ViewModel信息
- 通過(guò)類作為key獲取具體的ViewModel實(shí)現(xiàn)共享
我們知道,在屏幕旋轉(zhuǎn)時(shí),如果沒有對(duì)Activity做相應(yīng)的配置更變?cè)O(shè)置,Activity是會(huì)被重建的,而Activity被銷毀時(shí),相應(yīng)持有的數(shù)據(jù)理應(yīng)被釋放。那ViewModel是如何逃過(guò)一劫的?
思考
在源碼看到這的時(shí)候,實(shí)際上正面線索已無(wú)法跟蹤,因?yàn)樵邶嫶蟮腁ctivity架構(gòu)之中,很難快速地找到事件發(fā)源地。那現(xiàn)在,如何找到ViewModel持久的線索呢?在當(dāng)前條件下,F(xiàn)ragmentActivty是直接持有了ViewModelStore的信息,那么在配置更變需要重建時(shí),必定要對(duì)ViewModelStore做一些處理,因此選擇了跟蹤FragmentActity.mViewModelStore。
果然,定位到了以下位置
FragmentActivity
public final Object onRetainNonConfigurationInstance() {
.......
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = mViewModelStore;
nci.fragments = fragments;
return nci;
}
在配置更改需要重建頁(yè)面的時(shí)候,系統(tǒng)會(huì)去保存現(xiàn)場(chǎng)以便恢復(fù),在這個(gè)函數(shù)中,ViewModelStore被作為狀態(tài)之一被保存在NonConfigurationInstances之中。依次類推,有保存就有取出,繼續(xù)跟蹤,定位到了以下位置
FragmentActivity
protected void onCreate(@Nullable Bundle savedInstanceState) {
.....
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
.....
}
可見,在生命周期onCreate,備份的ViewModelStore被取出。因此,當(dāng)重建后,再次通過(guò)ViewModelProvider.get()去獲取ViewModel時(shí)候,會(huì)直接獲取到此ViewModelStore并取出ViewModel,不會(huì)再通過(guò)AndroidVIewModelFactory重建ViewModel。
這里小結(jié)一下:
- 在配置更變需要重建頁(yè)面時(shí),ViewModelStore會(huì)在重建前交由NonConfigurationInstances保管,并在重建后取出恢復(fù)。
以上,就是ViewModel在高SDK下的數(shù)據(jù)共享與持久化的原理。
接下來(lái),是適配低版本的。
原理二 (實(shí)例SDK為25)
之前說(shuō)過(guò),ViewModelStore規(guī)劃了自身將被如何存儲(chǔ),而且差異也在于低版本的SDK的Activity的各父類并不是ViewModelStoreOwner,回看代碼ViewModelStore.of()
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
// 當(dāng)前SDK下,運(yùn)行到此返回
return holderFragmentFor(activity).getViewModelStore();
}
以上代碼從HolderFragment取得ViewModelStore
當(dāng)前位置
HolderFragment
private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
return sHolderFragmentManager.holderFragmentFor(activity);
}
當(dāng)前位置
HolderFragement.HolderFragmentManager
HolderFragment holderFragmentFor(FragmentActivity activity) {
FragmentManager fm = activity.getSupportFragmentManager();
// 查找合適的HolderFragment
HolderFragment holder = findHolderFragment(fm);
// 查找到返回
if (holder != null) {
return holder;
}
// 通過(guò)key(activity)取出HolderFragment
holder = mNotCommittedActivityHolders.get(activity);
// 取到返回
if (holder != null) {
return holder;
}
if (!mActivityCallbacksIsAdded) {
mActivityCallbacksIsAdded = true;
activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
}
// 注入HolderFragment
holder = createHolderFragment(fm);
// 綁定HolderFragment和key
mNotCommittedActivityHolders.put(activity, holder);
return holder;
}
以上代碼代碼可知,HolderFragment靜態(tài)對(duì)象HolderFragmentManager,持有HolderFragment對(duì)象與key的對(duì)應(yīng)管理。這里因?yàn)镠olderFragment被注入,需要看一下初始工作。
當(dāng)前位置HolderFragment
private ViewModelStore mViewModelStore = new ViewModelStore();
public HolderFragment() {
setRetainInstance(true);
}
新的HolderFragment新建時(shí)自身持有了ViewMolderStore,之前通過(guò)ViewModelStores.of()獲取的,就是這個(gè)ViewMolderStore。
能區(qū)別出,在原理一種,具有生命周期的對(duì)象,本身會(huì)持有ViewModelStore,而在原理二中,會(huì)通過(guò)注入HolderFrament,去間接持有ViewModelStore。其他流程是一致的。
現(xiàn)在,就還剩一個(gè)問(wèn)題,簡(jiǎn)介持有的ViewModelStore,如何保持持久化?
注意到,在初始化HolderFragment是,設(shè)置了mRetainInstance,如下
/**
* Control whether a fragment instance is retained across Activity
* re-creation (such as from a configuration change). This can only
* be used with fragments not in the back stack. If set, the fragment
* lifecycle will be slightly different when an activity is recreated
*/
public void setRetainInstance(boolean retain) {
mRetainInstance = retain;
}
注釋大意為:在Activity 銷毀-重建時(shí)控制是否是有fragment實(shí)例。僅在fragment不在back stack時(shí)生效。當(dāng)mRetainInstance設(shè)置為trues時(shí),生命周期表現(xiàn)行為與重建時(shí)有輕微不同。
簡(jiǎn)單來(lái)說(shuō),HolderFragment并沒有被銷毀,而當(dāng)再次通過(guò)key去取出對(duì)應(yīng)的HolderFragment時(shí),就能取出。
至于HolderFragment為什么沒有被銷毀,那就需要了解FragmentManager如何去管理Fragment了,這就扯遠(yuǎn)了。
總結(jié)
通過(guò)以上的梳理分析,算是講明了ViewModel的數(shù)據(jù)如何共享以及持久化,一下為要點(diǎn):
- 通過(guò)ViewModelProvider持有ViewModelStore和Factory,并主要用來(lái)獲取對(duì)應(yīng)的ViewModelStore
- ViewModelStore存儲(chǔ)了key-value形勢(shì)的類與ViewModel的對(duì)應(yīng),實(shí)現(xiàn)了數(shù)據(jù)的共享,F(xiàn)actory負(fù)責(zé)在需要時(shí)創(chuàng)建出ViewModel
- ViewModelStore被Activity或Fragment持有, 或通過(guò)注入HolderFragment間接持有
- Activity因配置原因銷毀-重建時(shí),ViewModelStore被NonConfigurationInstances保存或被HolderFragment保存,再此需求時(shí)從從保存處恢復(fù)。
簡(jiǎn)單原理圖

細(xì)節(jié)
當(dāng)前位置
HolderFragment
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sHolderFragmentManager.holderFragmentCreated(this);
}
當(dāng)前位置
HolderFragment.HolderFragmentManager
void holderFragmentCreated(Fragment holderFragment) {
// 獲取作為依托的父fragment
Fragment parentFragment = holderFragment.getParentFragment();
if (parentFragment != null) {
// 釋放父fragment
mNotCommittedFragmentHolders.remove(parentFragment);
parentFragment.getFragmentManager().unregisterFragmentLifecycleCallbacks(
mParentDestroyedCallback);
} else {
//釋放Activity
mNotCommittedActivityHolders.remove(holderFragment.getActivity());
}
}
在Activity銷毀-重建狀態(tài)下,雖然ViewModelStore跟隨HolderFragment被保存了,但是此時(shí)的與HolderFragment綁定的Activity或Fragment已不再是當(dāng)時(shí)候的對(duì)象,因此,會(huì)存在內(nèi)存泄漏問(wèn)題。因此,在HolderFragment生命周期onCreate()里解決這一問(wèn)題。
注意到,HolderFragment與Activity或Fragment間的對(duì)應(yīng)關(guān)系鏈已不存在,那么再去獲取對(duì)應(yīng)的HolderFragment是,會(huì)通過(guò)holderFragmentFor() ->findHolderFragment() 找到,如下圖
