欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android?Jetpack庫剖析之ViewModel組件篇

 更新時間:2022年07月22日 11:46:53   作者:豬飛啦~  
這篇文章主要介紹了Android?Jetpack架構組件?ViewModel詳解,ViewModel類讓數(shù)據可在發(fā)生屏幕旋轉等配置更改后繼續(xù)存在,ViewModel類旨在以注重生命周期的方式存儲和管理界面相關的數(shù)據。感興趣可以來學習一下

前言

今天讓我們一起去探究一下ViewModel的實現(xiàn)原理,描述的不對或不足還請海涵,僅作為參考

ViewModel簡介

ViewModel是一個可感知Activity或Fragment生命周期的一個架構組件,當視圖銷毀,數(shù)據也會被清除,所以它的本質就是用來存儲與視圖相關的數(shù)據,讓視圖顯示控制與數(shù)據分離,即使界面配置發(fā)生改變數(shù)據也不會被銷毀,通常配合LiveData使用

ViewModel用法

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(LayoutInflater.from(baseContext))
        setContentView(binding.root)
        //獲取ViewModel實例
        val viewModel: TextViewModel = ViewModelProvider(this).get(TextViewModel::class.java)
        //訂閱數(shù)據
        viewModel.liveData.observe(this, { println(it) })
        //調用函數(shù)
        viewModel.test()
    }
    class TextViewModel : ViewModel() {
        val liveData = MediatorLiveData<String>()
        fun test() {
            liveData.postValue("Hello")
        }
    }
}

1,使用ViewModelProvider獲取ViewModel實例

2,訂閱VIewModel的LiveData

3,調用ViewModel的方法

構造ViewModelProvider過程做了什么

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    

1,當我們創(chuàng)建ViewModelProvider的時候需要傳入一個ViewModelStoreOwner對象,ViewModelStoreOwner是負責提供ViewModelStore對象的, 而ComponentActivity實現(xiàn)了這個接口,所以我們默認傳當前的Activity即可

2,首先判斷是否有默認的ViewModel工廠,如果沒有就創(chuàng)建一個Factory

3,F(xiàn)actory是用來創(chuàng)建ViewModel的,ViewModelStore是用來存儲ViewModel的

調用get()方法是如何構建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);
    }
    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        //通過key到ViewModelStore中取
        ViewModel viewModel = mViewModelStore.get(key);
        
        //如果取到ViewModel 代表已經創(chuàng)建過這個ViewModel 直接返回
        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        //通過Factor利用反射創(chuàng)建一個實例
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
        } else {
            viewModel = mFactory.create(modelClass);
        }
        //把ViewModel實例存儲到ViewModelStore中
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

1,當我們調用get()方法時會先獲取類名,然后生成一個唯一的key

2,先通過key到ViewModelStore中取ViewModel,如果不為空代表已經創(chuàng)建過這個ViewModel的實例,直接返回這個實例

3,如果為空就通過工廠利用java反射機制創(chuàng)建一個實例,通過鍵值對形式保存到ViewModelStore中,返回實例,看到這里是不是對為什么多個fragment可以共享同一個ViewModel的疑問豁然開朗了,因為Fragment是依附于Activity之上的,在我們構建ViewModelProvider的時候傳入同一個Activity,那么ViewModelProvider得到的ViewModelStore是同一個,在我們調用get()方法時就能通過key到ViewModelStore中取到同一個ViewModel實例,說白了就是共用Activity的ViewModel

Activity配置發(fā)生改變如何緩存ViewModelStore

ActivityThread{
    private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
            PendingTransactionActions pendingActions, boolean startsNotResumed,
            Configuration overrideConfig, String reason) {
        // Preserve last used intent, it may be set from Activity#setIntent().
        final Intent customIntent = r.activity.mIntent;
        // Need to ensure state is saved.
        if (!r.paused) {
            performPauseActivity(r, false, reason, null /* pendingActions */);
        }
        if (!r.stopped) {
            callActivityOnStop(r, true /* saveState */, reason);
        }
        //銷毀Activity
        handleDestroyActivity(r.token, false, configChanges, true, reason);
        //啟動Activity
        handleLaunchActivity(r, pendingActions, customIntent);
    }
}

1,當我們切換橫豎屏的時候,ActivityThread會接收到RELAUNCH_ACTIVITY消息,會調用自身的handleRelaunchActivityInner(),這個方法里面有一個參數(shù)r,類型是ActivityClientRecord,我們每打開一個Activity,ActivityThread就會生成這么個對象來記錄我們打開的Activity并保存起來

2,handleRelaunchActivityInner()這個方法里調用了handleDestroyActivity()方法去銷毀我們的Activity

