剖析 Android 架構(gòu)組件之 ViewModel

ViewModel 是 Android 架構(gòu)組件之一栓辜,用于分離 UI 邏輯與 UI 數(shù)據(jù)恋拍。在發(fā)生 Configuration Changes 時(shí),它不會(huì)被銷毀藕甩。在界面重建后施敢,方便開發(fā)者呈現(xiàn)界面銷毀前的 UI 狀態(tài)。

本文主要分析 ViewModel 的以下3個(gè)方面:

  • 獲取和創(chuàng)建過程狭莱。
  • Configuration Changes 存活原理僵娃。
  • 銷毀過程。

1. 依賴庫

implementation "androidx.fragment:fragment:1.0.0"
implementation "androidx.lifecycle:lifecycle-viewmodel:2.0.0"
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"

2. 主要類與接口

import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelProvider.Factory;
import androidx.lifecycle.ViewModelProviders;
import androidx.lifecycle.ViewModelStore;
import androidx.lifecycle.ViewModelStoreOwner;

3. ViewModel

ViewModel 是一個(gè)抽象類贩毕,類中只定義了一個(gè)空實(shí)現(xiàn)的 onCleared() 方法悯许。

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() {
    }
}

3.1 AndroidViewModel

AndroidViewModel 類擴(kuò)展了 ViewModel 類,增加了 Application 字段辉阶,在構(gòu)造方法初始化先壕,并提供了 getApplication() 方法。

public class AndroidViewModel extends ViewModel {
    private Application mApplication;

    public AndroidViewModel(@NonNull Application application) {
        mApplication = application;
    }

    /**
     * Return the application.
     */
    @NonNull
    public <T extends Application> T getApplication() {
        return (T) mApplication;
    }
}

4. 獲取和創(chuàng)建過程分析

獲取 ViewModel 對象代碼如下:

ViewModelProviders.of(activityOrFragment).get(ViewModel::class.java)

4.1 ViewModelProviders

ViewModelProviders 類提供了4個(gè)靜態(tài)工廠方法 of() 創(chuàng)建新的 ViewModelProvider 對象谆甜。

ViewModelProviders.of(Fragment)
ViewModelProviders.of(FragmentActivity)
ViewModelProviders.of(Fragment, Factory)
ViewModelProviders.of(FragmentActivity, Factory)

4.2 ViewModelProvider

ViewModelProvider 負(fù)責(zé)提供 ViewModel 對象垃僚,類中定義了以下兩個(gè)字段:

private final Factory mFactory;
private final ViewModelStore mViewModelStore;

先說說這兩個(gè)類的功能。

4.3 ViewModelProvider.Factory

Factory 接口定義了一個(gè)創(chuàng)建 ViewModel 的接口 create()规辱,ViewModelProvider 在需要時(shí)調(diào)用該方法新建 ViewModel 對象谆棺。

public interface Factory {
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);
}

Android 已經(jīng)內(nèi)置了2個(gè) Factory 實(shí)現(xiàn)類,分別是:

  • AndroidViewModelFactory 實(shí)現(xiàn)類罕袋,可以創(chuàng)建 ViewModelAndroidViewModel 子類對象改淑。
  • NewInstanceFactory 類,只可以創(chuàng)建 ViewModel 子類對象浴讯。

它們的實(shí)現(xiàn)都是通過反射機(jī)制調(diào)用 ViewModel 子類的構(gòu)造方法創(chuàng)建對象朵夏。

public static class NewInstanceFactory implements Factory {
    @Override
    public <T extends ViewModel> T create(Class<T> modelClass) {
        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);
        }
    }
}

AndroidViewModelFactory 繼承 NewInstanceFactory 類,是個(gè)單例榆纽,支持創(chuàng)建 AndroidViewModel 子類對象仰猖。

public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

    private static AndroidViewModelFactory sInstance;

    public static AndroidViewModelFactory getInstance(Application application) {
        if (sInstance == null) {
            sInstance = new AndroidViewModelFactory(application);
        }
        return sInstance;
    }

    private Application mApplication;

    public AndroidViewModelFactory(Application application) {
        mApplication = application;
    }

    @Override
    public <T extends ViewModel> T create(Class<T> modelClass) {
        if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
            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);
    }
}

4.4 ViewModelStore

ViewModelStore 類中維護(hù)一個(gè) Map<String, ViewModel> 對象存儲(chǔ)已創(chuàng)建的 ViewModel 對象,并提供 put()get() 方法奈籽。

public class ViewModelStore {
    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    
    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }
}

4.5 ViewModelStoreOwner

