若是還不清楚什麼是ViewModel,能夠看下[譯] Architecture Components 之 ViewModel 這個系列的文章,翻譯自Android Developer的官方文章。android
ViewModel 類是被設計用來存儲和管理 UI 相關的數據,主要實現了兩個功能:緩存
接下來會經過分析源碼的方式來看看是如何實現這兩個功能的。bash
咱們先來找到ViewModel這個類app
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() {
}
}
複製代碼
發現只是一個抽象類,而且只有一個空實現的方法,說明實現特殊功能的代碼必定在其餘地方。 看過官方介紹的應該知道,ViewModel是經過ViewModelProvider建立的:ide
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// 更新 UI
});
}
}
複製代碼
那咱們從這裏開始分析,經過調用ViewModelProviders.of(this).get(MyViewModel.class)
是如何獲取到ViewModel的那?咱們來看ViewModelProviders
的of()
方法(of方法重載還有Fragment,這裏咱們只分析Activity,Fragment與activity一模一樣):post
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
initializeFactoryIfNeeded(checkApplication(activity));
return new ViewModelProvider(ViewModelStores.of(activity), sDefaultFactory);
}
@SuppressLint("StaticFieldLeak")
private static DefaultFactory sDefaultFactory;
private static void initializeFactoryIfNeeded(Application application) {
if (sDefaultFactory == null) {
sDefaultFactory = new DefaultFactory(application);
}
}
private static Application checkApplication(Activity activity) {
Application application = activity.getApplication();
if (application == null) {
throw new IllegalStateException("Your activity/fragment is not yet attached to "
+ "Application. You can't request ViewModel before onCreate call.");
}
return application;
}
複製代碼
ViewModelProviders.of()
方法返回了一個ViewModelProvider對象,該對象須要兩個參數:ViewModelStore、Factory。經過命名,咱們能夠猜想ViewModelStore是一個ViewModel的倉庫,用於緩存ViewModel,Factory是工廠類,用於建立ViewModel實例。獲取到ViewModelProvider對象後,又調用了它的get
方法就獲取到ViewModel對象,來看下這個方法:ui
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);
}
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;
}
複製代碼
ViewModelProvider類中將構造的參數ViewModelStore和Factory做爲成員變量,get
方法先是從mViewModelStore.get
中獲取,若是沒有獲取到則經過Factory
建立一個ViewModel
實例,並放入ViewModelStore
中,這種使用方式更加驗證了上面咱們的猜想,以後會仔細分析ViewModelStore
以及Factory
。this
既然有一個ViewModel
的緩存ViewModelStore
,那第一個功能:在配置更改(如:屏幕旋轉)時數據能夠保留下來,就很好理解了。只要讓緩存在Activity配置更改重建是存活下來,那重建後獲取的ViewModel
就是以前緩存的那個了。spa
接下的問題即是: ViewModelStore
存放在哪裏能夠保證在Activity配置更改重建是存活下來?翻譯
字面意思即爲ViewModel的倉庫
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.get(key);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
mMap.put(key, viewModel);
}
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();
}
}
複製代碼
這個很簡單,也很好理解,僅僅是一個HashMap用於存放ViewModel,提供放入,獲取,清空的方法。
咱們回到ViewModelProviders.of()
方法來,這裏是經過ViewModelStores.of(activity)
獲取到的ViewModelStore對象的,咱們繼續進入這個方法:
import static android.arch.lifecycle.HolderFragment.holderFragmentFor;
public class ViewModelStores {
private ViewModelStores() {
}
public static ViewModelStore of(@NonNull FragmentActivity activity) {
return holderFragmentFor(activity).getViewModelStore();
}
}
複製代碼
注意到經過靜態引入的方法調用了HolderFragment
的holderFragmentFor ()
方法,接着找到HolderFragment
:
public class HolderFragment extends Fragment {
...//去除了framgent相關的代碼,只保留activity相關
private static final HolderFragmentManager sHolderFragmentManager = new HolderFragmentManager();
public static final String HOLDER_TAG =
"android.arch.lifecycle.state.StateProviderHolderFragment";
private ViewModelStore mViewModelStore = new ViewModelStore();
public HolderFragment() {
setRetainInstance(true);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sHolderFragmentManager.holderFragmentCreated(this);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}
public ViewModelStore getViewModelStore() {
return mViewModelStore;
}
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
return sHolderFragmentManager.holderFragmentFor(activity);
}
static class HolderFragmentManager {
private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>();
private ActivityLifecycleCallbacks mActivityCallbacks =
new EmptyActivityLifecycleCallbacks() {
@Override
public void onActivityDestroyed(Activity activity) {
HolderFragment fragment = mNotCommittedActivityHolders.remove(activity);
if (fragment != null) {
Log.e(LOG_TAG, "Failed to save a ViewModel for " + activity);
}
}
};
private boolean mActivityCallbacksIsAdded = false;
void holderFragmentCreated(Fragment holderFragment) {
Fragment parentFragment = holderFragment.getParentFragment();
if (parentFragment != null) {
mNotCommittedFragmentHolders.remove(parentFragment);
parentFragment.getFragmentManager().unregisterFragmentLifecycleCallbacks(
mParentDestroyedCallback);
} else {
mNotCommittedActivityHolders.remove(holderFragment.getActivity());
}
}
private static HolderFragment findHolderFragment(FragmentManager manager) {
if (manager.isDestroyed()) {
throw new IllegalStateException("Can't access ViewModels from onDestroy");
}
Fragment fragmentByTag = manager.findFragmentByTag(HOLDER_TAG);
if (fragmentByTag != null && !(fragmentByTag instanceof HolderFragment)) {
throw new IllegalStateException("Unexpected "
+ "fragment instance was returned by HOLDER_TAG");
}
return (HolderFragment) fragmentByTag;
}
private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
HolderFragment holder = new HolderFragment();
fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
return holder;
}
HolderFragment holderFragmentFor(FragmentActivity activity) {
FragmentManager fm = activity.getSupportFragmentManager();
HolderFragment holder = findHolderFragment(fm);
if (holder != null) {
return holder;
}
holder = mNotCommittedActivityHolders.get(activity);
if (holder != null) {
return holder;
}
if (!mActivityCallbacksIsAdded) {
mActivityCallbacksIsAdded = true;
activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
}
holder = createHolderFragment(fm);
mNotCommittedActivityHolders.put(activity, holder);
return holder;
}
}
複製代碼
這個類就是ViewModel的核心類了,全部的功能都是經過該類來實現的,注意看好啦~
HolderFragment.holderFragment()
方法直接返回了sHolderFragmentManager.holderFragmentFor(activity)
的結果。而HolderFragmentManager
的holderFragmentFor
方法實際上就是建立了一個HolderFragment的實例,並添加到參數activity中,爲了不重複添加,先是調用 findHolderFragment(fm)
看看可否找到已添加HolderFragment,若是沒有的話再從緩存的Map中找,仍是沒有話纔去建立一個新的實例,放入緩存的Map,並返回這個對象,繼而調用getViewModelStore()
獲取viewModelStore
實例。
咱們找到了ViewModelStore
存放位置,是在HolderFragment中,那它如何保證在Activity配置更改重建是存活下來的?其實關鍵代碼就是Fragment的一個方法:
/**
* Control whether a fragment instance is retained across Activity
* re-creation (such as from a configuration change). This can only
* be used with fragments not in the back stack. If set, the fragment
* lifecycle will be slightly different when an activity is recreated:
* <ul>
* <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still
* will be, because the fragment is being detached from its current activity).
* <li> {@link #onCreate(Bundle)} will not be called since the fragment
* is not being re-created.
* <li> {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} <b>will</b>
* still be called.
* </ul>
*/
setRetainInstance(true);
複製代碼
就是這個方法保證了activity因配置更改重建時,該fragment的實例不會銷燬,重建後的Activity仍是使用該實例。
建立HolderFragment 的過程還有不少細節。
須要注意的是HolderFragmentManager
是聲明在HolderFragment
中的static成員,所以會隨着HolderFragment
的首次加載建立實例,只存在一個實例並永遠在內存中,緩存的map是HolderFragmentManager
的成員變量,也會永遠在內存中,而HolderFragment
能夠建立多個實例,因此對於再也不須要的HolderFragment
實例,須要及時從map中移除。
if (!mActivityCallbacksIsAdded) {
mActivityCallbacksIsAdded = true;
activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
}
複製代碼
這段代碼是經過Application的registerActivityLifecycleCallbacks註冊一個全局Activity生命週期的回調,任何Activity觸發了生命週期都會在mActivityCallbacks中回調對應的方法。HolderFragment
的源碼中就是經過該回調,在綁定HolderFragment
的Activity觸發onDestroy方法後移除map中的緩存。
一開始我覺得HolderFragmentManager
會緩存HolderFragment
直到依附的activity銷燬纔會移除緩存,但後來注意到在HolderFragment
的onCreate
方法中調用了sHolderFragmentManager.holderFragmentCreated(this);
直接移除了緩存。所以,這個緩存僅僅是從HolderFragment
的add方法調用到onCreate
方法執行爲止。或者add了Fragment可是尚未添加到Activity執行onCreate方法,依附的Activity就銷燬了,也會回調mActivityCallbacks的onDestroy方法移除HolderFragment的緩存。我想了好久也沒有想到這個緩存的使用場景,好像這個緩存是沒有意義的。
2017.12.27 補充: 通過JessYan的提點,我意識到HolderFragment
的緩存是頗有意義的,若是沒有這個緩存,在連續調用兩次獲取ViewModel時,Fragment尚未添加執行onCreate,那這樣就會建立兩個HolderFragment實例了。而且這種狀況在兩個Fragment之間發生的話,獲取到的就是不一樣ViewModel實例,也就沒法實現Fragment間通信了。我用as3.0 的Android profiler驗證了這種狀況,確實建立了兩個實例!
即爲建立ViewModel的工廠類,是一個接口,咱們能夠實現這個接口定義本身的ViewModel工廠。
/**
* Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
*/
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);
}
複製代碼
上面of()
使用的sDefaultFactory
默認工廠:
public static class DefaultFactory extends ViewModelProvider.NewInstanceFactory {
private Application mApplication;
/**
* Creates a {@code DefaultFactory}
*
* @param application an application to pass in {@link AndroidViewModel}
*/
public DefaultFactory(@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);
}
}
public static class NewInstanceFactory implements Factory {
@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);
}
}
}
複製代碼
DefaultFactory能夠建立AndroidViewModel
的對象,調用它AndroidViewModel(@NonNull Application application)
構造建立實例,若是不是AndroidViewModel.class則調用父類NewInstanceFactory的create方法調用ViewModel無參數的構造。 若是你的ViewModel實例的建立須要其餘參數,則要本身實現Factory複寫create。
ViewModelProviders.of()
提供ViewModelProvider
,ViewModelProvider
經過ViewModelStore
和Factory
管理和建立ViewModel,ViewModelStore
的引用存儲在向目標Activity/Fragment中添加的無界面HolderFragment
中,並經過setRetainInstance(true);
以保證在Activity配置更改重建是存活下來。
關於第二條功能:在 Fragment 之間共享數據也很好理解了,在同一個Activity的不一樣Fragment種使用ViewModelProviders.of()
時,參數須要傳入Activity對象,第一次獲取ViewModel時會建立一個新對象,而另外一個Fragment獲取相同ViewModel時,則會從ViewModelStore
的緩存中獲取,兩個Fragment持有的時同一個ViewModel對象,就能實現Fragment之間通信了。可是這種通信的前途是必須在同一個Activity中。