5. Jetpack源碼解析---ViewModel基本使用及源碼解析

截止到目前爲止,JetpackNote源碼分析的文章已經有四篇文章了,這一系列的文章個人初衷是想仔細研究一下Jetpack,最終使用Jetpack組件寫一個Demo,上一篇已經分析了LiveData,本篇文章將分析ViewModel.java

1.背景

Jetpack源碼解析系列文章:git

1. Jetpack源碼解析---看完你就知道Navigation是什麼了?github

2. Jetpack源碼解析---Navigation爲何切換Fragment會重繪?app

3. Jetpack源碼解析---用Lifecycles管理生命週期ide

4. Jetpack源碼解析—LiveData的使用及工做原理函數

上篇咱們對LiveData進行了分析,已清楚了它的主要做用,咱們再來溫習一下:源碼分析

LiveData是一個能夠感知Activity、Fragment生命週期的數據容器。其自己是基於觀察者模式設計的,當LiveData所持有的數據發生改變時,它會通知對應的界面所持有該數據的UI進行更新,而且LiveData中持有Lifecycle的引用,因此只會在LifecycleOwner處於Active的狀態下通知數據改變,果數據改變發生在非 active 狀態,數據會變化,可是不發送通知,等 owner 回到 active 的狀態下,再發送通知.post

LiveData配合今天所講的ViewModel使用起來真的是很是方便,接下來咱們開始分析:ui

2.簡介

2.1 是什麼?

簡單來講:ViewModel是以關聯生命週期的方式來存儲和管理UI相關數據的類,即便configuration發生改變(例如屏幕旋轉),數據仍然能夠存在不會銷燬.this

舉個例子來講:咱們在開發中常常會遇到這種場景,當咱們的Activity和Fragment被銷燬重建以後,它們中的數據將會丟失,而咱們通常的解決方案就是經過onSaveInstanceState()中保存數據,而後在onCreate()中恢復過來,可是當這些數據較大時或者數據可能又須要從新請求接口,這時候ViewModel就派上用場了,也就是說當咱們把數據保存在ViewModel中,無論Activity和Fragment被銷燬了仍是屏幕旋轉致使configuration發生了變化,保存在其中的數據依然存在。

不只如此,ViewModel還能夠幫助咱們輕易的實現Fragment及Activity之間的數據共享和通訊,下面會詳細介紹。

2.2 ViewModel生命週期

先來看一下官方給出的圖:

圖中展現了當一個Activity通過屏幕旋轉後的生命週期狀態改變,右側則是ViewModel的生命週期狀態。咱們能夠看到ViewModel的生命週期並不會跟隨着Activity的生命週期變化而變化,雖然ViewModel是由該Activity建立的。咱們會在源碼分析部分再去看ViewModel的生命週期具體是怎樣的。下面咱們看下ViewModel該怎樣使用?

3. 基本使用

3.1 數據存儲

咱們參考官方Demo實現一個計時器的功能,而且演示當屏幕發生旋轉時,計時器會不會從新啓動:

DemoViewModel

class DemoViewModel : ViewModel() {

    var time: Long? = null

    var seekbarValue = MutableLiveData<Int>()
}

複製代碼

ViewModelFragment

class ViewModelFragment : Fragment() {

    override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? {
        return inflater.inflate(R.layout.fragment_view_model, container, false)
    }


    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        val vm = ViewModelProviders.of(this).get(DemoViewModel::class.java)
        if (vm.time == null) {
            vm.time = SystemClock.elapsedRealtime()
        }
        chronometer.base = vm.time!!
        chronometer.start()

        btn_fragment_share.setOnClickListener {
            findNavController().navigate(R.id.viewModelShareActivity)
        }
    }

}

複製代碼

代碼很簡單,只是在ViewModel中存儲了一個time值,在fragment中啓動計時器,當咱們旋轉屏幕的時候你會發現,計時器的值並無變化,仍然按照旋轉以前的數值進行計數。

3.2 Fragment數據共享

