介绍
ViewModel属于ACC框架组件之一,用以解决数据持久与共享问题,此外,也将数据的相关行为从UI中分离出来。
前言
对于ViewModel的使用以及原理,可能需要对Lifecycle和LiveData有一些理解,不然可能会影响对某些内容的理解。以下为可参考资料。
正文
案例
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;
}
}
页面如下图
ViewModel.jpg
描述:LiveData持有String数据初始为hi,A和Bfragment分别从ViewModel中获取LiveData并监听其中数据,在Activity上有一按钮,每次点击更新String数据为其本身加上"~"。
行为:单机几次按钮,来回切换A和B按钮,可以看到数据在Fragment间都是最新的(图不贴,懒),翻转屏幕,再次观察,日志如下图
viewmodel日志.jpg
从日志图中可得到的信息如下:
- Activity与Fragment被重建
- LiveData对Fragment的绑定关系被重建
- ViewModel没有被重建,持有原来的数据对象
从运行情况可以得知: - 数据保持共享
ViewModel是如何做到数据持久化以及数据共享的?以下为讲解
提醒
由于版本问题,ViewModel对较低版本的SDK做了兼容,因此在实现原理上分有两种做法。在此提前点明情况,方便以下的叙述顺序流畅。
原理一(此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) {
// 获取当前程序所依托的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其实规划了自身将被如何存储
当前位置
ViewModelProviders.of()
- ViewModelProvider()
-- ViewModelStores.of()
public static ViewModelStore of(@NonNull FragmentActivity activity) {
// 当前SDK下,运行到这里就返回了,证据在下一张代码引用图
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
return holderFragmentFor(activity).getViewModelStore();
}
public class FragmentActivity extends BaseFragmentActivityApi16 implements
ViewModelStoreOwner,
既然FragmentActivity实现了ViewModelStoreOwner,那么对应的获取方式如下
当前位置
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;
}
可见,FragmentActivity自身是持有ViewModelStore
以上构造出了ViewModelProvider,紧接着去获取具体的ViewModel
当前位置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) {
}
}
// 创建viewModel,factory为AndroidViewModelFactory
viewModel = mFactory.create(modelClass);
// 将ViewModel与类名绑定,并保存
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
以上代码就获取到了具体的ViewModel,其中AndroidViewModelFactory.create()代码仅仅是通过反射创建了ViewModel实例并捕捉了异常。先做个小结:
- ViewModelProvider持有ViewModelStore信息
- VIewModelStore规划自身的提取方式并持有ViewModel信息
- 通过类作为key获取具体的ViewModel实现共享
我们知道,在屏幕旋转时,如果没有对Activity做相应的配置更变设置,Activity是会被重建的,而Activity被销毁时,相应持有的数据理应被释放。那ViewModel是如何逃过一劫的?
思考
在源码看到这的时候,实际上正面线索已无法跟踪,因为在庞大的Activity架构之中,很难快速地找到事件发源地。那现在,如何找到ViewModel持久的线索呢?在当前条件下,FragmentActivty是直接持有了ViewModelStore的信息,那么在配置更变需要重建时,必定要对ViewModelStore做一些处理,因此选择了跟踪FragmentActity.mViewModelStore。
果然,定位到了以下位置
FragmentActivity
public final Object onRetainNonConfigurationInstance() {
.......
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = mViewModelStore;
nci.fragments = fragments;
return nci;
}
在配置更改需要重建页面的时候,系统会去保存现场以便恢复,在这个函数中,ViewModelStore被作为状态之一被保存在NonConfigurationInstances之中。依次类推,有保存就有取出,继续跟踪,定位到了以下位置
FragmentActivity
protected void onCreate(@Nullable Bundle savedInstanceState) {
.....
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
.....
}
可见,在生命周期onCreate,备份的ViewModelStore被取出。因此,当重建后,再次通过ViewModelProvider.get()去获取ViewModel时候,会直接获取到此ViewModelStore并取出ViewModel,不会再通过AndroidVIewModelFactory重建ViewModel。
这里小结一下:
- 在配置更变需要重建页面时,ViewModelStore会在重建前交由NonConfigurationInstances保管,并在重建后取出恢复。
以上,就是ViewModel在高SDK下的数据共享与持久化的原理。
接下来,是适配低版本的。
原理二 (实例SDK为25)
之前说过,ViewModelStore规划了自身将被如何存储,而且差异也在于低版本的SDK的Activity的各父类并不是ViewModelStoreOwner,回看代码ViewModelStore.of()
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
// 当前SDK下,运行到此返回
return holderFragmentFor(activity).getViewModelStore();
}
以上代码从HolderFragment取得ViewModelStore
当前位置
HolderFragment
private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
return sHolderFragmentManager.holderFragmentFor(activity);
}
当前位置
HolderFragement.HolderFragmentManager
HolderFragment holderFragmentFor(FragmentActivity activity) {
FragmentManager fm = activity.getSupportFragmentManager();
// 查找合适的HolderFragment
HolderFragment holder = findHolderFragment(fm);
// 查找到返回
if (holder != null) {
return holder;
}
// 通过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静态对象HolderFragmentManager,持有HolderFragment对象与key的对应管理。这里因为HolderFragment被注入,需要看一下初始工作。
当前位置HolderFragment
private ViewModelStore mViewModelStore = new ViewModelStore();
public HolderFragment() {
setRetainInstance(true);
}
新的HolderFragment新建时自身持有了ViewMolderStore,之前通过ViewModelStores.of()获取的,就是这个ViewMolderStore。
能区别出,在原理一种,具有生命周期的对象,本身会持有ViewModelStore,而在原理二中,会通过注入HolderFrament,去间接持有ViewModelStore。其他流程是一致的。
现在,就还剩一个问题,简介持有的ViewModelStore,如何保持持久化?
注意到,在初始化HolderFragment是,设置了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 销毁-重建时控制是否是有fragment实例。仅在fragment不在back stack时生效。当mRetainInstance设置为trues时,生命周期表现行为与重建时有轻微不同。
简单来说,HolderFragment并没有被销毁,而当再次通过key去取出对应的HolderFragment时,就能取出。
至于HolderFragment为什么没有被销毁,那就需要了解FragmentManager如何去管理Fragment了,这就扯远了。
总结
通过以上的梳理分析,算是讲明了ViewModel的数据如何共享以及持久化,一下为要点:
- 通过ViewModelProvider持有ViewModelStore和Factory,并主要用来获取对应的ViewModelStore
- ViewModelStore存储了key-value形势的类与ViewModel的对应,实现了数据的共享,Factory负责在需要时创建出ViewModel
- ViewModelStore被Activity或Fragment持有, 或通过注入HolderFragment间接持有
- Activity因配置原因销毁-重建时,ViewModelStore被NonConfigurationInstances保存或被HolderFragment保存,再此需求时从从保存处恢复。
简单原理图
ViewModel原理-2.png
细节
当前位置
HolderFragment
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sHolderFragmentManager.holderFragmentCreated(this);
}
当前位置
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销毁-重建状态下,虽然ViewModelStore跟随HolderFragment被保存了,但是此时的与HolderFragment绑定的Activity或Fragment已不再是当时候的对象,因此,会存在内存泄漏问题。因此,在HolderFragment生命周期onCreate()里解决这一问题。
注意到,HolderFragment与Activity或Fragment间的对应关系链已不存在,那么再去获取对应的HolderFragment是,会通过holderFragmentFor() ->findHolderFragment() 找到,如下图
viewModel获取Holder.jpg
网友评论