Android Architecture Component源碼解析之ViewModel

目錄

源碼解析目錄

問(wèn)題

用過(guò)AAC的人都知道,ViewModel的使用是很簡(jiǎn)單的,沒(méi)有太多復(fù)雜的東西,而ViewModel本身的源碼也異常簡(jiǎn)單。真正“神奇”的地方在于ViewModel的生命周期:

在Activity的configuration(配置)發(fā)生變化時(shí),Activity會(huì)被重建,但是ViewModel卻不會(huì),直至Activity真正消失的時(shí)候,ViewModel的onCleared方法才會(huì)被調(diào)用。這就是我們的核心問(wèn)題,這一切是怎么實(shí)現(xiàn)的?
(源碼版本androidx.lifecycle:lifecycle-viewmodel:2.2.0

1. ViewModel的獲取

ViewModel本身是異常簡(jiǎn)單的:

public abstract class ViewModel {

    protected void onCleared() {
    }

}

ViewModel就是這樣,僅僅包含一個(gè)onCeared方法(最初的ViewModel的確只有這一個(gè)方法,后來(lái)又增加了一些方法,但是這些方法都不是給我們使用的,所以可以忽略)。
AAC給我們提供的獲取ViewModel的方法很簡(jiǎn)單:
ViewModelProvider(activity/fragment).get(ViewModel.class) (之前更常見(jiàn)的一種方式是ViewModelProviders.of(activity/fragment).get(ViewModel.class),但是現(xiàn)在這種方式已經(jīng)被刪除了)。

我們來(lái)看一下ViewModelProvider是怎么實(shí)現(xiàn)的:

@SuppressWarnings("WeakerAccess")
public class ViewModelProvider {

    private static final String DEFAULT_KEY =
            "androidx.lifecycle.ViewModelProvider.DefaultKey";

    /**
     * 創(chuàng)建ViewModel的Factory
     */
    public interface Factory {
        @NonNull
        <T extends ViewModel> T create(@NonNull Class<T> modelClass);
    }

    private final Factory mFactory;
    private final ViewModelStore mViewModelStore;

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        //這里的ViewModelStoreOwner 一般而言不是Activity就是Fragment,并且它們都實(shí)現(xiàn)了HasDefaultViewModelProviderFactory,也就是說(shuō)Activity/Fragment都會(huì)提供默認(rèn)的ViewModelFactory
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }

    /**
     * @param store   ViewModel存儲(chǔ)的地方
     * @param factory ViewModel構(gòu)建的地方
     */
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }

    /**
     * 如果不提供key,默認(rèn)的key就是ViewModel的類名(有前綴)
     */
    @NonNull
    @MainThread
    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);
    }

    /**
     * 如果ViewModel在ViewModelStore中已經(jīng)存在,則可以直接返回使用
     * 如果不存在,則通過(guò)Factory新建,默認(rèn)的工廠是SavedStateViewModelFactory,新建ViewModel的方式是反射
     */
    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            //給你一個(gè)機(jī)會(huì)再重新獲取ViewModel時(shí)做一些操作
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }
}

總結(jié)一下ViewModelProvider的作用。ViewModelProvider是我們獲取ViewModel的地方,創(chuàng)建ViewModelProvider需要我們提供兩個(gè)參數(shù):1. ViewModelStore 2. ViewModelProvider.Factory。它們的作用也很明了,分別用來(lái)存儲(chǔ)ViewModel和創(chuàng)建ViewModel。首先在ViewModelStore中查找,沒(méi)有的話再通過(guò)Factory創(chuàng)建。
ViewModelProviderget方法是可以提供一個(gè)key來(lái)說(shuō)明我們需要的是哪個(gè)ViewModel。不過(guò),我們可能不常提供,不提供的話默認(rèn)的key是ViewModel的類名(前面加了一個(gè)前綴)。如果我們的Activity或者Fragment需要同一種ViewModel類的兩個(gè)對(duì)象時(shí),key的作用就很明顯了。

2. ViewModel的創(chuàng)建和存儲(chǔ)

2.1 ViewModelProvider.Factory