ActivityThread{
        @Override
    public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
            boolean getNonConfigInstance, String reason) {
        //執(zhí)行銷毀Activity
        ActivityClientRecord r = performDestroyActivity(token, finishing,
                configChanges, getNonConfigInstance, reason);
    }
    ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance, String reason) {
        //通過token獲取Activity的記錄
        ActivityClientRecord r = mActivities.get(token);
        if (r != null) {
            //是否需要獲取配置實例
            if (getNonConfigInstance) {
                try {
                    //調用Activity的retainNonConfigurationInstances方法獲取配置實例
                    r.lastNonConfigurationInstances
                            = r.activity.retainNonConfigurationInstances();
                } catch (Exception e) {
                }
            }
            r.setState(ON_DESTROY);
        }
        //通過token移除這條Activity的記錄
        synchronized (mResourcesManager) {
            mActivities.remove(token);
        }
        return r;
    }
}

3,handleDestroyActivity()則直接調用了performDestroyActivity()方法去銷毀Activity,核心部分就是調用了Activity的retainNonConfigurationInstances()方法獲取了配置實例并復制給了ActivityClientRecord,把NonConfigurationInstances實例保存起來

Activity{
    NonConfigurationInstances retainNonConfigurationInstances() {
        //獲取NonConfigurationInstances對象
        Object activity = onRetainNonConfigurationInstance();
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
        mFragments.doLoaderStart();
        mFragments.doLoaderStop(true);
        ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
        //創(chuàng)建NonConfigurationInstances實例
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.activity = activity;
        nci.children = children;
        nci.fragments = fragments;
        nci.loaders = loaders;
        if (mVoiceInteractor != null) {
            mVoiceInteractor.retainInstance();
            nci.voiceInteractor = mVoiceInteractor;
        }
        return nci;
    }
}
ComponentActivity{
    @Override
    @Nullable
    public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();
        //獲取ViewModelStore
        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;
        }
        //創(chuàng)建NonConfigurationInstances實例
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }
}

4, 通過查閱源碼,一層一層的往下剖析,ActivityThread是通過這樣的調用鏈來獲取我們的ViewModelStore實例并保存在ActivityClientRecord中的

Activity重建后如何恢復ViewModelStore

ActivityThread{
    private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
            PendingTransactionActions pendingActions, boolean startsNotResumed,
            Configuration overrideConfig, String reason) {
        // Preserve last used intent, it may be set from Activity#setIntent().
        final Intent customIntent = r.activity.mIntent;
        // Need to ensure state is saved.
        if (!r.paused) {
            performPauseActivity(r, false, reason, null /* pendingActions */);
        }
        if (!r.stopped) {
            callActivityOnStop(r, true /* saveState */, reason);
        }
        //銷毀Activity
        handleDestroyActivity(r.token, false, configChanges, true, reason);
        //啟動Activity
        handleLaunchActivity(r, pendingActions, customIntent);
    }
}

1,當handleDestroyActivity執(zhí)行完畢就已經把ViewModelStore的實例獲取到并存放到ActivityClientRecord中,此時就開始執(zhí)行handleLaunchActivity()方法來啟動activity

2,handleLaunchActivity()這個方法也需要ActivityClientRecord這個參數(shù),而此時ActivityClientRecord這個對象正是經過handleDestroyActivity()這個方法獲取并保存了ViewModelStore 實例的對象

3,handleLaunchActivity()則調用了performLaunchActivity()方法來啟動Activity

ActivityThread{
    @Override
    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
        // Make sure we are running with the most recent config.
        handleConfigurationChanged(null, null);
        //去啟動Activity
        final Activity a = performLaunchActivity(r, customIntent);
        return a;
    }
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            if (activity != null) {
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (r.overrideConfig != null) {
                    config.updateFrom(r.overrideConfig);
                }
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }
                appContext.setOuterContext(activity);
                //調用當前打開的Activity的attach()方法
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback,
                        r.assistToken);
                r.activity = activity;
            }
            r.setState(ON_CREATE);
            //保存這條Activity記錄
            synchronized (mResourcesManager) {
                mActivities.put(r.token, r);
            }
        } catch (SuperNotCalledException e) {
            throw e;
        } catch (Exception e) {
        }
        return activity;
    }
}

4,通過代碼發(fā)現(xiàn)performLaunchActivity調用了當前正在打開的Activity的attach方法,而這個方法需要一個NonConfigurationInstances類型的參數(shù),這個參數(shù)里面就有我們的ViewModelStore實例

Activity{
     final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
        attachBaseContext(context);
        mFragments.attachHost(null /*parent*/);
        //賦值給mLastNonConfigurationInstances
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        setAutofillOptions(application.getAutofillOptions());
        setContentCaptureOptions(application.getContentCaptureOptions());
    }
    public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }
}
ComponentActivity{
    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) {
            //先去lastNonConfigurationInstance中取,沒有再創(chuàng)建
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
}

