Android架構(gòu)組件(三):ViewModel

前言

上篇我們分析了Livedata的使用及原理,相信我們已經(jīng)學(xué)會(huì)了使用Livedata來存儲(chǔ)數(shù)據(jù),并在觀察者組件中實(shí)現(xiàn)回調(diào)方法,來動(dòng)態(tài)更新UI數(shù)據(jù)。這里奉上(雙膝已經(jīng)跪爛了...)上兩篇的地址:
Android架構(gòu)組件(一):Lifecycle
Android架構(gòu)組件(二):LiveData
方便大家進(jìn)行查閱和回顧。

那么,接下來我們要學(xué)習(xí)我們的第三個(gè)架構(gòu)組件——Viewmodel,我們從字面上理解,它肯定和view,model有關(guān)聯(lián),它是負(fù)責(zé)準(zhǔn)備和管理UI組件(activity/fragment)相關(guān)的數(shù)據(jù)類,也就是說Viewmodel是用來管理UI相關(guān)數(shù)據(jù)的,同時(shí)Viewmodel還可以負(fù)責(zé)UI間組件的通訊。

Viewmodel是什么?

我們已經(jīng)知道,Viewmodel有以下兩點(diǎn)作用:

  1. 用來管理數(shù)據(jù)(model)和UI組件(view)的數(shù)據(jù)類
  2. 負(fù)責(zé)UI組件之間的通訊
  • 管理數(shù)據(jù)(model)和UI組件(view)的數(shù)據(jù)
    我們先來看一下它的基本使用:
public class MyViewModel extends ViewModel {
    //如果不熟悉Livedata用法可以閱讀上一篇博客
    private MutableLiveData<List<User>> users;
    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<Users>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // 異步調(diào)用獲取用戶列表
        ...
        users.setValue(data);
    }
}

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // 更新 UI
        });
    }
}

用法很簡單,
我們?cè)?strong>viewmodel中定義一個(gè)livedata的集合,通過網(wǎng)絡(luò)獲取數(shù)據(jù)后,調(diào)用setValue方法通知觀察者(UI)在活躍狀態(tài)下時(shí)更新數(shù)據(jù)。
在activity中,我們初始化viewmodel拿到livedata并在他的onchanged()方法里做UI相關(guān)操作。
這里我們發(fā)現(xiàn)viewmodel做了一個(gè)中間人的角色,它管理著model與view之間相互關(guān)聯(lián)的數(shù)據(jù),這樣我們就可以把數(shù)據(jù)相關(guān)(model)操作放到viewmodel中,把UI操作放到view中,完全由viewmodel管理,使model與view層完全解耦。

  • 負(fù)責(zé)UI間組件之間的通訊
    一個(gè)activity中的多個(gè)Fragment互相間通訊時(shí)很常見的需求,我們可以使用activity中的viewmodel來實(shí)現(xiàn)fragment之間數(shù)據(jù)的共享。
    下面這個(gè)例子也很簡單:
//我們定義viewmodel并設(shè)置set,get方法
public class CommunicateViewModel extends ViewModel {
    private MutableLiveData<String> mNameLiveData;

    public LiveData<String> getName(){
        if (mNameLiveData == null) {
            mNameLiveData = new MutableLiveData<>();
        }
        return mNameLiveData;
    }

    public void setName(String name){
        if (mNameLiveData != null) {
            mNameLiveData.setValue(name);
        }
    }
}

//我們通過fragment1設(shè)置name的值
public class FragmentOne extends Fragment {
    private CommunicateViewModel mCommunicateViewModel;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mCommunicateViewModel = ViewModelProviders.of(getActivity()).get(CommunicateViewModel.class);
    }

    @OnClick(R.id.btn_set_name)
    void onViewClicked(View v){
        switch (v.getId()){
            case R.id.btn_set_name:
                mCommunicateViewModel.setName("Jane");
                break;
        }
    }
}

//在fragment2中我們通過同一個(gè)viewmodel拿到livedata并更新UI
public class FragmentTwo extends Fragment {
    private CommunicateViewModel mCommunicateViewModel;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mCommunicateViewModel = ViewModelProviders.of(getActivity()).get(CommunicateViewModel.class);
        mCommunicateViewModel.getName().observe(this, name -> mTvName.setText(name));
    }
}

上述代碼我們知道了,兩個(gè)fragment的是通過同一個(gè)viewmodel進(jìn)行組件之間的通訊,這里值得注意的是兩個(gè)fragment中初始化viewmodel時(shí)傳入的都是getActivity() 這也就意味著他們傳入的是同一個(gè)對(duì)象,如果不同,那么得到的將是兩個(gè)viewmodel對(duì)象,也不會(huì)收到通知進(jìn)行更新了。

這種組件間通訊的好處在于

  • activity不需要做任何事情
  • fragment不需要知道彼此,而是通過viewmodel進(jìn)行聯(lián)系

Viewmodel分析

