Android AAC第三篇 ViewModel源码解析

Posted 宿罪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android AAC第三篇 ViewModel源码解析相关的知识,希望对你有一定的参考价值。

前言

先看一个ViewModel的使用例子,这里使用Kotlin语言(读者不该困惑于语言,主要是看用法,Java的类似,不懂欢迎留言):

class ShareViewModel : androidViewModel 

    private val userLiveData:MutableLiveData<User> = MutableLiveData()

    constructor(application: Application) : super(application)
        userLiveData.value = User("default","123")
    


    fun getUserLiveData() : MutableLiveData<User>
        return userLiveData
    

mShareViewModel = ViewModelProviders.of(activity!!).get(ShareViewModel::class.java)
mShareViewModel.getUserLiveData().observe(this, Observer 
    mTvUserName.text = it.username
)

首先我们需要创建一个ViewModel,在这个ViewModel里面处理与UI操作无关的业务逻辑,我们这里的demo为了简化只是管理了一个LiveData,如果读者不了解LiveData,可以点击这里:【Android AAC】第二篇 LiveData的源码解析

通过以上两步我们的ViewModel就可以通过LiveData连接User(Model)和UI(Activity/Fragment)了,可以在不同的Fragment共享这个ViewModel,从而共享数据。

类图


下面我们通过分析ViewModel的源码来分析ViewModel的工作原理。

源码解析

ViewModel的源码很简单,只有一个方法

ViewModel.java

public abstract class ViewModel 
    /**
     * This method will be called when this ViewModel is no longer used and will be destroyed.
     * <p>
     * It is useful when ViewModel observes some data and you need to clear this subscription to
     * prevent a leak of this ViewModel.
     */
    @SuppressWarnings("WeakerAccess")
    protected void onCleared() 
    

AndroidViewModel.java

public class AndroidViewModel extends ViewModel 
    @SuppressLint("StaticFieldLeak")
    private Application mApplication;

    public AndroidViewModel(@NonNull Application application) 
        mApplication = application;
    

    /**
     * Return the application.
     */
    @SuppressWarnings("TypeParameterUnusedInFormals")
    @NonNull
    public <T extends Application> T getApplication() 
        //noinspection unchecked
        return (T) mApplication;
    


AndroidViewModel和ViewModel的源码都很少,ViewModel被设计用来承载与UI无关的业务代码,同时它可能在不同的Fragment之间共享,那么我们从这一点入手,看看它是怎么实现数据共享的。

ViewModel的创建过程

ViewModel的创建代码如下:

ViewModelProviders.of(activity!!).get(ShareViewModel::class.java)
public static ViewModelProvider of(@NonNull FragmentActivity activity) 
    return of(activity, null);

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);

ViewModel的创建过程为:ViewModelProviders.of -> ViewModelProvider instance -> ViewModelProvider instance . get(),ViewModelProviders类似一个工具类,我们看看ViewModelProvider.java的构造方法

/**
  * Creates @code ViewModelProvider, which will create @code ViewModels via the given
  * @code Factory and retain them in a store of the given @code ViewModelStoreOwner.
  *
  * @param owner   a @code ViewModelStoreOwner whose @link ViewModelStore will be used to
  *                retain @code ViewModels
  * @param factory a @code Factory which will be used to instantiate
  *                new @code ViewModels
  */
 public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) 
     this(owner.getViewModelStore(), factory);
 

 /**
  * Creates @code ViewModelProvider, which will create @code ViewModels via the given
  * @code Factory and retain them in the given @code store.
  *
  * @param store   @code ViewModelStore where ViewModels will be stored.
  * @param factory factory a @code Factory which will be used to instantiate
  *                new @code ViewModels
  */
 public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) 
     mFactory = factory;
     this.mViewModelStore = store;
 

ViewModelProvider的创建依赖于ViewModelStoreFactory,ViewModelStore是一个维护了一个String和ViewModel映射表的类,用来管理ViewModel,在ViewModel不再使用的时候调用它的onCleared()方法释放资源。而Factory是一个接口

ViewModelStore.java

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);
    

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

Factory.java