ViewModelShareActivity中展現了ViewModel中的數據進行Fragment數據共享的功能。

在以前的DemoViewModel中咱們存儲了seekbar的值,而後咱們看Fragment中是怎麼實現的?

class DataShareFragment : Fragment() {

    private lateinit var mViewModel: DemoViewModel

    override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? {
        mViewModel = ViewModelProviders.of(activity!!).get(DemoViewModel::class.java)
        return inflater.inflate(R.layout.fragment_data_share, container, false)
    }


    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        subscribeSeekBar()
    }

    private fun subscribeSeekBar() {
        // 監聽SeekBar改變ViewModel的中的值
        seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
            override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
                if (fromUser) {
                    mViewModel.seekbarValue.value = progress
                }
            }

            override fun onStartTrackingTouch(seekBar: SeekBar) {}

            override fun onStopTrackingTouch(seekBar: SeekBar) {}
        })
        // 當ViewModel中的值發生變化時,更新SeekBar
        activity?.let {
            mViewModel.seekbarValue.observe(it, Observer<Int> { value ->
                if (value != null) {
                    seekBar.progress = value
                }
            })
        }
    }
}

複製代碼

看代碼其實挺簡單的,只作了兩個操做:

  1. 監聽SeekBar改變ViewModel的中的值
  2. 當ViewModel中的值發生變化時,更新SeekBar

一樣,當旋轉屏幕以後,SeekBar的值也不會改變。到這裏ViewModel的基本使用方式咱們已經瞭解了,接下來咱們來分析一下它具體是怎麼實現的?

沒錯,又到了源碼分析的部分了,相信不少童鞋也都比較不喜歡源碼的部分,包括我也是,以前很不肯意去看源碼,可是當你嘗試看了一些源碼以後,你會發現頗有意思的,由於有些東西你是有必要去分析源碼實現的,這是做爲一個開發者必備的基本的素質,並且當你使用一個組件的時候,一步一步的跟着代碼走,慢慢的分析了整個的組件設計方式,最後站在開發這個組件的角度,去看他的設計思想和一些模式的時候,對本身自己也是一個很大的提升,因此我真的建議有興趣的能夠跟着本身的思路一步一步的看下源碼。好了廢話很少說。

4. 源碼分析

ViewModelProviders

在使用VM的時候通常都經過val vm = ViewModelProviders.of(this).get(DemoViewModel::class.java)去建立,具體來看一下of方法:

@NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
        Application application = checkApplication(checkActivity(fragment));
        if (factory == null) {
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(fragment.getViewModelStore(), factory);
    }

複製代碼

of方法經過ViewModelProvider中的一個單例AndroidViewModelFactory建立了Factory幫助咱們去建立VM,而且返回了一個ViewModelProvider。你們注意一下第一個參數fragment.getViewModelStore(),這個ViewModelStore是什麼呢?接着看

public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();
		//存儲VM
    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());
    }

    /** * Clears internal storage and notifies ViewModels that they are no longer used. */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}
複製代碼

其實就是一個存儲VM的容器,裏面經過HashMap進行存和取。

下面咱們看建立VM時的get()方法

@NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    		//在ViewModelStore中拿到VM實例
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            //noinspection unchecked
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        //工廠建立VM,並保存VM
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }

複製代碼

get()方法就是獲取VM實例的過程,若是VM不存在,則經過工廠去建立實例。具體工廠建立實現:



public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

        private static AndroidViewModelFactory sInstance;

        /** * Retrieve a singleton instance of AndroidViewModelFactory. * * @param application an application to pass in {@link AndroidViewModel} * @return A valid {@link AndroidViewModelFactory} */
        @NonNull
        public static AndroidViewModelFactory getInstance(@NonNull Application application) {
            if (sInstance == null) {
                sInstance = new AndroidViewModelFactory(application);
            }
            return sInstance;
        }

        private Application mApplication;

        /** * Creates a {@code AndroidViewModelFactory} * * @param application an application to pass in {@link AndroidViewModel} */
        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);
                } 
              .......
            }
            return super.create(modelClass);
        }
    }
