Jetpack系列组件之ViewModel

Jetpack简介

在2018年Google I/O大会发布了一系列辅助android开发者的实用工具,统称Jetpack,Jetpack 是一套库、工具和指南,可帮助开发者更轻松地编写优质应用。这些组件可帮助您遵循最佳做法、让您摆脱编写样板代码的工作并简化复杂任务,以便您将精力集中放在所需的代码上。

ViewModel

这篇文章主要讲ViewModel的使用及原理。ViewModel主要用来管理UI与数据的交互,当屏幕的旋转、权限开启与关闭、内存不足回收Activity等会重新创建页面,页面数据就会丢失,而通过ViewModel来保存数据不会因为Activity的配置改变而发生变化。

基本使用

添加依赖:

  allprojects {
        repositories {
            google()
            jcenter()
        }
    }
  dependencies {
        implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
        implementation"androidx.lifecycle:lifecycle-viewmodel:2.0.0"
    }

我们要使用ViewModel通常的做法是继承ViewModel,例如:

class MainViewModel : ViewModel() {
    
    
    val noticeCount = MutableLiveData<Int>()
}

通常ViewModel和LiveData配合使用,LiveData后面再讲,其实LiveDataRxJava类似,都是基于观察者模式。创建好了ViewModel,接下来就是使用它,比如我们在Activity中使用:

class MainActivity : AppCompatActivity() {
    
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
      	val model = ViewModelProviders.of(this).get(MainViewModel::class.java)
        model.noticeCount.observe(this, Observer{
    
    
						//数据发生变化,更新UI
        })
    }
}

首先我们看ViewModelProviders.of(this)这个静态方法,它有4个重载方法:

public static ViewModelProvider of(@NonNull Fragment fragment) {
    
    
        return of(fragment, null);
    }
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
    
    
        return of(activity, null);
    }
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
    
    
        Application application = checkApplication(checkActivity(fragment));
        if (factory == null) {
    
    
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(fragment.getViewModelStore(), factory);
    }
public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @Nullable Factory factory) {
    
    
        Application application = checkApplication(activity);
        if (factory == null) {
    
    
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(activity.getViewModelStore(), factory);
    }

of方法里首先先判断是否有factory,没有则创建,默认使用AndroidViewModelFactory。调用of方法获取到ViewModelProvider对象,再调用get方法,get方法主要是通过AndroidViewModelFactorycreate方法通过反射创建ViewModel实例。

public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    
    
        //在缓存中获取(内部通过HashMap缓存)
  			ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
    
    
            //如果找到直接返回
            return (T) viewModel;
        } else {
    
    
            if (viewModel != null) {
    
    
                // TODO: log a warning.
            }
        }
  			//没有找到则通过Factory创建
        if (mFactory instanceof KeyedFactory) {
    
    
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
    
    
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    } 
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    
    
        ViewModel viewModel = mViewModelStore.get(key);
        ...
        if (mFactory instanceof KeyedFactory) {
    
    
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
    
    
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
    
    
       ...
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
    
    
            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
    
    
                //noinspection TryWithIdenticalCatches
                try {
    
    
                  //反射创建ViewModel
                    return modelClass.getConstructor(Application.class).newInstance(mApplication);
                } ...
            return super.create(modelClass);
        }
    }

上面主要讲ViewModel是怎样获取的,接下来看一下ViewModel为什么不会随着Activity重建而造成数据丢失。在ViewModelProviders.of(this)方法中获取Activity或Fragment中的ViewModelStore缓存作为参数创建了ViewModelProvider对象,那么ViewModelStore的缓存一定在Activity或Fragment中。当配置改变时,在onSaveInstanceState里保存数据,然后在onRestoreInstanceState恢复数据,但是onSaveInstanceState只能存放少量数据,viewModelStore存放的数据可能很大因此在onSaveInstanceState中存放viewModelStore并不合适,那么viewModelStore怎么保存的呢,通过查看源码原来是利用Activity中的onRetainNonConfigurationInstance和getLastNonConfigurationInstance方法。我们看一下ComponentActivity中的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) {
    
    
          //获取NonConfigurationInstances,如果不为空再获取viewModelStore,当配置发生改变可以重写onRetainCustomNonConfigurationInstance方法传入自定义数据,当需要时再通过getLastNonConfigurationInstance获取
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
    
    
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
          //如果没有获取到,创建ViewModelStore对象
            if (mViewModelStore == null) {
    
    
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

ComponentActivity中onRetainNonConfigurationInstance保存viewModelStore

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

        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
    
    
            // 如果NonConfigurationInstance保存了viewModelStore,把它取出来
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
    
    
                viewModelStore = nc.viewModelStore;
            }
        }

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

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
  			//把viewModelStore放到NonConfigurationInstances中并返回
        nci.viewModelStore = viewModelStore;
        return nci;
    }

onRetainNonConfigurationInstance的原理是什么呢,他的数据最终是怎样保存的呢,了解过Activity启动流程的都知道ActivityThread,他就是我们所说的主线程,它控制着Activity的生命周期,当ActivityThread执行performDestroyActivity这个方法时,会调用Activity.retainNonConfigurationInstances获取到保存的数据并保存到ActivityClientRecord中。

ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance, String reason) {
    
    
        ...
            if (getNonConfigInstance) {
    
    
                try {
    
    
                  //保存retainNonConfigurationInstances中的数据到ActivityClientRecord中
                    r.lastNonConfigurationInstances
                            = r.activity.retainNonConfigurationInstances();
                } catch (Exception e) {
    
    
                    if (!mInstrumentation.onException(r.activity, e)) {
    
    
                        throw new RuntimeException(
                                "Unable to retain activity "
                                + r.intent.getComponent().toShortString()
                                + ": " + e.toString(), e);
                    }
                }
            }
        ...
        return r;
    }

当页面重建数据保存下来了,那么数据是怎么恢复的呢,其实在ActivityThread创建Activity执行activity.attach时,会把lastNonConfigurationInstances传递过来,这样就可以在lastNonConfigurationInstances中获取保存的viewModelStore了。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    
    
				activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);
}

当关闭Activity时,会清除viewModelStore。

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

这里使用的是Lifecycle监听生命周期,Lifecycle是什么先不管,它可以监听到Activity生命周期的变化,当Activity关闭调用onDestory时清空viewModelStore中的数据。

ViewModel的源码很少,但是设计的很精妙,确实很强大。ViewModel通常是和LiveData配合使用,下一篇主要讲LiveData的实现原理。

猜你喜欢

转载自blog.csdn.net/ZYJWR/article/details/97613668