前言
ViewModel的雖然可以以很簡單方便的 ViewModelProviders.of(getActivity()).get(ViewModel.class); 但是這只是google提供的簡單構建,你需要引入下面的庫才能使用:
implementation "androidx.lifecycle:lifecycle-extensions:2.1.0"
但是!遠遠不止這樣。l還可以使用ViewModelProvider.Factory自定義實例ViewModel(其實繼承工廠類實例ViewModel才是google推薦的,這點在Android studio的模板代碼里可以看到)
為什么要學習ViewModel自定義實例?
那么你可能會疑問了,使用 ViewModelProviders.of(getActivity()).get(ViewModel.class); 創建ViewModel這么簡單輕松,為什么還要自己構建Factory呢。原因有以下幾點:
- 如果你繼承的ViewModel類是有參構造,那么上面這個方式是不支持在實例ViewModel的傳參的,所以需要自己構建Factory類
- 一個Activity可能根據業務會有多個不同的ViewModel,那么根據不同的業務下配置不同的ViewModel,需要自己構建Factory類,來根據類別返回不同的ViewModel給外部(這也是工廠模式的核心,解決接口選擇的問題)
了解ViewModel的構建原理
因為后續的使用ViewModelProvider.Factory創建ViewModel,所以我們必需了解一些些ViewModel構建、保存、移除原理。好在有一個參照物可以閱讀理解,那就是上面提到以 ViewModelProviders 形式的構建ViewModel。對照着它看基本上就能明白一些東西了。
看看 ViewModelProviders.of() 干了啥事情
我們點擊進入源碼后可以看到如下代碼
@NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity) { return of(activity, null); }
恩,of又去調用了一個重載的of,但是傳入了一個activity或者Fragment 和 一個null ,我們看看這個2個參數的of干了什么
@NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) { Application application = checkApplication(activity); //源碼在這里檢查了一下activity的application if (factory == null) { //它拿application去創建了一個Factory類 //這個factory其實就是構建ViewModel的工廠類 factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); } //實例化了一個ViewModelProvider, //在activity里取得ViewModelStore(這個東西是用於保存ViewModel使用的),也傳入了 //並且也把Factory類一起傳入 //其實ViewModelStore和factory傳入后就直接成為成員變量緩存了,等待你調用get方法來創建ViewModel return new ViewModelProvider(activity.getViewModelStore(), factory); }
我們可以看看ViewModelProvider.AndroidViewModelFactory.getInstance(application) 這行代碼
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; }
可以看到,直接給你了一個有Application的全局單例的Factory類。
接下來看看ViewModelProviders.of().get() get方法干了啥事
get()方法其實就是實現了 factory 創建 與 ViewModelStore保存 ViewModel,並且返回ViewModel。 我們可以看看下面這些代碼
@NonNull @MainThread public <T extends ViewModel> T get(@NonNull Class<T> modelClass) { String canonicalName = modelClass.getCanonicalName(); //獲取了傳入的實現的ViewModel的類名 if (canonicalName == null) { throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels"); } //因為我們沒有傳入key,所以這里直接用類名取代的成為了key //然后就進入下面這個重載的get方法里進行實現了 return get(DEFAULT_KEY + ":" + canonicalName, modelClass); } @NonNull @MainThread public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) { ViewModel viewModel = mViewModelStore.get(key);//去查找這個ViewModelStore 對應key的保存viewModel if (modelClass.isInstance(viewModel)) { //檢查一下這個viewModel是不是已經實例化,不是null //noinspection unchecked return (T) viewModel; //如果已經實例化,不等於null就直接返回 } else { //noinspection StatementWithEmptyBody if (viewModel != null) { // TODO: log a warning. } } //下面就用傳入的Factory類來實例化你需要的ViewModel if (mFactory instanceof KeyedFactory) { //這里檢查是不是帶key viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass); } else { viewModel = (mFactory).create(modelClass); //這里的create使用了反射的形式創建實例 } mViewModelStore.put(key, viewModel); //然后存一下ViewModel //noinspection unchecked return (T) viewModel; //返回viewModel }
移除緩存的ViewModel
其實蛛絲馬跡就在ViewModelStore里
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); } Set<String> keys() { return new HashSet<>(mMap.keySet()); } /** * Clears internal storage and notifies ViewModels that they are no longer used. */ public final void clear() { //這里清理了當前Activity或者Fragment的所有ViewModel,這個方法是在Activity或者Fragment在銷毀的時候有調用的 for (ViewModel vm : mMap.values()) { vm.clear(); } mMap.clear(); } }
使用ViewModelProvider.Factory實現創建ViewModel
使用反射創建無參實例(在上面的源碼上可以看到的創建方式,這樣一個Factory就可以傳入繼承ViewModel的泛型,加以實例)
public class MainViewModelFactory implements ViewModelProvider.Factory { @NonNull @Override public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { try { return modelClass.newInstance(); //使用newInstance反射實例ViewModel,並且傳出去 } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } return null; } }
使用MainViewModelFactory創建ViewModel,注意!千萬別自己就使用Factory直接創建ViewModel,因為上面的get()源碼里說明了,這個ViewModel是還需要保存到ViewModelStore里面的,而ViewModelStore的put方法是私有方法。所以我們還是需要交給ViewModelProviders.of().get();來創建讓它幫我們保存到ViewModelStore里,代碼如下:
private MainViewModel mMainViewModel; private void initViewModel() { MainViewModelFactory mainViewModelFactory = new MainViewModelFactory(); mMainViewModel = ViewModelProviders.of(this, mainViewModelFactory).get(MainViewModel.class); }
使用反射創建有參實例
代碼如下:
public class MainViewModelFactory4 implements ViewModelProvider.Factory { private String mValue; public MainViewModelFactory4(String value){ mValue = value; } @NonNull @Override public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { try { Class[] parameterTypeArray = new Class[]{String.class}; return modelClass.getConstructor(parameterTypeArray).newInstance(mValue); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; } }
還是跟上面一樣使用ViewModelProviders.of().get();來創建。
簡單暴力直接實例目標ViewModel
public class MainViewModelFactory implements ViewModelProvider.Factory { @NonNull @Override public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { MainViewModel mainViewModel = new MainViewModel(); mainViewModel.setContent("設置初始化值"); return (T)mainViewModel; } }
還是跟上面一樣使用ViewModelProviders.of().get();來創建。
使用KEY創建ViewModel
這沒啥好說的,就是根據不同的key創建獨立數據與內存地址的ViewModel,他們之間數據不互通。
mMainViewModel1 = ViewModelProviders.of(this).get("1", MainViewModel.class); mMainViewModel2 = ViewModelProviders.of(this).get("2", MainViewModel.class); mMainViewModel3 = ViewModelProviders.of(this).get("3", MainViewModel.class);
關於ViewModelProvider.NewInstanceFactory
也是繼承Factory,然后幫你實現了newInstance();如果你只是簡簡單單實例一個ViewModel,可以直接繼承這個,我們可以看源碼:
/** * 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); } } }
關於ViewModelProvider.AndroidViewModelFactory
帶application的工廠類,你需要配置一個需要application的ViewModel,可以直接使用這個單例的AndroidViewModelFactory。可以看源碼(下面的代碼上面已經貼了一部分了,這里貼全),需要一個構造方式是傳入application的ViewModel:
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); } }
End
