Android源碼解析-ViewModel

前言

最近一個(gè)月主要實(shí)現(xiàn)了IM功能,其中UI框架使用了ViewModel和LiveData的存儲(chǔ)和通知機(jī)制,實(shí)現(xiàn)后代碼簡潔易于維護(hù)。
感慨于Android arch components控件強(qiáng)大同時(shí),需要順帶分析一波其中源碼實(shí)現(xiàn)。今天先來分析一下ViewModel的源碼實(shí)現(xiàn)。

ViewModel簡介

ViewModel是通過關(guān)聯(lián)生命周期的方式來存儲(chǔ)和管理跟UI相關(guān)的數(shù)據(jù)。即使configuration發(fā)生變化,在ViewModel中存放的數(shù)據(jù)是不會(huì)被銷毀的。
沒使用ViewModel的時(shí)候,如果系統(tǒng)Configuration發(fā)生變化,我們的Activity會(huì)被銷毀重建,導(dǎo)致Activity中的UI數(shù)據(jù)丟失。為了規(guī)避這個(gè)問題,我們只能在onSaveInstanceState()將UI數(shù)據(jù)進(jìn)行保存,在onCreate方法中判斷savedInstanceState中是否有我們存儲(chǔ)的數(shù)據(jù)。
有了ViewModel之后,我們只需要將數(shù)據(jù)存儲(chǔ)到ViewModel即可,ViewModel中的數(shù)據(jù)不會(huì)隨著Activity的銷毀重建而消失。同時(shí),如果不同的Fragment使用相同的Activity對象來獲取ViewModel,可以輕易的實(shí)現(xiàn)數(shù)據(jù)共享和通信。

使用ViewModel的例子:

// 自定義一個(gè)ViewModel,存儲(chǔ)一個(gè)字符串
public class TestViewModel extends ViewModel {
    public String content;

    @Override
    protected void onCleared() {
        // 數(shù)據(jù)清理工作
        content = null;
    }
}

// 在Activity中獲取并使用ViewModel
TestViewModel viewModel = ViewModelProviders.of(activity).get(TestViewModel.class);
Log.d("tag", viewModel.content);

三個(gè)問題

  1. ViewModel是如何創(chuàng)建出來的?
  2. 為什么不同的Fragment使用相同的Activity對象來獲取ViewModel,可以輕易的實(shí)現(xiàn)ViewModel共享?
  3. ViewModel為什么在Activity銷毀重建時(shí)不會(huì)被銷毀回收?

我個(gè)人習(xí)慣,看源碼時(shí)候總是要帶著問題去分析理解,這樣才會(huì)有所收獲。

源碼分析

ViewModel是如何創(chuàng)建出來的?

通過示例代碼,去掉鏈?zhǔn)秸{(diào)用后,我們可以看到ViewModel是通過如下兩步創(chuàng)建出來的:

// 1\. 創(chuàng)建ViewModelProvider
ViewModelProvider viewModelProvider = ViewModelProviders.of(activity);

// 2\. 通過反射獲取ViewModel
TestViewModel viewModel = viewModelProvider.get(TestViewModel.class);

創(chuàng)建ViewModelProvider

先看下第一步的源碼實(shí)現(xiàn):

public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) {
    if (factory == null) {
        // 如果傳入的對象創(chuàng)建工廠類為null,則使用默認(rèn)的AndroidViewModelFactory來創(chuàng)建對象
        factory = AndroidViewModelFactory.getInstance(application);
    }

    // 創(chuàng)建一個(gè)ViewModelProvider
    return new ViewModelProvider(ViewModelStores.of(activity), (Factory)factory);
}

源碼中,我們發(fā)現(xiàn)創(chuàng)建一個(gè)ViewModelProvider需要傳入兩個(gè)參數(shù):ViewModelStore和Factory。我們先看下Factory的實(shí)現(xiàn)。

Factory

Factory顧名思義,定義了創(chuàng)建ViewModel的行為接口。里面只有一個(gè)create方法,用于子類自行決定如何實(shí)現(xiàn)一個(gè)ViewModel對象的創(chuàng)建。

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