5,在attach方法里就把這個參數(shù)賦值給mLastNonConfigurationInstances,當我們獲取ViewModelStore實例的時候,就會先去mLastNonConfigurationInstances中取,如果沒有再自己創(chuàng)建一個ViewModelStore實例,到這里整個調用鏈就搞明白了

生命周期綁定

ComponentActivity{
    public ComponentActivity(){
        //訂閱生命周期,當生命周期==ON_DESTROY,清除ViewModel數(shù)據
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    if (!isChangingConfigurations()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });
    }
}
ViewModelStore{
    public final void clear() {
        //遍歷所有ViewModel并調用其clear()方法清空數(shù)據
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        //清空所有ViewModel
        mMap.clear();
    }
}

總結

1,ComponentActivity實現(xiàn)了ViewModelStoreOwner接口并實現(xiàn)了其抽象方法getViewModelStore()

2,我們通過ViewModelProvider使用默認工廠創(chuàng)建了ViewModel,通過唯一key值進行標識并保存到ViewModelStore中

3,當切換橫豎屏的時候ActivityThread接收到RELAUNCH_ACTIVITY消息,就會調用Activity的retainNonConfigurationInstances()方法獲取我們的ViewModelStore實例并保存起來

4,當Activity啟動并調用attach()方法時就將切換橫豎屏前的ViewModel恢復過來

5,當我們獲取ViewModelStore實例的時候會調用先getLastNonConfigurationInstance()方法去取ViewModelStore,如果為null就會重新創(chuàng)建ViewModelStore并存儲在全局中

6,當生命周期發(fā)生改變,并且狀態(tài)為ON_DESTROY,清除ViewModel數(shù)據以及實例

到此這篇關于Android Jetpack庫剖析之ViewModel組件篇的文章就介紹到這了,更多相關Android ViewModel內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Android開發(fā)案例手冊Application跳出dialog

    Android開發(fā)案例手冊Application跳出dialog

    這篇文章主要為大家介紹了Android開發(fā)案例手冊Application跳出dialog,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • 詳解MVP模式在Android開發(fā)中的應用

    詳解MVP模式在Android開發(fā)中的應用

    MVP是MVC衍生而來的,很早以前就由某軟公司提出,近年來在Android應用開發(fā)中越來越多的被提及,越來越重要了。這篇文章主要介紹了詳解MVP模式在Android開發(fā)中的應用,有興趣的可以了解一下。
    2016-11-11
  • Android實現(xiàn)文字動態(tài)高亮讀取進度效果

    Android實現(xiàn)文字動態(tài)高亮讀取進度效果

    這篇文章主要為大家詳細介紹了Android實現(xiàn)文字動態(tài)高亮讀取進度效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • Android UI實現(xiàn)SlidingMenu側滑菜單效果

    Android UI實現(xiàn)SlidingMenu側滑菜單效果

    這篇文章主要為大家詳細介紹了Android UI實現(xiàn)SlidingMenu側滑菜單效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • AndroidStudio實現(xiàn)能在圖片上涂鴉程序

    AndroidStudio實現(xiàn)能在圖片上涂鴉程序

    這篇文章主要為大家詳細介紹了AndroidStudio實現(xiàn)能在圖片上涂鴉程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • RecyclerView實現(xiàn)仿支付寶應用管理

    RecyclerView實現(xiàn)仿支付寶應用管理

    這篇文章主要為大家詳細介紹了RecyclerView實現(xiàn)仿支付寶應用管理的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • Android 進度條使用詳解及示例代碼

    Android 進度條使用詳解及示例代碼

    本文主要介紹Android 進度條的知識,這里整理了相關資料及實現(xiàn)示例代碼,有需要的小伙伴可以參考下
    2016-09-09
  • Android利用軟引用和弱引用避免OOM的方法

    Android利用軟引用和弱引用避免OOM的方法

    Java從JDK1.2版本開始,就把對象的引用分為四種級別,從而使程序能更加靈活的控制對象的生命周期。這四種級別由高到低依次為:強引用、軟引用、弱引用和虛引用。本文給大家介紹Android利用軟引用和弱引用避免OOM,需要的朋友一起學習吧
    2016-04-04
  • Android仿今日頭條滑動頁面導航效果

    Android仿今日頭條滑動頁面導航效果

    這篇文章主要為大家詳細介紹了Android仿今日頭條滑動頁面導航效果的相關資料,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-01-01
  • Android如何實現(xiàn)年月選擇器功能

    Android如何實現(xiàn)年月選擇器功能

    這篇文章主要介紹了Android如何實現(xiàn)年月選擇器功能,幫助大家更好的理解和學習使用Android,感興趣的朋友可以了解下
    2021-03-03

最新評論