前言
先看一個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的建立代碼如下:
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:
- 如果你使用的是ViewModel,即直接繼承ViewModel,那麼必須提供一個無參數的構造方法,否則運作的時候會報錯哦。
- 我們也可以自己建立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
}
}