0
点赞
收藏
分享

微信扫一扫

Jetpack学习-3-ViewModel源码分析

十日十月Freddie 2022-04-07 阅读 87

以简单来说,ViewModel是一个抽象类,但它可以以注重生命周期的方式存储和管理界面相关的数据,同时,它可以让数据可在发生屏幕旋转等配置更改后继续留存。下图是ViewModel对应Activity屏幕旋转及重开页面的生命周期图。

ViewModel与LiveData的组合可以将业务代码从Activity/fragment中挪走,是MVVM模式中重要的一环,此节只讨论ViewModel,详细分析ViewModel的实现逻辑。

ViewModel的依赖支持

 //添加ViewModel依赖
    implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'

下面开始最简单的例子来分析ViewModel的逻辑。

1,实现一个最简单的ViewModel类

public class MyViewModel extends ViewModel {

    private String TAG = "MyViewModel";

    public int viewModel_Int = 0;

    @Override
    protected void onCleared() {
        super.onCleared();
        LogUtil.e(TAG, "oncleared");
    }
}

2,页面做简单处理

public class MainActivity extends BaseActivity {
    private String TAG = "ViewModel_Main";

    private TextView tv_local;
    private TextView tv_viewmodel;
    MyViewModel viewModel;

    int local_Int = 0;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_main2;
    }

    @Override
    protected void initView() {
        tv_local = this.findViewById(R.id.tv_local);
        tv_viewmodel = this.findViewById(R.id.tv_viewmodel);
        viewModel = new ViewModelProvider(this).get(MyViewModel.class);
        LogUtil.e(TAG, "activity=" + this);
        LogUtil.e(TAG, "viewmodelstore=" + getViewModelStore());
        LogUtil.e(TAG, "viewmodel=" + viewModel.toString());
        setText();
    }

    @Override
    protected void initData() {

    }

    public void changeClick(View view) {
        viewModel.viewModel_Int++;
        this.local_Int++;
        setText();
    }

    public void setText() {
        tv_local.setText(local_Int+"");
        tv_viewmodel.setText(viewModel.viewModel_Int+"");

        LogUtil.e(TAG, "local-Int=" + local_Int + "   viewmodel-Int=" + viewModel.viewModel_Int);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        LogUtil.e(TAG, "onDestroy");
    }
}

3,最终有了如下日志

进入APP
ViewModel_Main: activity=com.sun.livedata.MainActivity@69b8bab
ViewModel_Main: viewmodelstore=androidx.lifecycle.ViewModelStore@60ac208
ViewModel_Main: viewmodel=com.sun.livedata.MyViewModel@3e33da1
ViewModel_Main: local-Int=0   viewmodel-Int=0

手动更改数据
ViewModel_Main: local-Int=1   viewmodel-Int=1
ViewModel_Main: local-Int=2   viewmodel-Int=2
ViewModel_Main: local-Int=3   viewmodel-Int=3

手机屏幕旋转
ViewModel_Main: onDestroy													//activity被销毁
ViewModel_Main: activity=com.sun.livedata.MainActivity@8b1d7b				//activity和上一个已经不是同一个了
ViewModel_Main: viewmodelstore=androidx.lifecycle.ViewModelStore@60ac208   //viewmodelstore是同一个
ViewModel_Main: viewmodel=com.sun.livedata.MyViewModel@3e33da1			   //viewmodel是同一个
ViewModel_Main: local-Int=0   viewmodel-Int=3
ViewModel_Main: local-Int=1   viewmodel-Int=4
ViewModel_Main: local-Int=2   viewmodel-Int=5

退出APP
MyViewModel: oncleared
ViewModel_Main: onDestroy

 小结:

屏幕旋转后,activity销毁并重新打开,页面上的变量重新开始计数,viewmodel上的变量还是继续计数,并且,销毁和新开的activity上的viewmodel和viewmodelstore是同一个实例,并没有改变。

下面做具体分析

1,viewmodel的获取及新建

1,分析如何获取ViewModle
MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);


2,分析new ViewModelProvider(this)

androidx.lifecycle.ViewModelProvider

public ViewModelProvider(ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
}

ComponentActivity中实现了ViewModelStoreOwner,通过getViewModelStore()给我们返回了一个ViewModelStore的实例,具体怎么返回,后面分析
ComponentActivity没有实现Factory相关接口,所以第二个是NewInstanceFactory.getInstance(),返回的是NewInstanceFactory

