天天看點

【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