同時(shí),ViewModelProvider源碼內(nèi)部也提供了兩個(gè)默認(rèn)Factory實(shí)現(xiàn):NewInstanceFactory和AndroidViewModelFactory。

// 直接反射Class對象的無參構(gòu)造函數(shù)來創(chuàng)建ViewModel
public static class NewInstanceFactory implements Factory {
    @Override
    public <T extends ViewModel> T create(@NonNull 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);
        }
    }
}

// 1\. 如果對象繼承自AndroidViewModel,發(fā)射調(diào)用帶Application參數(shù)的構(gòu)造函數(shù)創(chuàng)建ViewModel對象;
// 2\. 如果對象不繼承自AndroidViewModel,則直接調(diào)用父類,即調(diào)用Class的無參構(gòu)造函數(shù)創(chuàng)建ViewModel對象。
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
    private static AndroidViewModelFactory sInstance;
    @NonNull
    public static AndroidViewModelFactory getInstance(@NonNull Application application) {
        if (sInstance == null) {
            sInstance = new AndroidViewModelFactory(application);
        }
        return sInstance;
    }

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

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

啟示:我們自定義的ViewModel對象如果構(gòu)造函數(shù)中需要其他各種參數(shù),我們只需要?jiǎng)?chuàng)建一個(gè)自定義的Factory類,然后調(diào)用該class的有參構(gòu)造函數(shù)進(jìn)行創(chuàng)建即可。

ViewModelStore

ViewModelStore就是個(gè)HashMap,通過key來獲取ViewModel對象。

public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.get(key);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
        mMap.put(key, viewModel);
    }

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

ViewModelProvider

了解了Factory實(shí)現(xiàn)和ViewModelStore實(shí)現(xiàn)后,我們來看一下ViewModelProvider的get方法是如何創(chuàng)建ViewModel對象的。

public class ViewModelProvider {
    private final Factory mFactory;
    private final ViewModelStore mViewModelStore;

    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        this.mViewModelStore = store;
    }

    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }    

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

        if (modelClass.isInstance(viewModel)) {
            return (T) viewModel;
        }
        viewModel = mFactory.create(modelClass);
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }
}

ViewModleProvider的get方法實(shí)現(xiàn)也比較簡單,總結(jié)一下就是:

  1. 使用ViewModel Class的canonicalName作為ViewModel在ViewModelStore中的唯一標(biāo)識。
  2. 通過唯一標(biāo)識,先查詢一下ViewModelStore中是否有該ViewModel對象,如果有則直接返回。
  3. 如果ViewModelStore中沒有該ViewModel對象,則通過Factory工廠類反射創(chuàng)建出ViewModel對象,存入ViewModelStore中,并返回給調(diào)用者。

小結(jié)

至此,第一個(gè)問題ViewModel是如何創(chuàng)建出來的已經(jīng)分析完畢。接下來,我們看第二個(gè)問題。

為什么不同的Fragment使用相同的Activity對象來獲取ViewModel,可以輕易的實(shí)現(xiàn)ViewModel共享?

講道理,如果同學(xué)們仔細(xì)看了ViewModel的創(chuàng)建流程,這個(gè)問題自然迎刃而解。
因?yàn)椴煌腇ragment使用相同的Activity對象來獲取ViewModel,在創(chuàng)建ViewModel之前都會(huì)先從Activity提供的ViewModelStore中先查詢一遍是否已經(jīng)存在該ViewModel對象。所以我們只需要先在Activity中同樣調(diào)用一遍ViewModel的獲取代碼,即可讓ViewModel存在于ViewModelStore中,從而不同的Fragment可以共享一份ViewModel了。

ViewModel為什么在Activity銷毀重建時(shí)不會(huì)被銷毀回收?

在看這個(gè)問題之前,我們回到ViewModelProviders.of()源碼中:

public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) {
    if (factory == null) {
        // 如果傳入的對象創(chuàng)建工廠類為null,則使用默認(rèn)的AndroidViewModelFactory來創(chuàng)建對象
        factory = AndroidViewModelFactory.getInstance(application);
    }

    // 創(chuàng)建一個(gè)ViewModelProvider
    return new ViewModelProvider(ViewModelStores.of(activity), (Factory)factory);
}