3,分析ViewModelProvidel.get(MyViewModel.class)

 public <T extends ViewModel> T get( String key,  Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);//从ViewModelstore中的map集合中获取viewmodel,第一遍肯定是为null
		//第二步知道 mFactory的类型为NewInstanceFactory

        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);//走一步,factory中创建
        }
        mViewModelStore.put(key, viewModel);//新建好的viewmodel放到hashmap中
        return (T) viewModel;
    }

4,创建viewmodel

 public static class NewInstanceFactory implements Factory {

        ......
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        
            try {
                return modelClass.newInstance();//通过反射生成了viewmodel
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
    }

下面分析在屏幕旋转页面重开ViewModelStore是如何保持不改变的

1,Android横竖屏切换时会触发onSaveInstanceState,可用来保持可序列化数据,而在页面重开时onRestoreInstanceState中还原数据。另外的,Android还有一组方法随着屏幕切换会调用,那就是onRetainNonConfigurationInstance()和getLastNonConfigurationInstance()。

为什么这2个方法能够实现在activity销毁重开的情况能够数据保持?

答案是在Activity基类中有一个私有静态太监类,有个Object的变量保存数据,,在ActivithThread实现了NonConfigurationInstances随着activity重启启动的传递。

android.app.activity

public class Activity{

 static final class NonConfigurationInstances {
        Object activity;
        HashMap<String, Object> children;
        FragmentManagerNonConfig fragments;
        ArrayMap<String, LoaderManager> loaders;
        VoiceInteractor voiceInteractor;
    }
}

2,继续看ViewModelStore切换app时保存及获取的相关源码

androidx.activity.ComponentActivity

     @Override
    @Nullable
    public final Object onRetainNonConfigurationInstance() {//屏幕切换时自动调用
        Object custom = onRetainCustomNonConfigurationInstance();

        ViewModelStore viewModelStore = mViewModelStore;//当前的viewmodel保存
        if (viewModelStore == null) {
            // No one called getViewModelStore(), so see if there was an existing
            // ViewModelStore from our last NonConfigurationInstance
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                viewModelStore = nc.viewModelStore;//如果当前的viewmodel为null,则切换前的viewmodel
            }
        }

        if (viewModelStore == null && custom == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;//返回要保存的对象到Activity.NonConfigurationInstances中
    }
   
   
   
   @NonNull
    @Override
    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) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();//获取NonConfigurationInstances
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;//完美得到viewmodelstore
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();//如果没有就新建
            }
        }
        return mViewModelStore;
    }

通过onRetainNonConfigurationInstance()和getLastNonConfigurationInstance()这2个方法就实现了viewmodelstore的保存,为了防止开发者重写onRetainNonConfigurationInstance()造成代码干扰,google特意将此方法设为final了。

但还有个问题,viewmodel的生命周期伴随着activity的开启到销毁到重新开启再一直到app死的很彻底而viewmodel消亡,这一部分是怎么实现的?

androidx.activity.ComponentActivity

public ComponentActivity() {
      ......
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {//收到app销毁的感知通知了
                    if (!isChangingConfigurations()) {//不是屏幕配置切换的话那就是activity彻底死了,执行viewmodel的clear操作
                        getViewModelStore().clear();
                    }
                }
            }
        });
       .......
    }

androidx.activity.ViewModelStore
public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    
    public final void clear() {//遍历viewmodel,执行clear操作
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

自己写的viewmodel实现类,onCleared中进行相关资源释放/各类变量置空
public class MyViewModel extends ViewModel {

    @Override
    protected void onCleared() {
        super.onCleared();
        LogUtil.e(TAG, "oncleared");
    }
}

至此,viewmodel的简单使用及源码分析已经全面完成,总体来说还是收货颇多。

总结一下:

ViewModelStore保存并管理着ViewModel,ViewModelStore通过屏幕切换会依次调用的onRetainNonConfigurationInstance()和getLastNonConfigurationInstance()将ViewModelStore进行了保存(依靠静态类,没有序列化和反序列化过程),这样在新开的Activity中ViewModelStore和ViewModel还是原来的,实现了屏幕旋转等配置更改后ViewModel继续留存,同时,ComponentActivity增加观察者感知ondestory方法,在非屏幕旋转情况下,收到销毁方法时进行ViewModel的销毁。

Android-Jetpack代码位置:github

举报

相关推荐

0 条评论