我們先來看下它的聲明周期圖:


viewmodel生命周期

從上圖我們分析得出,左側(cè)表示Activity的生命周期狀態(tài),右側(cè)綠色部分表示ViewModel的生命周期范圍。當(dāng)屏幕旋轉(zhuǎn)的時(shí)候,Activity會(huì)被recreate,Activity會(huì)經(jīng)過幾個(gè)生命周期方法,但是這個(gè)時(shí)候ViewModel還是之前的對(duì)象,并沒有被重新創(chuàng)建,只有當(dāng)Activity的finish()方法被調(diào)用時(shí),ViewModel.onCleared()方法會(huì)被調(diào)用,對(duì)象才會(huì)被銷毀。這張圖很好的描述了當(dāng)Activity被recreate時(shí),ViewModel的生命周期。

另外,有個(gè)注意的地方:在ViewModel中不要持有Activity的引用。為什么要注意這一點(diǎn)呢?從上面的圖我們看到,當(dāng)Activity被recreate時(shí),ViewModel對(duì)象并沒有被銷毀,如果Model持有Activity的引用時(shí)就可能會(huì)導(dǎo)致內(nèi)存泄漏。那如果你要使用到Context對(duì)象怎么辦呢,ViewModel的子類AndroidViewModel為我們很好的解決了這一問題,我們稍后會(huì)分析。

我們?cè)賮砜匆幌?strong>viewmodel的類圖:

viewmodel類圖

根據(jù)這張類圖,我們來分析一下:

  • ViewModelProviders是ViewModel工具類,該類提供了通過Fragment和Activity得到ViewModel的方法,而具體實(shí)現(xiàn)又是由ViewModelProvider實(shí)現(xiàn)的。
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,@Nullable Factory factory) {
    Application application = checkApplication(activity);
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    //param1是ViewModelStore,param2是工廠類
    return new ViewModelProvider(ViewModelStores.of(activity), factory);
    }
//他在of()方法里初始化了ViewModelProvider里的工廠類AndroidViewModelFactory,并renturn了ViewModelProvider對(duì)象
//內(nèi)部還有一些check方法用于檢查Fragment是否Attached to Activity,Activity的Application對(duì)象是否為空等
  • ViewModelStores是ViewModelStore的工廠方法類,它會(huì)關(guān)聯(lián)Fragment,activity
    上個(gè)代碼片段我們看見它在of()方法里renturn時(shí)new了一個(gè)provider對(duì)象并通過ViewModelStores.of()得到stroe對(duì)象

//ViewModelStores.of(activity)方法返回了ViewModelStore對(duì)象
return new ViewModelProvider(ViewModelStores.of(activity), factory);

public static ViewModelStore of(@NonNull FragmentActivity activity) {
    if (activity instanceof ViewModelStoreOwner) {
        return ((ViewModelStoreOwner) activity).getViewModelStore();
    }
    //我們看見這里有一個(gè)holderFragmentFor對(duì)象(HolderFragment)
    return holderFragmentFor(activity).getViewModelStore();
}

public HolderFragment() {
    //將這個(gè)方法設(shè)置為true就可以使當(dāng)前Fragment在Activity重建時(shí)存活下來,如果不設(shè)置或者設(shè)置為false,當(dāng)前Fragment會(huì)在Activity重建時(shí)同樣發(fā)生重建,以至于被新建的對(duì)象所替代。
    setRetainInstance(true);
    //這樣就解決了旋轉(zhuǎn)屏幕時(shí)因?yàn)橹亟▽?dǎo)致數(shù)據(jù)丟失的問題
}
//在HoldFragment中初始化了ViewModelStore用于在銷毀時(shí)clear,釋放掉viewmodel
private ViewModelStore mViewModelStore = new ViewModelStore();
@Override
    public void onDestroy() {
        super.onDestroy();
        mViewModelStore.clear();
    }

    @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        return mViewModelStore;
    }
  • ViewModelStore是存儲(chǔ)ViewModel的類,具體實(shí)現(xiàn)是通過HashMap來保存ViewModle對(duì)象。

//viewmodelStroe用戶存儲(chǔ)Viewmodel,并提供set,get方法和clear方法
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()) {
            //這里調(diào)用了viewmodel的onCleared()方法
            vm.onCleared();
        }
        mMap.clear();
    }
}
  • ViewModelProvider是實(shí)現(xiàn)ViewModel創(chuàng)建、獲取的工具類。在ViewModelProvider中定義了一個(gè)創(chuàng)建ViewModel的接口類——Factory。ViewModelProvider中有個(gè)ViewModelStore對(duì)象,用于存儲(chǔ)ViewModel對(duì)象。

