• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • JetPack之ViewModel最新源碼詳細分析

    本文會基于最新版ViewModel使用方法與源碼進行詳細分析,從注冊到實現ViewModel界面數據如何保存與管理全部涉及。

    **

    簡介:

    **ViewModel是JetPack系列庫之一,它用來對組件的界面數據進行管理,且當組件的狀態發生改變時數據依然留存。

    優點:1.當所依賴組件的狀態發生改變時,例如屏幕旋轉等,界面數據不會發生改變
    2.實現MVVM架構的基礎,在日常開發中,ViewModel就是MVVM架構的VM層,它相當于MVP架構的present,我們可以將業務邏輯以及數據交互的部分放入ViewModel中。

    源碼分析:

    版本:viewmodel:1.1.1
    此處使用的示例代碼與livedata源碼分析的相同,不影響流程分析

    首先構造一個繼承于ViewModel的類
    在這里插入圖片描述
    接著創建一個factory 實例傳入ViewModelProvider(this,factory )中去并調用get方法將我們的ViewModel作為參數傳入。
    此處需要注意的是在以前的版本中直接通過 ViewModelProviders.of(this).get()方法來完成這個操作的。在最新的版本中已經取消了of方法,并且構造方法必須要傳入2個參數
    在這里插入圖片描述
    查看ViewModelProvider的構造方法可看出構造ViewModelProvider時必須傳入2個參數才行,其中第一個參數為ViewModelStore,第二個參數為factory,
    ViewModelStore用來存儲和管理ViewModelfactory則用來對ViewModel進行實例化。此時構建ViewModelProvider時傳入的參數為所依賴的組件的ViewModelStore,也就是說,我們通過組件的ViewModelStore來對ViewModel進行存儲和管理。
    在這里插入圖片描述

    其中factory需要我們在構造的時候進行傳入,ViewModelProvider給我們提供了3種實例化的方法,分別是簡單工廠模式構造,通過反射構造實例,以及我們自己實現factory構造實例。

    當我們使用ViewModelProvider.NewInstanceFactory()構造實例時,查看源碼可看出,它實現了Factory 接口,并且在其內部的create方法中會調用我們在之前get方法中傳入的viewmodelnewInstance方法。這個方法需我們自己在viewmodel中去實現,用來返回viewmodel實例,如果實例化出錯則會拋出異常

       public static class NewInstanceFactory implements Factory {
    
            @SuppressWarnings("ClassNewInstance")
            @NonNull
            @Override
            public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
                //noinspection TryWithIdenticalCatches
                try {
                //返回modelClass實例對象
                    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);
                }
            }
        }
    

    使用AndroidViewModelFactory 方式反射生成ViewModel,該Factory使用單例模式進行構建 ,所以它的生命周期等同于應用程序的生命周期,且只有一個實例,推薦使用此模式來獲取factory,當它的create方法被調用時,會通過application利用反射創建一個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 {
                    //通過反射創建viewmodel
                        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);
            }
        }
    

    第三種方式就是直接實現Factory接口,并自己實現create方法,我們需要在該create方法內,返回viewmodel的實例

      /**
         * Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
         */
        public interface Factory {
            /**
             * Creates a new instance of the given {@code Class}.
             * <p>
             *
             * @param modelClass a {@code Class} whose instance is requested
             * @param <T>        The type parameter for the ViewModel.
             * @return a newly created ViewModel
             */
            @NonNull
            <T extends ViewModel> T create(@NonNull Class<T> modelClass);
        }
    

    當我們在構造ViewModelProvider時,會將我們傳入的這2個參數進行保存,

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

    當我們調用get方法時

        public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        //獲取到ViewModel的類名
            String canonicalName = modelClass.getCanonicalName();
            if (canonicalName == null) {
                throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
            }
            //再次進行回調
            return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
        }
    
    

    在該方法內首先會從ViewModelStore中通過key去獲取ViewModel ,這個key就是上面的DEFAULT_KEY +類名。如果找到則直接返回,否則調用factorycreate方法創建ViewModel實例放入ViewModelStore進行管理和保存并返回該實例

        public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        //從ViewModelStore中獲取viewModel 
            ViewModel viewModel = mViewModelStore.get(key);
    	//如果找到則直接返回
            if (modelClass.isInstance(viewModel)) {
                //noinspection unchecked
                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;
        }
    

    ViewModelStore 內部通過hashmap來對viewmodel進行管理,

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

    到此處為止源碼部分分析完畢,如何觸發viewmodel對組件界面的狀態保存繼續分析

    上面說過在構建ViewModelProvider時傳入的ViewModelStore來自于構建它所傳入的this,也就是當前組件,我們知道ViewModelStore起到了對viewmodel的管理作用,viewmodel的獲取和存儲都是通過它來完成。

        public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
        在這里調用了組件的owner.getViewModelStore()方法
            this(owner.getViewModelStore(), factory);
        }
    
    

    追蹤getViewModelStore方法,發現它是一個ViewModelStoreOwner 接口中的一個方法

    public interface ViewModelStoreOwner {
        /**
         * Returns owned {@link ViewModelStore}
         *
         * @return a {@code ViewModelStore}
         */
        @NonNull
        ViewModelStore getViewModelStore();
    }
    

    這里發現它的實現類有很多個,我們一般在ViewModelProvider中傳入的是fragment或者activity,而fragment其實最終也是調用的activity的getViewModelStore方法這里查看activity如何處理。
    在這里插入圖片描述

    發現在activity的父類ComponentActivity中有這個方法的實現,通過getViewModelStore去獲取一個ViewModelStore ,當ViewModelStore 獲取為空時,會調用getLastNonConfigurationInstance方法去恢復狀態改變前的NonConfigurationInstances,和這個方法對應的是onRetainNonConfigurationInstance,該方法會在組件狀態改變時去保存數據信息,由系統調用

       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.");
            }
            //如果當前組件的mViewModelStore 為空,則從NonConfigurationInstances 中去獲取
            if (mViewModelStore == null) {
            //返回之前
                NonConfigurationInstances nc =
                        (NonConfigurationInstances) getLastNonConfigurationInstance();
                if (nc != null) {
                    // Restore the ViewModelStore from NonConfigurationInstances
                    mViewModelStore = nc.viewModelStore;
                }
                //如果在NonConfigurationInstances 中沒有找到,則創建一個新的ViewModelStore并保存起來
                if (mViewModelStore == null) {
                    mViewModelStore = new ViewModelStore();
                }
            }
            return mViewModelStore;
        }
    

    在代碼中ViewModelStore會從NonConfigurationInstances 中去嘗試獲取,我們查看viewModelStore的賦值時機即可

      static final class NonConfigurationInstances {
            Object custom;
            ViewModelStore viewModelStore;
        }
    

    查看發現它的賦值時機就是在onRetainNonConfigurationInstance中完成的,也就是說在組件的狀態發生改變時,會去嘗試獲取viewModelStore,并將viewModelStore保存在NonConfigurationInstances 中并返回。
    也就是在onRetainNonConfigurationInstance中會對viewModelStore進行保存,在getLastNonConfigurationInstance中會對viewModelStore進行恢復
    在這里插入圖片描述
    具體流程:
    當我們在activity中去實例化ViewModelProvider時,所依賴的activity在其內部會調用getViewModelStore方法去獲取一個ViewModelStore,如果沒有則創建一個,并保存起來,然后調用ViewModelProvider.get方法將我們的viewmodel實例通過ViewModelStore進行保存管理,當我們的activity的狀態發生改變,如旋轉屏幕,此時系統會調用onRetainNonConfigurationInstance方法,在這個方法內會將我們的ViewModelStore進行保存。

    我們一般會搭配livedata來使用viewmodellivedata常用來存儲與界面相關的數據元素,當我們的activity狀態改變后,界面再次從livedata中獲取界面數據時,由于livedata實例由viewmodel保管,而viewmodel存儲在ViewModelStore中,當前activity去獲取ViewModelStore時會通過getLastNonConfigurationInstance方法恢復之前的ViewModelStore,所以狀態改變前后的ViewModelStore是同一個,所以數據沒有發生任何改變,從而界面相關數據不會發生丟失。

    版權聲明:本文為qq_24856205原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/qq_24856205/article/details/106922328

    智能推薦

    LiveData詳細分析

    目錄介紹 01.LiveData是什么東西 02.使用LiveData的優勢 03.使用LiveData的步驟 04.簡單使用LiveData 05.observe()和observerForever() 06.LiveData原理介紹 07.observe訂閱源碼分析 08.setValue發送源碼分析 09.observeForever源碼 10.LiveData源碼總結 00.使用LiveD...

    Lifecycle詳細分析

    Lifecycle源碼分析 目錄介紹 01.Lifecycle的作用是什么 02.Lifecycle的簡單使用 03.Lifecycle的使用場景 04.如何實現生命周期感知 05.注解方法如何被調用 06.addObserver調用分析 07.知識點梳理和總結一下 00.使用AAC實現bus事件總線 利用LiveData實現事件總線,替代EventBus。充分利用了生命周期感知功能,可以在act...

    ConcurrentHashmap 詳細分析

    詳盡的分析 JDK8 后的ConcurrentHashmap,思路分析輔以源碼走讀,徹底讀懂 ConcurrentHashmap。 簡介 放入數據 容器元素總數更新 容器擴容 協助擴容 遍歷 簡介 在從 JDK8 開始,為了提高并發度,ConcurrentHashMap的源碼進行了很大的調整。在 JDK7 中,采用的是分段鎖的思路。簡單的說,就是ConcurrentHashMap是由多個HashM...

    ION詳細分析

    參考: http://blog.csdn.net/armwind/article/details/53454251?locationNum=2&fps=1 代碼路徑 驅動代碼: kernel-3.18/drivers/staging/android/ion Native lib代碼: system\core\libion & vendor/mediatek/proprietary/...

    CMA 詳細分析

    關于CMA的config @LINUX/android/kernel/arch/arm/configs/msm8909_defconfig CONFIG_CMA=y 已經打開 # CONFIG_CMA_DEBUG is not set # # Default contiguous memory area size: # CONFIG_CMA_SIZE_MBYTES=8 //兩個配對定義 CONFI...

    猜你喜歡

    MapReduce詳細分析

    一、MapReduce概述 1、定義 MapReduce核心功能是將用戶編寫的業務邏輯代碼和自帶默認組件整合成一個完整的分布式運算程序,并發運行在一個Hadoop集群 上。 2、MR進程 一個完整的MapR educe程序在分布式運行時有三類實例進程: **Mr AppMaster:**負責整個程序的過程調度及狀態協調。 MapTask:負責Map階段的整個數據處理流程。 ReduceTask:負...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    精品国产乱码久久久久久蜜桃不卡