默認(rèn)的ViewModelProvider.Factory是SavedStateViewModelFactory,從這個(gè)名字可以看出,這是一個(gè)可以為ViewModel提供SavedState的Factory,ViewModel的SavedState是2.x版本新添加的功能,目的是為了在ViewModel中方便的保存狀態(tài),在Activity重建的時(shí)候使用。不了解這些沒(méi)關(guān)系,因?yàn)槿绻悴皇褂眠@個(gè)SavedState功能的話,這個(gè)SavedStateViewModelFactory其實(shí)會(huì)退化成AndroidViewModelFactory,它只是通過(guò)反射創(chuàng)建ViewModel而已。這要求我們的ViewModel必須有默認(rèn)構(gòu)造函數(shù)。從ViewModel的生命周期圖上可以看出,ViewModel的生命周期要比Activity或者Fragment的更長(zhǎng),ViewModel中自然就不能包含Activity或者Fragment,如果實(shí)在需要在ViewModel中使用Context,那么只能使用ApplicationContext,為了方便我們使用,AAC中還定義了一個(gè)AndroidViewModel

public class AndroidViewModel extends ViewModel {
    @SuppressLint("StaticFieldLeak")
    private Application mApplication;

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

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

我們的ViewModel也可以繼承自AndroidViewModel,這樣在ViewModel中就可以方便地使用Application了。而AndroidViewModelFactory也可以通過(guò)反射創(chuàng)建繼承自AndroidViewModel的ViewModel。
想了解更多關(guān)于ViewModel-SavedState的內(nèi)容,可以查看這篇文章ViewModel如何保存狀態(tài)——SavedState源碼解析。

2.2 ViewModelStore

兼容包中的Activity和Fragment皆實(shí)現(xiàn)了ViewModelStoreOwner接口,該接口只包含一個(gè)方法getViewModelStore

2.2.1 Activity與ViewModelStore

public class ComponentActivity implements ViewModelStoreOwner {

    static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
    }
    
    // 延遲創(chuàng)建,直到 getViewModelStore() 被調(diào)用時(shí)才會(huì)被創(chuàng)建
    private ViewModelStore mViewModelStore;
    
    /**
     * 保留所有的 non-config state. 不隨配置變化的那些變量。
     * 最重要的就是保留了 ViewModelStore
     */
    @Override
    @Nullable
    public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();

        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            // No one called getViewModelStore(), so see if there was an existing
            // ViewModelStore from our last NonConfigurationInstance
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                viewModelStore = nc.viewModelStore;
            }
        }

        if (viewModelStore == null && custom == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }
    
    /**
     * 從 NonConfigurationInstances 中取出 viewModelStore
     * 沒(méi)有則新建一個(gè) ViewModelStore
     */
    @NonNull
    @Override
    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) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
}

對(duì)于Activity而言,它本身就包含有一個(gè)ViewModelStore,在調(diào)用getViewModelStore方法時(shí)會(huì)被創(chuàng)建(如果需要的話)。ViewModel最神奇的地方就是它不會(huì)隨配置的變化而重建,表現(xiàn)在生命周期上就是文中的第一幅圖。這一切是怎么實(shí)現(xiàn)的呢?在上述代碼中我們已經(jīng)可以看出來(lái)了。Activity的ViewModelStore會(huì)在配置發(fā)生變化時(shí)通過(guò)onRetainNonConfigurationInstance方法被保留下來(lái),在getViewModelStore方法中又首先會(huì)從NonConfigurationInstances中取出被保留下來(lái)的ViewModelStore(如果有的話)。這樣,ViewModelStore就不會(huì)因?yàn)榕渲玫淖兓亟?,也就是說(shuō),我們的ViewModel不受配置變化的影響。

2.2.2 Fragment與ViewModelStore

public class Fragment implements ViewModelStoreOwner {

    //自身并沒(méi)有實(shí)現(xiàn),委托給了FragmentManager
    @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        if (mFragmentManager == null) {
            throw new IllegalStateException("Can't access ViewModels from detached fragment");
        }
        return mFragmentManager.getViewModelStore(this);
    }
}

