簡單介紹
從圖可以看出來,ViewModel 與 LiveData 和 Paging 是谷歌新元件,同時它是 android.arch.lifecycle 包裡面的類,可以支援 activity 和 fragment 共享資料(比如在 fragment 獲取 activity 搜尋框的內容)當然 activity 銷燬了資料就不存在了;又或者是 fragment 與子 fragment 共享資料(比如 fragment 裡面巢狀了 viewpager , viewpager 裡又有 fragemnt)。使用方法
//build.gralde
implementation 'android.arch.lifecycle:extensions:1.1.1' //包括了 viewmodel 和 livedata
implementation 'android.arch.lifecycle:compiler:1.1.1'
implementation 'android.arch.lifecycle:common-java8:1.1.1'
複製程式碼
建立一個類傳字串,然後在 activity 例項化並且賦值。
//KeywordModel.kt
class KeywordModel : ViewModel() {
var keyword: MutableLiveData<String>? = MutableLiveData()
fun getKeyWord(): LiveData<String> {
return keyword!!
}
fun setKeyWord(key: String) {
keyword?.value = key
}
}
//MainActivity.kt
val keywordModel=ViewModelProviders.of(this).get(KeywordModel::class.java) //在activity例項化
keywordModel.setKeyWord("siri") //賦值
複製程式碼
在fragment 獲取值
//ExampleFragment.kt
val keywordModel = ViewModelProviders.of(activity as MainActivity).get(KeywordModel::class.java)
keywordModel.getKeyWord().observe(this, Observer<String> {
mPresenter.requestArticleList(requireNotNull(it), true)
})
複製程式碼
以上就完成了 ViewModel 的基本使用了。
原始碼分析
在分析原始碼之前,應該思考一下,如果這個功能由你來寫,你會是怎麼實現,用什麼資料結構去存資料,然後根據這個功能,可以擴充出什麼其他用途。
首先看建立方法。ViewModelProviders 的 of 方法可以傳入 fragment 或者是 activity,就是說你可以在fragment 或者 activity 建立 viewmodel,而這個 viewmodel 只會依賴你的建立的 activity 或者是 fragment(都是同一個viewmodel)。我們先來分析 傳入引數是 activity 的這種情況。
//ViewModelProviders.java
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(ViewModelStores.of(activity), factory);
}
複製程式碼
呼叫了兩個引數的 of 方法,在 of 方法裡面,通過 activity 來獲取 applicaiton 從而來建立預設的 AndroidViewModelFactory 工廠。 這個工廠是 ViewModelProvider 的靜態內部類。主作用是同是通過反射來獲取 viewmodel
//AndroidViewModelFactory.java
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
try {
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (Exception e) {
throw new RuntimeException("", e);
}
}
return super.create(modelClass);
}
複製程式碼
還有另外一個引數 ViewModelStores.of(activity),判斷了當前activity有沒有實現 ViewModelStoreOwner 介面,而我們沒實現,就下一步吧。呼叫了 HolderFragment 的 holderFragmentFor 來建立一個 holderFragment, 新增到 activity 上(當然這個fragment 沒介面),而這個 fragment 上有 mViewModelStore 的例項。
//ViewModelStores.java
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
return holderFragmentFor(activity).getViewModelStore();
}
複製程式碼
而在 ViewModelStore 裡面有 hashmap ,通過 key 獲取儲存 viewmodel 。所以 viewmodel 使用了hashmap來儲存 viewmodel 啦。
//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);
}
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
複製程式碼
而看回 ViewModelProvider 這個類,通過get方法來獲取viewmodel,從下面的的 get 方法可以看到 hashmap 的 key 是由DEFAULT_KEY 字首和的 viewmodel 的 canonicalName 來組成。並且get的時候 會先從hashmah 中獲取viewmodel ,不存在這個viewmodel,再從 mFactory 裡面建立 viewmodel ,並存進 hashmap。
//ViewModelProvider.java
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;
}
複製程式碼
同理
當viewmodelproviders.of 傳入fragment時,也會走上面的流程,只不過生成holderfragment的時候用getChildFragmentManager()
//HolderFragment.java
HolderFragment holderFragmentFor(Fragment parentFragment) {
FragmentManager fm = parentFragment.getChildFragmentManager();
HolderFragment holder = findHolderFragment(fm);
if (holder != null) {
return holder;
}
holder = mNotCommittedFragmentHolders.get(parentFragment);
if (holder != null) {
return holder;
}
parentFragment.getFragmentManager()
.registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false);
holder = createHolderFragment(fm);
mNotCommittedFragmentHolders.put(parentFragment, holder);
return holder;
}
複製程式碼
小總結
- ViewModel 是從 ViewModelproviders 通過 viewmodelandroidfactory 工廠 和 holderfragment 獲取到一個 viewmodelprovider ,呼叫viewmodelprovider 的 get 方法從 hashmap 獲取對用的 viewmodel。 管理 hashmap 的ViewModelStore 在 holderfragment 裡面,而這個holderfragment為什麼不會隨著 activity 的重建而不銷燬呢,這是因為對應的 holderfragment 設定了setRetainInstance(true)。不會隨著重建 activity 而銷燬。
- 可以建立多個 viewmodel,而 activity 和 fragment 中 holderfragment 只有一個。
ps:在 RxPermissions 中也用到setRetainInstance這個屬性。
2018年08月27日09:22:29更新
ViewModel 儲存 Activity Fragment View 等例項時,會因為 Activity 或者 Fragment 的銷燬 造成記憶體洩漏,所以 ViewModel 不可以儲存 Activity Fragment View 等例項。