ViewModelStore 是來自于 FragmentActivityFragment饥侵,它們實(shí)現(xiàn)了 ViewModelStoreOwner 接口,返回當(dāng)前 UI 作用域里的 ViewModelStore 對象衣屏。

public interface ViewModelStoreOwner {
    ViewModelStore getViewModelStore();
}

Fragment 類中的實(shí)現(xiàn)如下:

public ViewModelStore getViewModelStore() {
    if (getContext() == null) {
        throw new IllegalStateException("Can't access ViewModels from detached fragment");
    }
    if (mViewModelStore == null) {
        mViewModelStore = new ViewModelStore();
    }
    return mViewModelStore;
}

FragmentActivity 類中的實(shí)現(xiàn)如下:

public ViewModelStore getViewModelStore() {
    if (getApplication() == null) {
        throw new IllegalStateException("Your activity is not yet attached to the "
                + "Application instance. You can't request ViewModel before onCreate call.");
    }
    if (mViewModelStore == null) {
        mViewModelStore = new ViewModelStore();
    }
    return mViewModelStore;
}

4.6 創(chuàng)建 ViewModelProvider

回到 of() 方法的實(shí)現(xiàn)躏升。

public static ViewModelProvider of(FragmentActivity activity, Factory factory) {
    Application application = checkApplication(activity);
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(activity.getViewModelStore(), factory);
}

在創(chuàng)建 ViewModelProvider 對象時(shí)需要傳入 ViewModelStoreFactory 對象。若 factorynull狼忱,將使用 AndroidViewModelFactory 單例對象煮甥。

4.7 獲取 ViewModel 對象

調(diào)用 ViewModelProvider 對象的 get() 方法獲取 ViewModel 對象盗温,如果在 ViewModelStore 里不存在藕赞,則使用 Factory 創(chuàng)建一個(gè)新的對象并存放到 ViewModelStore 里成肘。

public <T extends ViewModel> T get(String key, Class<T> modelClass) {
    ViewModel viewModel = mViewModelStore.get(key);

    if (modelClass.isInstance(viewModel)) {
        return (T) viewModel;
    }

    viewModel = mFactory.create(modelClass);
    mViewModelStore.put(key, viewModel);

    return (T) viewModel;
}

5. Configuration Changes 存活原理

當(dāng) ActivityFragment 被系統(tǒng)重建時(shí),ViewModel 對象不會(huì)被銷毀斧蜕,新的 ActivityFragment 對象拿到的是同一個(gè) ViewModel 對象双霍。

FragmentActivity#onRetainNonConfigurationInstance() 方法中,會(huì)將 ViewModelStore 對象保留起來批销。

public final Object onRetainNonConfigurationInstance() {
    Object custom = onRetainCustomNonConfigurationInstance();

    FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

    if (fragments == null && mViewModelStore == null && custom == null) {
        return null;
    }

    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = mViewModelStore;
    nci.fragments = fragments;
    return nci;
}

然后在 onCreate() 方法能獲取之前保留起來的 ViewModelStore 對象洒闸。

protected void onCreate(Bundle savedInstanceState) {
    mFragments.attachHost(null /*parent*/);
    super.onCreate(savedInstanceState);

    NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
    if (nc != null) {
        mViewModelStore = nc.viewModelStore;
    }
    // ...
}

Fragment 作用域里是如何實(shí)現(xiàn)的呢?在 FragmentActivityonRetainNonConfigurationInstance() 方法中里有這樣一句代碼:

FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

實(shí)現(xiàn)保留的機(jī)制是一樣的均芽,只不過放在 FragmentManagerNonConfig 對象中丘逸。是在 FragmentManager#saveNonConfig() 方法中將 ViewModelStore 對象保存到 FragmentManagerNonConfig 里的。

void saveNonConfig() {
    ArrayList<Fragment> fragments = null;
    ArrayList<FragmentManagerNonConfig> childFragments = null;
    ArrayList<ViewModelStore> viewModelStores = null;
    if (mActive != null) {
        for (int i=0; i<mActive.size(); i++) {
            Fragment f = mActive.valueAt(i);
            if (f != null) {
                if (f.mRetainInstance) {
                    if (fragments == null) {
                        fragments = new ArrayList<Fragment>();
                    }
                    fragments.add(f);
                    f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1;
                    if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f);
                }
                FragmentManagerNonConfig child;
                if (f.mChildFragmentManager != null) {
                    f.mChildFragmentManager.saveNonConfig();
                    child = f.mChildFragmentManager.mSavedNonConfig;
                } else {
                    // f.mChildNonConfig may be not null, when the parent fragment is
                    // in the backstack.
                    child = f.mChildNonConfig;
                }

                if (childFragments == null && child != null) {
                    childFragments = new ArrayList<>(mActive.size());
                    for (int j = 0; j < i; j++) {
                        childFragments.add(null);
                    }
                }

                if (childFragments != null) {
                    childFragments.add(child);
                }
                if (viewModelStores == null && f.mViewModelStore != null) {
                    viewModelStores = new ArrayList<>(mActive.size());
                    for (int j = 0; j < i; j++) {
                        viewModelStores.add(null);
                    }
                }

                if (viewModelStores != null) {
                    viewModelStores.add(f.mViewModelStore);
                }
            }
        }
    }
    if (fragments == null && childFragments == null && viewModelStores == null) {
        mSavedNonConfig = null;
    } else {
        mSavedNonConfig = new FragmentManagerNonConfig(fragments, childFragments,
        viewModelStores);
    }
}