再來(lái)看看FragmentManager

final class FragmentManagerImpl extends FragmentManager {
    private FragmentManagerViewModel mNonConfig;
    
    //又委托給了mNonConfig
    @NonNull
    ViewModelStore getViewModelStore(@NonNull Fragment f) {
        return mNonConfig.getViewModelStore(f);
    }
    
    /**
     * 這個(gè)方法會(huì)在上述方法 getViewModelStore 之前被調(diào)用,是mNonConfig被賦值的地方
     * 其中host一般情況下是我們的Activity
     * parent是指父Fragment
     */
    public void attachController(@NonNull FragmentHostCallback host,
                                 @NonNull FragmentContainer container, @Nullable Fragment parent) {
        if (mHost != null) throw new IllegalStateException("Already attached");
        mHost = host;
        mContainer = container;
        mParent = parent;
        if (parent != null) { //父Fragment不為null,則從父Fragment的FragmentManager中獲取NonConfig
            mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
        } else if (host instanceof ViewModelStoreOwner) {
            //host是我們的Activity,它的確實(shí)現(xiàn)了ViewModelStoreOwner接口
            ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
            mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
        } else {
            mNonConfig = new FragmentManagerViewModel(false);
        }
    }
}

Fragment將getViewModelStore委托給了其FragmentManager,F(xiàn)ragmentManager又把getViewModelStore委托給了NonConfig。從NonConfig這個(gè)名字可以猜測(cè)出,它應(yīng)該是保存Fragment中與配置無(wú)關(guān)的變量的地方(比如說(shuō)我們的ViewModelStore)。而NonConfig的類型卻是FragmentManagerViewModel,用一個(gè)ViewModel去保存ViewModelStore,這是什么騷操作?

看看FragmentManagerViewModel就知道了

/**
 * FragmentManagerViewModel is the always up to date view of the Fragment's non configuration state
 * FragmentManagerViewModel保存了最新的Fragment的non configuration state
 */
class FragmentManagerViewModel extends ViewModel {

    private static final ViewModelProvider.Factory FACTORY = new ViewModelProvider.Factory() {
        @NonNull
        @Override
        @SuppressWarnings("unchecked")
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            FragmentManagerViewModel viewModel = new FragmentManagerViewModel(true);
            return (T) viewModel;
        }
    };

    /**
     * 從上一段代碼中可以看出,此處的ViewModelStore其實(shí)就是Activity中的ViewModelStore
     * 也就是說(shuō)這個(gè)FragmentManagerViewModel是被保存在Activity的ViewModelStore中的
     */
    @NonNull
    static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {
        ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore,
                FACTORY);
        return viewModelProvider.get(FragmentManagerViewModel.class);
    }

    //保存子Fragment的NonConfig,對(duì)于我們而言,其實(shí)就是保存子Fragment的ViewModelStore
    private final HashMap<String, FragmentManagerViewModel> mChildNonConfigs = new HashMap<>();
    //保存Fragment的ViewModelStore
    private final HashMap<String, ViewModelStore> mViewModelStores = new HashMap<>();

    
    @NonNull
    FragmentManagerViewModel getChildNonConfig(@NonNull Fragment f) {
        FragmentManagerViewModel childNonConfig = mChildNonConfigs.get(f.mWho);
        if (childNonConfig == null) {
            childNonConfig = new FragmentManagerViewModel(mStateAutomaticallySaved);
            mChildNonConfigs.put(f.mWho, childNonConfig);
        }
        return childNonConfig;
    }

    @NonNull
    ViewModelStore getViewModelStore(@NonNull Fragment f) {
        ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
        if (viewModelStore == null) {
            viewModelStore = new ViewModelStore();
            mViewModelStores.put(f.mWho, viewModelStore);
        }
        return viewModelStore;
    }
}

