Android源碼解析-ViewModel

前言

最近一個月主要實現了IM功能,其中UI框架使用了ViewModel和LiveData的存儲和通知機制,實現後代碼簡潔易於維護。
感慨於Android arch components控件強大同時,須要順帶分析一波其中源碼實現。今天先來分析一下ViewModel的源碼實現。java

ViewModel簡介

ViewModel是經過關聯生命週期的方式來存儲和管理跟UI相關的數據。即便configuration發生變化,在ViewModel中存放的數據是不會被銷燬的。
沒使用ViewModel的時候,若是系統Configuration發生變化,咱們的Activity會被銷燬重建,致使Activity中的UI數據丟失。爲了規避這個問題,咱們只能在onSaveInstanceState()將UI數據進行保存,在onCreate方法中判斷savedInstanceState中是否有咱們存儲的數據。
有了ViewModel以後,咱們只須要將數據存儲到ViewModel便可,ViewModel中的數據不會隨着Activity的銷燬重建而消失。同時,若是不一樣的Fragment使用相同的Activity對象來獲取ViewModel,能夠輕易的實現數據共享和通訊。app

使用ViewModel的例子:框架

// 自定義一個ViewModel,存儲一個字符串
public class TestViewModel extends ViewModel {
    public String content;
    
    @Override
    protected void onCleared() {
        // 數據清理工做
        content = null;
    }
}

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

三個問題

  1. ViewModel是如何建立出來的?
  2. 爲何不一樣的Fragment使用相同的Activity對象來獲取ViewModel,能夠輕易的實現ViewModel共享?
  3. ViewModel爲何在Activity銷燬重建時不會被銷燬回收?

我我的習慣,看源碼時候老是要帶着問題去分析理解,這樣纔會有所收穫。ide

源碼分析

ViewModel是如何建立出來的?

經過示例代碼,去掉鏈式調用後,咱們能夠看到ViewModel是經過以下兩步建立出來的:函數

// 1. 建立ViewModelProvider
ViewModelProvider viewModelProvider = ViewModelProviders.of(activity);

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

建立ViewModelProvider

先看下第一步的源碼實現:源碼分析

public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) {
    if (factory == null) {
        // 若是傳入的對象建立工廠類爲null,則使用默認的AndroidViewModelFactory來建立對象
        factory = AndroidViewModelFactory.getInstance(application);
    }
    
    // 建立一個ViewModelProvider
    return new ViewModelProvider(ViewModelStores.of(activity), (Factory)factory);
}

源碼中,咱們發現建立一個ViewModelProvider須要傳入兩個參數:ViewModelStore和Factory。咱們先看下Factory的實現。學習

Factory

Factory顧名思義,定義了建立ViewModel的行爲接口。裏面只有一個create方法,用於子類自行決定如何實現一個ViewModel對象的建立。this

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

同時,ViewModelProvider源碼內部也提供了兩個默認Factory實現:NewInstanceFactory和AndroidViewModelFactory。google

// 直接反射Class對象的無參構造函數來建立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,發射調用帶Application參數的構造函數建立ViewModel對象;
// 2. 若是對象不繼承自AndroidViewModel,則直接調用父類,即調用Class的無參構造函數建立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對象若是構造函數中須要其餘各類參數,咱們只須要建立一個自定義的Factory類,而後調用該class的有參構造函數進行建立便可。spa

ViewModelStore

ViewModelStore就是個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實現和ViewModelStore實現後,咱們來看一下ViewModelProvider的get方法是如何建立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方法實現也比較簡單,總結一下就是:

  1. 使用ViewModel Class的canonicalName做爲ViewModel在ViewModelStore中的惟一標識。
  2. 經過惟一標識,先查詢一下ViewModelStore中是否有該ViewModel對象,若是有則直接返回。
  3. 若是ViewModelStore中沒有該ViewModel對象,則經過Factory工廠類反射建立出ViewModel對象,存入ViewModelStore中,並返回給調用者。

小結