public interface Factory 
   /**
     * Creates a new instance of the given @code Class.
     * <p>
     *
     * @param modelClass a @code Class whose instance is requested
     * @param <T>        The type parameter for the ViewModel.
     * @return a newly created ViewModel
     */
    @NonNull
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);

我们看看我们在Activity中创建ViewModel时传入的ViewModelStore

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);

FragmentActivity.java -> getViewModelStore(),在这个方法中先是判断是不是由于Activity配置发生改变,如果是配置发生改变就恢复ViewModelStore,否则会创建一个ViewModelStore实例保存在Activity成员变量中,一个Activity实例只会有一个ViewModelStore对象。

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;

下面我们看看我们传入的Factory

if (factory == null) 
   factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);

AndroidViewModelFactory.java

 public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory 

 private static AndroidViewModelFactory sInstance;

 /**
  * Retrieve a singleton instance of AndroidViewModelFactory.
  *
  * @param application an application to pass in @link AndroidViewModel
  * @return A valid @link AndroidViewModelFactory
  */
 @NonNull
 public static AndroidViewModelFactory getInstance(@NonNull Application application) 
     if (sInstance == null) 
         sInstance = new AndroidViewModelFactory(application);
     
     return sInstance;
 

 private Application mApplication;

 /**
  * Creates a @code AndroidViewModelFactory
  *
  * @param application an application to pass in @link AndroidViewModel
  */
 public AndroidViewModelFactory(@NonNull Application application) 
     mApplication = application;
 

 @NonNull
 @Override
 public <T extends ViewModel> T create(@NonNull Class<T> modelClass) 
     if (AndroidViewModel.class.isAssignableFrom(modelClass)) 
         //noinspection TryWithIdenticalCatches
         try 
             return modelClass.getConstructor(Application.class).newInstance(mApplication);
          catch (NoSuchMethodException e) 
             throw new RuntimeException("Cannot create an instance of " + modelClass, e);
          catch (IllegalAccessException e) 
             throw new RuntimeException("Cannot create an instance of " + modelClass, e);
          catch (InstantiationException e) 
             throw new RuntimeException("Cannot create an instance of " + modelClass, e);
          catch (InvocationTargetException e) 
             throw new RuntimeException("Cannot create an instance of " + modelClass, e);
         
     
     return super.create(modelClass);
 

AndroidViewModelFactory继承于ViewModelProvider.NewInstanceFactory ,重写了create方法,在方法里面判断了我们ViewModel的class类型,如果是AndroidViewModel的子类,那么就会调用AndroidViewModel的Application参数的构造方法,否则调用父类的create方法实现。

NewInstanceFactory.java

/**
     * Simple factory, which calls empty constructor on the give class.
     */
    public static class NewInstanceFactory implements Factory 

        @SuppressWarnings("ClassNewInstance")
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) 
            //noinspection TryWithIdenticalCatches
            try 
                return modelClass.newInstance();
             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);
            
        
    

它父类的create方法中直接通过反射调用ViewModel的一个参数的构造方法创建ViewModel实例。

Tips:

  1. 如果你使用的是ViewModel,即直接继承ViewModel,那么必须提供一个无参数的构造方法,否则运行的时候会报错哦。
  2. 我们也可以自己创建Factory(实现Factory接口即可)来改变创建ViewModel的方式。

ViewModelProvider类生成之后调用它的get方法获得ViewModel实例,下面再来看看它的get方法

 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);

方法中首先判断了我们传入的class是不是匿名内部类的class,匿名内部类不允许作为ViewModel。然后通过约定的KEY前缀 + 类名组合成KEY调用重载的get方法。

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

    if (modelClass.isInstance(viewModel)) 
        //noinspection unchecked
        return (T) viewModel;
     else 
        //noinspection StatementWithEmptyBody
        if (viewModel != null) 
            // TODO: log a warning.
        
    

    viewModel = mFactory.create(modelClass);
    mViewModelStore.put(key, viewModel);
    //noinspection unchecked
    return (T) viewModel;

可以看到在这里先判断ViewModelStore是否有ViewModel,如果有则直接返回,否则通过Factory的create方法创建了ViewModel后保存在了ViewModelStore中,意味着如果ViewModelStoremodelClass一样的话多次调用ViewModelProvider的get方法将会获取到同一个ViewModel,所以,我们在Fragment中共享ViewModel只需要让Fragment依附在同一个Activity中并且通过Activity来获取ViewModel,同一个Activity的ViewModelStore是一样的。

