概述
這篇我們繼續(xù)講 Jetpack顶伞,具體講實(shí)現(xiàn) MVVM架構(gòu)的 ViewModel掂摔。這次的例子會(huì)將 ViewModel和 LiveData結(jié)合使用荆几,然后分析一下 ViewModel的源碼。LiveData將留到下篇文章分析储藐。
上一篇文章MVP基礎(chǔ)架構(gòu)搭建 的例子里,用了MVP的架構(gòu)實(shí)現(xiàn)了網(wǎng)絡(luò)加載的流程嘶是。這次我們要把上次 MVP架構(gòu)升級(jí)為 這次的MVVM钙勃,將上次例子的 Presenter模塊和 Model模塊去掉,只保留網(wǎng)絡(luò)加載引擎聂喇。然后結(jié)合 ViewModel和 LiveData來(lái)實(shí)現(xiàn) MVVM辖源。
1、使用
先導(dǎo)入依賴:
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
我們需要知道希太,ViewModel是一個(gè)抽象類克饶,需要我們?nèi)?shí)現(xiàn)。所以我們先創(chuàng)建一個(gè)實(shí)現(xiàn)類:
public class DataViewModel extends ViewModel {
// LiveData
private MutableLiveData<String> mLiveData;
// 網(wǎng)絡(luò)加載引擎
private RetrofitRequestInterface retrofit;
public DataViewModel(){
// 注釋 1誊辉, 初始化 LiveData
mLiveData = new MutableLiveData();
// 注釋 2矾湃, 初始化網(wǎng)絡(luò)引擎
retrofit = RetrofitInstance.getRetrofitInstance().create(RetrofitRequestInterface.class);
}
public LiveData getLiveData(){
return mLiveData;
}
public void getData(String url){
// 注釋 3, 網(wǎng)絡(luò)請(qǐng)求
Call<ResponseBody> call = retrofit.getRequest(url);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
String result = response.body().string();
// 注釋 4堕澄, 用 LiveData回調(diào)
mLiveData.postValue(result);
} catch (IOException e) {
e.printStackTrace();
mLiveData.postValue(e.getMessage());
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
mLiveData.postValue(t.getMessage());
}
});
}
}
上面我們創(chuàng)建了一個(gè) ViewModel實(shí)現(xiàn)類邀跃,這個(gè)類的作用與MVP的 Presenter有點(diǎn)類似。上面注釋 1和注釋 2分別創(chuàng)建 LiveData對(duì)象和網(wǎng)絡(luò)引擎蛙紫,在注釋 3的地方外界調(diào)用時(shí)執(zhí)行網(wǎng)絡(luò)加載拍屑,然后在注釋 4處用 LiveData回調(diào)加載結(jié)果。
下面看界面如何調(diào)用:
public class MainActivity extends AppCompatActivity {
private Button getData;
private DataViewModel mViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getData = findViewById(R.id.id_get_data);
init();
}
private void init(){
// 注釋5坑傅, 工廠設(shè)計(jì)模式創(chuàng)建 ViewModel對(duì)象
mViewModel = new ViewModelProvider(this).get(DataViewModel.class);
// 以 LiveData對(duì)象設(shè)置數(shù)據(jù)回調(diào)
mViewModel.getLiveData().observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
Toast.makeText(getApplicationContext(), "mag = " + s, Toast.LENGTH_SHORT).show();
}
});
getData.setOnClickListener((View view) -> {
// 網(wǎng)絡(luò)請(qǐng)求
mViewModel.getData("https://www.baidu.com");
});
}
}
上面步驟分別是創(chuàng)建了 ViewModel對(duì)象僵驰、設(shè)置數(shù)據(jù)回調(diào)以及執(zhí)行網(wǎng)絡(luò)請(qǐng)求。這就是 MVVM的基礎(chǔ)實(shí)現(xiàn)唁毒。Demo: ViewModel
現(xiàn)在我們先拋出兩個(gè)問(wèn)題:
1蒜茴、上面注釋 5處為什么使用 ViewModelProvider創(chuàng)建 ViewModel對(duì)象,而不直接 new一個(gè)枉证?
2矮男、傳說(shuō) ViewModel可以避免內(nèi)存泄漏,如何做到的室谚?
下面我們來(lái)分析一下 ViewModel的源碼毡鉴。
2崔泵、源碼分析
先看上面 ViewModelProvider的構(gòu)造方法和 get() 方法:
// ViewModelProvider.java
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
// 注釋 6, 通過(guò)ViewModelStore 擁有者的 getDefaultViewModelProviderFactory方法獲取工廠對(duì)象
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
: ViewModelProvider.NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull ViewModelProvider.Factory factory) {
//注釋7猪瞬, 保存工廠 Factory和 緩存對(duì)象憎瘸,ViewModelStore
mFactory = factory;
mViewModelStore = store;
}
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();,
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//注釋 9, 從緩存 mViewModelStore中取 ViewModel的對(duì)象
// Key值是 “androidx.lifecycle.ViewModelProvider.DefaultKey” + 類名
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
......
// 是響應(yīng)類的對(duì)象則直接返回
return (T) viewModel;
}
......
......
//注釋 10陈瘦, 緩存中沒(méi)有 ViewModel對(duì)象幌甘,用工廠創(chuàng)建,并存入緩存
viewModel = (mFactory).create(modelClass);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
上面注釋 6和注釋 7 處ViewModelProvider重載的構(gòu)造器里痊项,分別調(diào)用了Activity的 getViewModelStore()和 getDefaultViewModelProviderFactory()方法獲取或創(chuàng)建了一個(gè) ViewModel的緩存對(duì)象 ViewModelStore以及工廠對(duì)象 Factory锅风,并保存起來(lái)。其中緩存對(duì)象 ViewModelStore內(nèi)部是一個(gè) HashMap鞍泉,以鍵值對(duì)的形式緩存 ViewModel對(duì)象皱埠。
再看上面注釋 9和注釋 10處 get()的重載方法,ViewModelProvider創(chuàng)建 ViewModel對(duì)象前咖驮,首先從緩存 ViewModelStore中取边器,key值是“androidx.lifecycle.ViewModelProvider.DefaultKey” + 類名。當(dāng)緩存中沒(méi)有該對(duì)象時(shí)就用工廠 Factory創(chuàng)建托修,并存入緩存和返回忘巧。
Factory是一個(gè)抽象類,實(shí)現(xiàn)類對(duì)象是從上面注釋 6處通過(guò) Activity的 getDefaultViewModelProviderFactory()方法獲取的睦刃。我們來(lái)看看 ComponentActivity的這個(gè)方法如何創(chuàng)建工廠實(shí)現(xiàn)類的對(duì)象:
// ComponentActivity.java
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
......
if (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
//注釋 11砚嘴, 獲取了 Application對(duì)象,創(chuàng)建SavedStateViewModelFactory對(duì)象
getApplication(),
this,
getIntent() != null ? getIntent().getExtras() : null);
}
return mDefaultFactory;
}
// SavedStateViewModelFactory.java
public SavedStateViewModelFactory(@NonNull Application application,
@NonNull SavedStateRegistryOwner owner,
@Nullable Bundle defaultArgs) {
mSavedStateRegistry = owner.getSavedStateRegistry();
mLifecycle = owner.getLifecycle();
mDefaultArgs = defaultArgs;
mApplication = application;
//注釋 12眯勾, 創(chuàng)建 AndroidViewModelFactory工廠
mFactory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
......
// 注釋 13枣宫,通過(guò) AndroidViewModelFactory創(chuàng)建 ViewModel對(duì)象
return mFactory.create(modelClass);
......
}
上面注釋 11處,ComponentActivity獲取了 Application對(duì)象吃环,創(chuàng)建了 Factory的子類 SavedStateViewModelFactory的對(duì)象也颤。然后注釋 12處SavedStateViewModelFactory又以 application作為參數(shù)創(chuàng)建了 Factory的另一實(shí)現(xiàn)類 AndroidViewModelFactory的對(duì)象,并用該對(duì)象創(chuàng)建 ViewModel對(duì)象郁轻。
下面重點(diǎn)看看 AndroidViewModelFactory這個(gè)工廠:
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static ViewModelProvider.AndroidViewModelFactory sInstance;
@NonNull
public static ViewModelProvider.AndroidViewModelFactory getInstance(@NonNull Application application) {
// 注釋14翅娶, 單例模式創(chuàng)建工廠實(shí)例
if (sInstance == null) {
sInstance = new ViewModelProvider.AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication;
// 傳入 Application對(duì)象
public AndroidViewModelFactory(@NonNull Application application) {
mApplication = application;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
try {
//注釋 15, 工廠方法設(shè)計(jì)模式創(chuàng)建 ViewModel對(duì)象好唯,持有 Application的引用
return modelClass.getConstructor(Application.class).newInstance(mApplication);
}
......
}
return super.create(modelClass);
}
}
上面 AndroidViewModelFactory 這個(gè)工廠竭沫,注釋14處用單例模式創(chuàng)建了該工廠對(duì)象,然后在注釋 15處用工廠方法設(shè)計(jì)模式創(chuàng)建 ViewModel對(duì)象骑篙。有一點(diǎn)值得注意的是蜕提,ViewModel創(chuàng)建時(shí)傳入的是 mApplication參數(shù)。也就是說(shuō) ViewModel對(duì)象最終并不持有 Activity的引用靶端,而是持有了 Application的引用谎势。
經(jīng)過(guò)上面的分析凛膏,我們現(xiàn)在應(yīng)該可以回答上面的兩個(gè)問(wèn)題了:
1、ViewModel對(duì)象使用了 ViewModelProvider獲取而不直接 new脏榆,是因?yàn)?ViewModelProvider有緩存機(jī)制猖毫,內(nèi)部用HashMap以鍵值對(duì)的形式緩存 ViewModel對(duì)象。當(dāng)緩存中沒(méi)有相應(yīng)對(duì)象時(shí)才用工廠去創(chuàng)建须喂。
2吁断、ViewModel對(duì)象并不持有 Activity的引用,而是持有了 Application的引用坞生。這是避免 Activity內(nèi)存泄漏的一種方式仔役。