天天看点

【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,从而共享数据。

类图

【Android AAC】第三篇 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的创建代码如下:

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

的创建依赖于

ViewModelStore

Factory

,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

中,意味着如果

ViewModelStore

modelClass

一样的话多次调用

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

方法的调用发现在

FragmentActivity

onDestroy()

中被调用

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

也就是说在Activity销毁的时候系统会自动调用ViewModel的

onCleared()

方法,因此我们可以在

onCleared()

中释放我们的资源。

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

我们看看

ViewModelProviders

of

的另一个重载方法

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

之间或者不同

Fragment

ViewModel

共享。

下面是不同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.getUserLiveData().value?.username = p0.toString()
        mShareViewModel.getUserLiveData().value =  mShareViewModel.getUserLiveData().value
    }
}
           

本文demo github地址:https://github.com/ydxlt/viewmodel_simple