複製代碼

這個工廠就很簡單了,就是經過反射來建立VM實例。

到這裏VM的建立過程就差很少了,而咱們發現他並無和生命週期有什麼相關的東西,或者說VM是怎樣保證的的數據不被銷燬的呢?看了網上的一些文章,不少都說在of方法的時候傳入了Fragment,而且經過HolderFragment持有ViewModelStore對象等等……好比:

可是在我這裏發現跟他們的都不同,我搜了一下ViewModelStores,發現它已經‘退役’了。

而且它的註釋也告訴了咱們它的繼承者:

也就是咱們在of()方法中的:

也就是說谷歌已經將ViewModelStore集成到了Fragment中,一塊兒去看一下吧:

Fragment.java

@NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        if (mFragmentManager == null) {
            throw new IllegalStateException("Can't access ViewModels from detached fragment");
        }
        return mFragmentManager.getViewModelStore(this);
    }
FragmentManagerImpl.java

		private FragmentManagerViewModel mNonConfig;
		......
    @NonNull
    ViewModelStore getViewModelStore(@NonNull Fragment f) {
        return mNonConfig.getViewModelStore(f);
    }
複製代碼

咱們能夠看到代碼到了咱們熟悉的FragmentManagerImpl,它的裏面持有了一個FragmentManagerViewModel實例,進去看看這個是個什麼東西:

FragmentManagerViewModel.java

class FragmentManagerViewModel extends ViewModel {

    //建立FragmentVM的工廠
    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;
        }
    };

    //從viewModelProvider中獲取FragmentVM實例
    @NonNull
    static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {
        ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore,
                FACTORY);
        return viewModelProvider.get(FragmentManagerViewModel.class);
    }
    //非中斷的Fragment集合
    private final HashSet<Fragment> mRetainedFragments = new HashSet<>();
    private final HashMap<String, FragmentManagerViewModel> mChildNonConfigs = new HashMap<>();
    private final HashMap<String, ViewModelStore> mViewModelStores = new HashMap<>();

    private final boolean mStateAutomaticallySaved;
    // Only used when mStateAutomaticallySaved is true
    private boolean mHasBeenCleared = false;
    // Only used when mStateAutomaticallySaved is false
    private boolean mHasSavedSnapshot = false;

 
    FragmentManagerViewModel(boolean stateAutomaticallySaved) {
        mStateAutomaticallySaved = stateAutomaticallySaved;
    }

    
    //添加非中斷Fragment
    boolean addRetainedFragment(@NonNull Fragment fragment) {
        return mRetainedFragments.add(fragment);
    }

    @NonNull
    Collection<Fragment> getRetainedFragments() {
        return mRetainedFragments;
    }
    //是否改銷燬
    boolean shouldDestroy(@NonNull Fragment fragment) {
        if (!mRetainedFragments.contains(fragment)) {
            // Always destroy non-retained Fragments
            return true;
        }
        if (mStateAutomaticallySaved) {
            // If we automatically save our state, then only
            // destroy a retained Fragment when we've been cleared
            return mHasBeenCleared;
        } else {
            // Else, only destroy retained Fragments if they've
            // been reaped before the state has been saved
            return !mHasSavedSnapshot;
        }
    }

    boolean removeRetainedFragment(@NonNull Fragment fragment) {
        return mRetainedFragments.remove(fragment);
    }

		//獲取VMStore
    @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;
    }
    //銷燬並清空VM
    void clearNonConfigState(@NonNull Fragment f) {
        if (FragmentManagerImpl.DEBUG) {
            Log.d(FragmentManagerImpl.TAG, "Clearing non-config state for " + f);
        }
        // Clear and remove the Fragment's child non config state
        FragmentManagerViewModel childNonConfig = mChildNonConfigs.get(f.mWho);
        if (childNonConfig != null) {
            childNonConfig.onCleared();
            mChildNonConfigs.remove(f.mWho);
        }
        // Clear and remove the Fragment's ViewModelStore
        ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
        if (viewModelStore != null) {
            viewModelStore.clear();
            mViewModelStores.remove(f.mWho);
        }
    }