ViewModelProvider獲取ViewModelStore是通過ViewModelStores.of(activity)實(shí)現(xiàn)的。我們先看一下這個(gè)ViewModelStores做了什么操作。

ViewModelStores

看名字又像是一個(gè)封裝好的工廠類??聪略创a:

public static ViewModelStore of(@NonNull FragmentActivity activity) {
    return activity instanceof ViewModelStoreOwner ? ((ViewModelStoreOwner)activity).getViewModelStore() : HolderFragment.holderFragmentFor(activity).getViewModelStore();
}

通過源碼可以發(fā)現(xiàn),如果Activity是ViewModelStoreOwner的實(shí)現(xiàn)類,則直接通過activity獲取ViewModelStore,如果不是,則通過HolderFragment.holderFragmentFor(activity).getViewModelStore()來獲取。

通過查看FragmentActivity源碼,發(fā)現(xiàn)其已經(jīng)是實(shí)現(xiàn)了ViewModelStoreOwner接口。

public class FragmentActivity extends BaseFragmentActivityApi16 implements
        ViewModelStoreOwner,
        ActivityCompat.OnRequestPermissionsResultCallback,
        ActivityCompat.RequestPermissionsRequestCodeValidator {
}

所以這里我們已經(jīng)不需要理會(huì)HolderFragment了,很多分析ViewModel源碼的文章都會(huì)花很多篇幅分析HolderFragment,我實(shí)在是搞不懂為什么要這種操作。(初步懷疑是互相抄襲。。.)

雖然FragmentActivity實(shí)現(xiàn)了ViewModelStoreOwner接口,能夠提供ViewModelStore,但是ViewModelStore是如何跟Activity生命周期關(guān)聯(lián)起來的呢?

FragmentActivity中ViewModelStore的生命周期處理

搜索了一下FragmentActivity中關(guān)于ViewModelStore的調(diào)用,發(fā)現(xiàn)這里的實(shí)現(xiàn)應(yīng)該跟生命周期處理有關(guān)。


image.png

google搜索了一下Activity的onRetainNonConfigurationInstance的作用:大部分同學(xué)知道Activity因?yàn)閏onfiguration變化銷毀和重建時(shí)會(huì)調(diào)用onSaveInstanceState和onRestoreInstanceState。與此同時(shí),Activity其實(shí)還會(huì)回調(diào)onRetainNonConfigurationInstance和getLastNonConfigurationInstance方法。

onRetainNonConfigurationInstance和onSaveInstanceState作用相同,用來保存UI相關(guān)變量,當(dāng)Activity意外銷毀時(shí),Activity的ViewModelStore對象就是在這里進(jìn)行了保存。

那什么時(shí)機(jī)進(jìn)行的恢復(fù)呢?
當(dāng)Activity的onCreate調(diào)用時(shí),會(huì)調(diào)用getLastNonConfigurationInstance,獲取之前保存的ViewModelStore,如果ViewModelStore不為空,就進(jìn)行賦值。這里進(jìn)行了ViewModelStore的恢復(fù)。

image.png

小結(jié)

這里我們又學(xué)到了Activity的兩個(gè)跟生命周期相關(guān)的函數(shù)調(diào)用:onRetainNonConfigurationInstance和getLastNonConfigurationInstance。

  1. Activity實(shí)現(xiàn)了ViewModelStoreOwner接口,創(chuàng)建了ViewModelStore對象。
  2. 當(dāng)Activity意外銷毀時(shí),onRetainNonConfigurationInstance函數(shù)被回調(diào),在此函數(shù)中對ViewModelStore對象進(jìn)行了保存。
  3. 當(dāng)Activity重建時(shí),onCreate方法中會(huì)先獲取getLastNonConfigurationInstance,如果其中的ViewModelStore對象不為空,就直接引用,不再重新創(chuàng)建ViewModelStore對象了。

總結(jié)

三個(gè)問題分析完畢,相信大家已經(jīng)對ViewModel的實(shí)現(xiàn)原理比較熟悉了。建議大家以后學(xué)習(xí)源碼時(shí),也帶著問題去分析思考,事半功倍。

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

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

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