//構(gòu)造方法中我們傳入了stroe和工廠類
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
    mFactory = factory;
    this.mViewModelStore = store;
}
//我們使用的.get(modele.class)方法最終會(huì)調(diào)用這個(gè)get方法
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    //從map中獲取model對(duì)象
    ViewModel viewModel = mViewModelStore.get(key);
    //判斷是否是同一個(gè)對(duì)象?如果是return此viewmode
    if (modelClass.isInstance(viewModel)) {
        return (T) viewModel;
    } else {
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    //如果不是則通過工廠類創(chuàng)建,然后緩存進(jìn)stroe,并return
    viewModel = mFactory.create(modelClass);
    mViewModelStore.put(key, viewModel);
    return (T) viewModel;
}

好了,至此我們通過閱讀源碼,已經(jīng)對(duì)viewmodel的工作原理有了一定的了解,那么們就來總結(jié)一下它如何通過一系列操作,來做到對(duì)view和model進(jìn)行管理的。

//1. 首先我們會(huì)在繼承viewmodel的類中,做一些數(shù)據(jù)操作(初始化livedata),并提供set,get方法返回livedata對(duì)象。(代碼省略...查看開頭基本用法的代碼塊)
//2. 我們?cè)趘iew組件(activity/fragment)中拿到viewmodel對(duì)象
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
//3. of()方法返回viewmodelprodiver對(duì)象
 public static ViewModelProvider of(@NonNull FragmentActivity activity,@Nullable Factory factory) {
    Application application = checkApplication(activity);
    if (factory == null) {
    //4. 在of方法里初始化ViewModelProvider中AndroidViewModelFactory對(duì)象
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    //5. ViewModelProvider中傳入ViewModelstore和factory對(duì)象
    return new ViewModelProvider(ViewModelStores.of(activity), factory);
    }
//6. 工廠類 ViewModelStores.of(activity)方法返回ViewModelStore對(duì)象
public static ViewModelStore of(@NonNull FragmentActivity activity) {
    if (activity instanceof ViewModelStoreOwner) {
        return ((ViewModelStoreOwner) activity).getViewModelStore();
    }
    //7.holderFragmentFor對(duì)象(HolderFragment)解決了屏幕旋轉(zhuǎn)時(shí)數(shù)據(jù)保存。setRetainInstance(true);在里面初始化了ViewModelStore對(duì)象
    return holderFragmentFor(activity).getViewModelStore();
}
//8. 這時(shí)我們拿到了ViewModelProviders.of(this)返回的provider對(duì)象,然后調(diào)用get方法.get(MyViewModel.class);最終走到此get()方法
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    //從map中獲取model對(duì)象
    ViewModel viewModel = mViewModelStore.get(key);
    //判斷是否是同一個(gè)對(duì)象?如果是return此viewmode
    if (modelClass.isInstance(viewModel)) {
        return (T) viewModel;
    } else {
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    //如果不是則通過工廠類創(chuàng)建,然后緩存進(jìn)stroe,并return
    viewModel = mFactory.create(modelClass);
    mViewModelStore.put(key, viewModel);
    return (T) viewModel;
}
//9. 返回了viewmodel對(duì)象,通過viewmodel的get方法拿到livedata對(duì)象,并在ui組件處于活躍狀態(tài)時(shí)更新UI
model.getUsers().observe(this, users -> {
            // 更新 UI
        });

參考&感謝

Android架構(gòu)組件——ViewModel

玩Android

總結(jié)

Viewmodel的職責(zé)是為UI組件管理數(shù)據(jù)。規(guī)范化viewmodel的使用方式,不要在viewmodel層中持有UI層的引用,避免因viewmodel超長的生命周期,導(dǎo)致內(nèi)存泄漏。實(shí)現(xiàn)UI組件和數(shù)據(jù)間的管理和解耦,才是這個(gè)框架帶給我們的理解。
通過我們對(duì)源碼的分析,它的功能并不復(fù)雜,但設(shè)計(jì)的十分巧妙,背后摻雜的思想和理念才是值得去反復(fù)揣度的。它可以更好的實(shí)現(xiàn)把業(yè)務(wù)代碼下沉到viewmodel中實(shí)現(xiàn),既保證了UI組件中代碼的清爽,又可以實(shí)現(xiàn)對(duì)數(shù)據(jù)的管理。

Viewmodel可以用于activity中不同fragment之間的通信,也可以用作Fragment之間一種解耦方式。

接下來我們會(huì)講到Android架構(gòu)的另一個(gè)組件Room,來看下這個(gè)數(shù)據(jù)庫能帶給我們哪些驚艷?
當(dāng)學(xué)習(xí)完所有的組件后,我們就開始嘗試著去搭一款適合自己的MVVM框架,用于加深我們對(duì)Android架構(gòu)組件的學(xué)習(xí),從而做到學(xué)以致用。

Android架構(gòu)組件系列文章

我的博客(Power)
Android架構(gòu)組件(一):Lifecycle
Android架構(gòu)組件(二):LiveData
Android架構(gòu)組件(三):Viewmodel
Android架構(gòu)組件(四):Room

感謝您的閱讀和支持!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容