複製代碼

看代碼咱們發現它繼承了VM,而且裏面保存了VMStore,也就是說保存了VM,同時清空的操做也在這裏面:clearNonConfigState()

ViewModelStore.java

/** * Clears internal storage and notifies ViewModels that they are no longer used. */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
複製代碼

那麼究竟是何時來清空VM的呢?也就是說clear()是何時調用的呢?查看源碼咱們發現有兩處:

  1. 也就是上面提到的FragmentViewModel.java裏面的clearNonConfigState()方法,而這個方法只在一個地方被調用了:
if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
boolean beingRemoved = f.mRemoving && !f.isInBackStack();
if (beingRemoved || mNonConfig.shouldDestroy(f)) {
    boolean shouldClear;
    if (mHost instanceof ViewModelStoreOwner) {
        shouldClear = mNonConfig.isCleared();
    } else if (mHost.getContext() instanceof Activity) {
        Activity activity = (Activity) mHost.getContext();
        shouldClear = !activity.isChangingConfigurations();
    } else {
        shouldClear = true;
    }
  	//Fragment正在被移除或者應該清空的狀態下
    if (beingRemoved || shouldClear) {
        mNonConfig.clearNonConfigState(f);
    }
    f.performDestroy();
    dispatchOnFragmentDestroyed(f, false);
} else {
    f.mState = Fragment.INITIALIZING;
}
複製代碼

這個方法是在FragmentManagerImpl.java中的moveToState方法裏面的,這個方法是跟隨着Fragment的生命週期的,當這個方法被調用時,判斷兩個狀態beingRemovedshoudClear而後調用clear()方法。關於moveToState()能夠查看這篇文章:Fragment之底層關鍵操做函數moveToState

  1. 第二個調用的地方就是ComponentActivity.java
getLifecycle().addObserver(new GenericLifecycleObserver() {
    @Override
    public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
            if (event == Lifecycle.Event.ON_DESTROY) {
                if (!isChangingConfigurations()) {//不是改變狀態配置的
                    getViewModelStore().clear();
                }
            }
        }
});
複製代碼

關於ComponentActivity,若是有看過以前我分析過的關於Lifecycles的應該對它有所瞭解3. Jetpack源碼解析---用Lifecycles管理生命週期FragmentActivity繼承了它,而ComponentActivity裏面經過Lifecycles觀察生命週期,當接受到ON_DESTROY的事件時,清空VM。

5. 總結

分析完源碼以後,咱們來總結一下整個流程:

  1. 經過ViewModelProviders.of(this).get(DemoViewModel::class.java)建立VM
  2. of()方法中傳入 fragment.getViewModelStore()而且返回VMProviders
  3. 查看FragmentManagerImplgetViewModelStore(),持有FragmentManagerViewModel對象
  4. FragmentManagerViewModel中清空VM操做 clearNonConfigState(),同時在ViewModelStoreclear()了ViewModel的value值
  5. 最後咱們發現只有在ComponentActivity中觀察到接收到ON_DESTROY的事件時同時並非因爲configuration發生變化時纔會執行clear()操做;另一處是在moveToState()方法中,知足beingRemovedshouldClear狀態也會清空VM

好了 整個流程就是這樣了,並無特別深刻的去分析,可是基本的原理咱們已經清楚了,Demo中也只是簡單的使用了VM。

JetPack源碼分析系列文章到這裏應該就結束了,後面還有Paging、WorkManager、Room,以及Camera2和ViewPager2,這些目前暫時不分析了,可是也會出基本的使用和簡單的分析,一共5篇源碼分析,文章中的Demo我寫了一個APP—JetpackNote,裏面有基本的Demo例子,和文章的分析;一直也沒有提到是由於功能還不完善,我會盡快完善它的,也但願有什麼意見的小夥伴能夠和我溝通交流。

Github源碼地址在:

JetpackNote

相關文章
相關標籤/搜索