ViewModel原理,源码分析,通俗易懂

先看使用方法:

public class MyActivity  extends AppCompatActivity {
    private MyViewModel vm;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        MyActivityBinding binding = MyActivityBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        vm = new ViewModelProvider(this,new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);

        binding.btSend.setOnClickListener(v -> {
            vm.setAccount("123456789");
        });

        binding.btGet.setOnClickListener(v->{
            Log.e("ViewModel",vm.mAccount.getValue().toString());
        });

        vm.mAccount.observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.e("ViewModel","Observer--onChanged" + s);
            }
        });

    }
}

public class MyViewModel extends AndroidViewModel implements Serializable {

    public  MyLiveData<String> mAccount;

    public MyViewModel(@NonNull Application application) {
        super(application);
        mAccount = new MyLiveData<>();
    }

    public void setAccount(String account){
        mAccount.setValue(account);
    }
}

上面这段代码用到了ViewBinding、去除粘性的LiveData、ViewModel和LiveData连用

这篇讲ViewModel:

先提几个问题

1.ViewModel以什么形式存在

ViewModel就是一个工具类,我们可以把它当成MVP中的Presenter,可以在里面处理一些网络请求的逻辑维护一些数据

2.ViewModel的作用

在横竖屏切换Activity重建的时候ViewModel中的数据还能保存下来

3.为啥ViewModel能够在横竖屏切换Activity重建的时候ViewModel中的数据还能保存下来

首先介绍一个类:ViewModelStore

public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }

    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

这个类很显然就是系统来帮我们维护我们创建的ViewModel的,用Map维护有存有取有清空

那具体是什么时候存什么时候取什么时候清空?

再介绍一个接口:ViewModelStoreOwner

public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
}

接口的实质就是,实现了这个接口就有了这个接口中方法的能力,我们的ComponentActivity实现了这个借口并且实现了getViewModelStore方法ComponentActivity就具备了获取ViewModelStore的能力。

    @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();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

这段代码需要注意下:

NonConfigurationInstances nc = (NonConfigurationInstances)getLastNonConfigurationInstance();

这一句,想完全搞清楚这个东西的来历和作用得看AMS的源码,所以这里大家就先记住这个NonConfigurationInstances保存了viewModelStore,viewModelStore保存了ViewModel,NonConfigurationInstances是AMS在上次横竖屏切换时AMS保存下来的,也正是因为这个NonConfigurationInstances使我们的ViewModel能够在横竖屏切换Activity重建之后还能保存之前的数据。

下面开始分析ViewModel具体是什么时候被viewModelStore存什么时候取什么时候清空?

存、取:

在获取ViewModel的时候:

ViewModel vm = new ViewModelProvider(this,new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);

Activity实现了ViewModelStoreOwner这个接口,把Activity的this当作参数传入,这个this其实就是ViewModelStoreOwner这个接口的实现类,上面代码中owner.getViewModelStore()就获取了ViewModelStore并传入

这时mViewModelStore就维护在了ViewModelProvider中,我们再看活去ViewModel这句:

ViewModel vm = new ViewModelProvider(this,new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);

这个get(Class)方法:参数就是我们已经定义好的ViewModel的Class在这里是MyViewModel.class

 public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        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);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

 这个就是通过反射来获取ViewModel的实例并通过ViewModelProvider中的mViewModelStore保存起来,这个ViewModelProvider中的mViewModelStore是通过Activity的this调用getViewModelStore方法获取的,而this是通过参数传到ViewModelProvider中的,我们知道如果参数是指针类型那在ViewModelProvider中改变这个指针中的内容那只要是同一个指针,其内容都会随之改变,所以反射得到的viewModel就被保存在mViewModelStore的map中了

清空:

在ComponentActivity的构造函数中:

        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    if (!isChangingConfigurations()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });

注意这个判断if (!isChangingConfigurations()),isChangingConfigurations()这个方法:

    public boolean isChangingConfigurations() {
        return mChangingConfigurations;
    }

mChangingConfigurations这个flag的意义是关键。默认是false

现在已知发生横竖屏切换的时候mChangingConfigurations就是true,所以当发生横竖屏切换的时候mViewModelStore中的viewModel不会被清空,当正常退出页面的时候才会。

现在知道了viewModel在获取的时候就被维护在了Activity的mViewModelStore中,横竖屏切换不会清除mViewModelStore中的ViewModel,只有正常退出才会清除,还有一个问题,现在只是没有清除mViewModelStore中的ViewModel,当横竖屏切换的时候Activity会重建mViewModelStore又是如何保存的呢?

在ComponentActivity中有这样一个方法:

    public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();

        ViewModelStore viewModelStore = mViewModelStore;
        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;
            }
        }

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

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }

在配置更改时会调用 Activity#
onRetainNonConfigurationInstance() 来保存保存着 ViewModel 示例的对象 mViewModelStore,并在 Activity 重建后调用 getViewModelStore() ,在getViewModelStore()内部会调用getLastNonConfigurationInstance() 方法获取是否有缓存的 ViewModelStore 对象,若有则返回,没有则创建新 ViewModelStore 实例

这个onRetainNonConfigurationInstance()是怎么调用的呢?

请看下篇:

AndroidFramwork源码解析onRetainNonConfigurationInstance()调用逻辑

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>