至此,第一個問題ViewModel是如何建立出來的已經分析完畢。接下來,咱們看第二個問題。

爲何不一樣的Fragment使用相同的Activity對象來獲取ViewModel,能夠輕易的實現ViewModel共享?

講道理,若是同窗們仔細看了ViewModel的建立流程,這個問題天然迎刃而解。
由於不一樣的Fragment使用相同的Activity對象來獲取ViewModel,在建立ViewModel以前都會先從Activity提供的ViewModelStore中先查詢一遍是否已經存在該ViewModel對象。因此咱們只須要先在Activity中一樣調用一遍ViewModel的獲取代碼,便可讓ViewModel存在於ViewModelStore中,從而不一樣的Fragment能夠共享一份ViewModel了。

ViewModel爲何在Activity銷燬重建時不會被銷燬回收?

在看這個問題以前,咱們回到ViewModelProviders.of()源碼中:

public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) {
    if (factory == null) {
        // 若是傳入的對象建立工廠類爲null,則使用默認的AndroidViewModelFactory來建立對象
        factory = AndroidViewModelFactory.getInstance(application);
    }
    
    // 建立一個ViewModelProvider
    return new ViewModelProvider(ViewModelStores.of(activity), (Factory)factory);
}

ViewModelProvider獲取ViewModelStore是經過ViewModelStores.of(activity)實現的。咱們先看一下這個ViewModelStores作了什麼操做。

ViewModelStores

看名字又像是一個封裝好的工廠類。看下源碼:

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

經過源碼能夠發現,若是Activity是ViewModelStoreOwner的實現類,則直接經過activity獲取ViewModelStore,若是不是,則經過HolderFragment.holderFragmentFor(activity).getViewModelStore()來獲取。

經過查看FragmentActivity源碼,發現其已是實現了ViewModelStoreOwner接口。

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

因此這裏咱們已經不須要理會HolderFragment了,不少分析ViewModel源碼的文章都會花不少篇幅分析HolderFragment,我實在是搞不懂爲何要這種操做。(初步懷疑是互相抄襲。。.)

雖然FragmentActivity實現了ViewModelStoreOwner接口,可以提供ViewModelStore,可是ViewModelStore是如何跟Activity生命週期關聯起來的呢?

FragmentActivity中ViewModelStore的生命週期處理

搜索了一下FragmentActivity中關於ViewModelStore的調用,發現這裏的實現應該跟生命週期處理有關。
FragmentActivity
google搜索了一下Activity的onRetainNonConfigurationInstance的做用:大部分同窗知道Activity由於configuration變化銷燬和重建時會調用onSaveInstanceState和onRestoreInstanceState。與此同時,Activity其實還會回調onRetainNonConfigurationInstance和getLastNonConfigurationInstance方法。

onRetainNonConfigurationInstance和onSaveInstanceState做用相同,用來保存UI相關變量,當Activity意外銷燬時,Activity的ViewModelStore對象就是在這裏進行了保存。

那什麼時機進行的恢復呢?
當Activity的onCreate調用時,會調用getLastNonConfigurationInstance,獲取以前保存的ViewModelStore,若是ViewModelStore不爲空,就進行賦值。這裏進行了ViewModelStore的恢復。
Activity恢復

小結

這裏咱們又學到了Activity的兩個跟生命週期相關的函數調用:onRetainNonConfigurationInstance和getLastNonConfigurationInstance。

  1. Activity實現了ViewModelStoreOwner接口,建立了ViewModelStore對象。
  2. 當Activity意外銷燬時,onRetainNonConfigurationInstance函數被回調,在此函數中對ViewModelStore對象進行了保存。
  3. 當Activity重建時,onCreate方法中會先獲取getLastNonConfigurationInstance,若是其中的ViewModelStore對象不爲空,就直接引用,再也不從新建立ViewModelStore對象了。

總結

三個問題分析完畢,相信你們已經對ViewModel的實現原理比較熟悉了。建議你們之後學習源碼時,也帶着問題去分析思考,事半功倍。

相關文章
相關標籤/搜索