如注釋所說(shuō),F(xiàn)ragmentManagerViewModel的作用是保存Fragment最新的non configuration state,我們要保存的Fragment的ViewModelStore也是non configuration state,保存在FragmentManagerViewModel中正合適。但是,F(xiàn)ragmentManagerViewModel是怎么做到這一點(diǎn)的呢?答案很簡(jiǎn)單,把FragmentManagerViewModel放到Activity的ViewModelStore中,我們上面已經(jīng)分析過(guò)了,Activity的ViewModelStore在配置發(fā)生變化時(shí)會(huì)被保留下來(lái),所以FragmentManagerViewModel自然也就不受配置變化的影響。想想也是,F(xiàn)ragmentManagerViewModel也就是個(gè)普通的ViewModel,自然擁有ViewModel的特點(diǎn),只不過(guò)AAC正好利用了這一點(diǎn),把它用來(lái)保存Fragment的non configuration state(我們最關(guān)心的Fragment的ViewModelStore也在其中)。

Activity的ViewModelStore是不隨配置變化的,F(xiàn)ragment的ViewModelStore也自然不隨配置變化。

2.2.3 ViewModelStore

說(shuō)了這么多ViewModelStore,到底ViewModelStore長(zhǎng)啥樣?顯然ViewModelStore就是存儲(chǔ)ViewModel的地方。如前所述,我們實(shí)際上是通過(guò)字符串key去獲取的ViewModel,那么ViewModelStore自然是以Map的形式來(lái)存儲(chǔ)ViewModel。嗯,基本上這就是ViewModelStore的全部,就是這么簡(jiǎn)單。

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

    /**
     *  會(huì)在Activity、Fragment destroy并且不是因?yàn)榕渲冒l(fā)生變化的時(shí)候被調(diào)用
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

3. 隱藏技能

  1. 如果我們需要在同一個(gè)Activity或者Fragment中創(chuàng)建多個(gè)同一類型的ViewModel,我們可以在獲取ViewModel的時(shí)候用不同的key進(jìn)行區(qū)分。
  2. 默認(rèn)情況下AndroidViewModelFactory會(huì)幫我們以反射的方式創(chuàng)建ViewModel,但是ViewModel是在View和Model之間起到橋梁的作用,ViewModel一般情況下都需要包含Model層,而使用AndroidViewModelFactory又要求我們的ViewModel必須包含默認(rèn)構(gòu)造函數(shù)(AndroidViewModel情況除外),這就導(dǎo)致我們不能使用構(gòu)造函數(shù)的方式去注入Model層,有時(shí)候這很不方便。還好,我們可以提供自己的ViewModelProvider.Factory。恰好,dagger2有一種能力叫作MultiBindings,是以依賴注入創(chuàng)建ViewModelProvider.Factory的絕佳方式,詳見(jiàn)當(dāng)Dagger2撞上ViewModel。
  3. 你可能覺(jué)得Activity的onRetainNonConfigurationInstance是個(gè)保存NonConfig State的好地方,如果我們也有什么不隨配置變化的變量,也可以利用onRetainNonConfigurationInstance來(lái)保存,甚至Activity中有個(gè)方法叫onRetainCustomNonConfigurationInstance,專門(mén)幫我們保存自己的NonConfig State。恭喜你,從源碼中又學(xué)到了一點(diǎn)沒(méi)有卵用的東西。Activity覺(jué)得你很聰明,并向你扔出一個(gè)Deprecated警告。onRetainCustomNonConfigurationInstance方法已經(jīng)被標(biāo)記為廢棄的,并推薦你使用ViewModel來(lái)保存NonConfig State。至于原因,不言自明。

4. 總結(jié)

ViewModel要解決的核心問(wèn)題是怎樣在配置發(fā)生變化時(shí)保留ViewModel。利用Activity提供的onRetainNonConfigurationInstance方法,可以方便的保留不受配置變化影響的數(shù)據(jù)。
在實(shí)現(xiàn)了Activity的ViewModelStore之后,我們便可以利用ViewModel本身的特點(diǎn)去保存Fragment的ViewModelStore,如此這般,F(xiàn)ragment的ViewModel便也不再受配置變化的影響。用Activity中的ViewModelStore去保存FragmentManagerViewModel,而FragmentManagerViewModel又保存了Fragment的ViewModelStore。嗯,真真是踩在自己的肩膀上向上爬。

最后編輯于
?著作權(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),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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