該方法的調(diào)用順序是:FragmentActivity#onSaveInstanceState() -> FragmentManager#saveAllState() -> FragmentManager#saveNonConfig()掀宋。

6. 銷毀過程

FragmentActivity 類的 onDestory() 方法中深纲。

@Override
protected void onDestroy() {
    super.onDestroy();
    if (mViewModelStore != null && !isChangingConfigurations()) {
        mViewModelStore.clear();
    }
    mFragments.dispatchDestroy();
}

Fragment 類的 onDestory() 方法中。

public void onDestroy() {
    mCalled = true;
    FragmentActivity activity = getActivity();
    boolean isChangingConfigurations = activity != null && activity.isChangingConfigurations();
    if (mViewModelStore != null && !isChangingConfigurations) {
        mViewModelStore.clear();
    }
}

先判斷是否有發(fā)生 Configuration Changes劲妙,如果沒有則會(huì)調(diào)用 ViewModelStoreclear() 方法湃鹊,再一一調(diào)用每一個(gè) ViewModelonCleared() 方法。

public final void clear() {
    for (ViewModel vm : mMap.values()) {
        vm.onCleared();
    }
    mMap.clear();
}

7. 總結(jié)

以上便是 ViewModel 3個(gè)主要過程的剖析镣奋,這里做一下總結(jié)币呵。

  • 通過 ViewModelProviders 創(chuàng)建 ViewModelProvider 對象,調(diào)用該對象的 get() 方法獲取 ViewModel 對象侨颈。 當(dāng) ViewModelStore 里不存在想要的對象余赢,ViewModelProvider 會(huì)使用 Factory 新建一個(gè)對象并存放到 ViewModelStore 里。
  • 當(dāng)發(fā)生 發(fā)生 Configuration Changes 時(shí)哈垢,FragmentActivity 利用 getLastNonConfigurationInstance()妻柒、onRetainNonConfigurationInstance() 方法實(shí)現(xiàn) ViewModelStore 的保留與恢復(fù),進(jìn)而實(shí)現(xiàn) ViewModel 對象的蔽屡猓活蛤奢。
  • 當(dāng) FragmentActivityFragment 被銷毀時(shí),會(huì)根據(jù)是否發(fā)生 Configuration Changes 來決定是否銷毀 ViewModel陶贼。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末啤贩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子拜秧,更是在濱河造成了極大的恐慌痹屹,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枉氮,死亡現(xiàn)場離奇詭異志衍,居然都是意外死亡暖庄,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門楼肪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來培廓,“玉大人,你說我怎么就攤上這事春叫〖缒疲” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵暂殖,是天一觀的道長价匠。 經(jīng)常有香客問我,道長呛每,這世上最難降的妖魔是什么踩窖? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮晨横,結(jié)果婚禮上洋腮,老公的妹妹穿的比我還像新娘。我一直安慰自己颓遏,他們只是感情好徐矩,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著叁幢,像睡著了一般滤灯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上曼玩,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天鳞骤,我揣著相機(jī)與錄音,去河邊找鬼黍判。 笑死豫尽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的顷帖。 我是一名探鬼主播美旧,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼贬墩!你這毒婦竟也來了榴嗅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤陶舞,失蹤者是張志新(化名)和其女友劉穎嗽测,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肿孵,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡唠粥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年疏魏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晤愧。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡大莫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出养涮,到底是詐尸還是另有隱情葵硕,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布贯吓,位于F島的核電站,受9級特大地震影響蜀变,放射性物質(zhì)發(fā)生泄漏悄谐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一库北、第九天 我趴在偏房一處隱蔽的房頂上張望爬舰。 院中可真熱鬧,春花似錦寒瓦、人聲如沸情屹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽垃你。三九已至,卻和暖如春喂很,著一層夾襖步出監(jiān)牢的瞬間惜颇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工少辣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留凌摄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓漓帅,卻偏偏與公主長得像锨亏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子忙干,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容