到这里我们知道了ViewModel的创建过程,那么ViewModel的onCleared方法是什么时候调用的呢,也就是我们在onCleared中释放的资源操作何时会执行?

细心的朋友也许发现了,刚我们在看ViewModelStore源码的时候,看到它有一个clear()方法

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

我们跟踪一下clear()方法的调用发现在FragmentActivityonDestroy()中被调用

@Override
    protected void onDestroy() 
        super.onDestroy();
        if (mViewModelStore != null && !isChangingConfigurations()) 
            mViewModelStore.clear();
        
        mFragments.dispatchDestroy();
    

也就是说在Activity销毁的时候系统会自动调用ViewModel的onCleared()方法,因此我们可以在onCleared()中释放我们的资源。

上面所说的是在Activity中创建ViewModel,那么在Fragment中创建ViewModel呢?

我们看看ViewModelProvidersof的另一个重载方法

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);

Fragment -> getViewModelStore()

public ViewModelStore getViewModelStore() 
     if (getContext() == null) 
         throw new IllegalStateException("Can't access ViewModels from detached fragment");
     
     if (mViewModelStore == null) 
         mViewModelStore = new ViewModelStore();
     
     return mViewModelStore;
 

可以看到,在Fragment的实例成员中也维护了一个ViewModelStore,所以在不同的Fragment通过该方法获得同一个ViewModel类的实例不一样。

之前说我们在不同的Fragment共享ViewModel可以通过Activity来获取ViewModel的方式共享,那么如果这两个Fragment在不同的Activity中呢?或是两个Activity要共享ViewModel呢?我们该如何实现ViewModel共享?

通过前面的源码分析我们可以得出结论,只需要保证ViewModelStore一样,那么同一个ViewModel类会获取同一个实例,那么我们可以在Application中自己维护一个ViewModelStore实例,通过这个ViewModelStore创建ViewModelProvider完成ViewModel的不同Activity之间或者不同FragmentViewModel共享。

下面是不同Activity共享ViewModel的demo(Kotlin):

不同Activity间共享ViewModel

App.java

class App : Application() 

    private lateinit var mViewModelStore:ViewModelStore

    override fun onCreate() 
        super.onCreate()
        mViewModelStore = ViewModelStore()
    

    fun getViewModelStore() : ViewModelStore 
        return mViewModelStore
    

MainActivity.java

class MainActivity : AppCompatActivity() 

    private lateinit var mTvUserName: TextView

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mTvUserName = findViewById(R.id.tv_username)

        if(application is App)
            val shareViewModel = ViewModelProvider(
                (application as App).getViewModelStore(),
                ViewModelProviders.DefaultFactory(application)
            ).get(
                ShareViewModel::class.java
            )
            shareViewModel.getUserLiveData().observe(this, Observer 
                mTvUserName.text = "name:$it.username"
            )
        
    

    fun second(view: View)
        startActivity(Intent(this,SecondActivity::class.java))
    

SecondActivity.java

class SecondActivity : AppCompatActivity(), TextWatcher 

    private lateinit var mShareViewModel:ShareViewModel

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        var et_username: EditText = findViewById(R.id.et_username)
        et_username.addTextChangedListener(this)
        if(application is App)
            mShareViewModel = ViewModelProvider(
                (application as App).getViewModelStore(),
                ViewModelProviders.DefaultFactory(application)
            ).get(
                ShareViewModel::class.java
            )
        
    

    override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) 
    

    override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) 
    

    override fun afterTextChanged(p0: Editable) 
        Log.e("SecondActivity","afterTextChanged/p0:$p0")
        mShareViewModel.getUse

以上是关于Android AAC第三篇 ViewModel源码解析的主要内容,如果未能解决你的问题,请参考以下文章

2022年Android面试之Jetpack(AAC框架)篇

Android AAC第一篇 Lifecycle的源码解析

Android AAC第一篇 Lifecycle的源码解析

第三篇,python编辑器和集成环境01

Android中的IPC进程通信方式第三篇

Gradle for Android 第三篇( 依赖管理 )