Android Jetpack架構組件之ViewModel入門到精通

要想獲得食物,就必須一直尋找,只有這樣,才有機會。不要氣餒,就算找不到肥羊,至少能找到一只兔子——《狼道》

前言
一、簡介
(1)ViewModel是什么
(2)ViewModel有什么用
(3)有什么優點
二、基本使用
(1)添加依賴
(2)繼承ViewModel
(3)使用方式
三、源碼分析
四、總結
五、內容推薦
六、項目參考


前言

——這篇主要是梳理一下Jetpack架構組件之一的ViewModel,并結合樓主所學做個總結。面向那些還不認識ViewModel的同學們。看完這篇可以快速了解它,并輕松使用。也想請教前輩們指點文章中的錯誤或不足的地方。本篇只針對ViewModel,不會拓展額外的知識如MVVM,若想了解更多關于Jetpack組件知識可以看樓主寫的Jetpack專欄。

一、簡介

(1)ViewModel是什么

——ViewModel 是google推出的Jetpack架構組件之一,設計成以生命周期的方式存儲和管理UI相關的數據。

舉個列子來消化一下:

——當發生橫豎屏切換或其他意外導致Activity重啟時,里面的臨時數據將會丟失。以前可以利用onSaveInstanceState()保存簡單的數據并在onCreate()中恢復,但只適用于少量可以序列化反序列化的數據,并不能適用于任何情況。這時就可以使用ViewModel來管理這些數據。當然ViewModel并不只 只有這個作用。

那ViewModel為什么可以管理這些數據呢?

主要還是因為ViewModel的生命周期比Activtiy生命周期來的更長。如:

這就要求我們在onCreate()方法時就啟動ViewModel。

從圖中可以看出當Activity意外重啟時,ViewModel也一直存活,所以把數據存交給ViewModel管理后就不會意外丟失數據。

(2)ViewModel有什么用

  1. 可以存儲和管理因Activity意外重啟(如:屏幕切換)丟失的數據。
  2. 可以管理Acitvity中使用的異步調用,或監聽事件因Activity銷毀而沒有及時清理造成的內存泄漏。
  3. 可以將Activity中有關數據獲取的操作移到ViewModel里面,實現視圖與數據相互分離,更容易維護。
  4. 可以在Fragment之間共享數據。

(3)有什么優點

針對用法,可以得出以下優點:

  1. 存儲和管理數據
  2. 避免內存泄漏
  3. 解耦
  4. 共享數據

?二、基本使用

(1)添加依賴

可以使用androidx的appcompat,里面包含許多的依賴,包括viewodel。

implementation 'androidx.appcompat:appcompat:1.0.0'

當然也可以添加自己熟悉的版本,每個版本使用方式都有稍微差別:

    implementation 'androidx.lifecycle:lifecycle-viewmodel:2.1.0'

樓主這邊要分析的是官網上的例子,所以添加的依賴是:

implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'

這個依賴主要作用是在使用ViewModel的基礎上多封裝了一個ViewModelProviders類。

(2)繼承ViewModel

因為ViewModel是個抽象類,所以需要聲明一個類來繼承它。

public class MyViewModel extends ViewModel {
    private String name ="張三";
    private String data ="網絡數據";
    //獲取用戶名字
    public String getUserName(){
        return name;
    }
    //獲取數據
    public String loadData(){
        //加載網絡數據的邏輯
        return data;
    }
}

其實這邊獲取網絡數據的時候可以配合LiveData一起使用,因為加載網絡數據都會有一點的延遲。并能立即就得到數據,所以使用liveData的話,當有數據回調的時候,可以通知UI更新。但這邊只單純介紹ViewModel,就不詳細說明了。想要了解LiveData 請看《Android Jetpack架構組件之LiveData》

(3)使用方式

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
        Log.e(TAG, "onCreate: "+viewModel.getUserName() );
        Log.e(TAG, "onCreate: "+viewModel.loadData() );
    }
}

通過ViewModelProviders獲取到MyViewModel的實例,就可以使用里面的方法了。

注意:ViewModel絕對不能引用視圖、生命周期或任何可能包含對活動上下文的引用的類。

這里就不展示太復雜的代碼了,一切從簡。主要目的是想讓大家一眼就能明白它是什么,如何實現的,用最少的時間,掌握知識。

再通過分析源碼來了解它。

三、源碼分析

通過使用步驟來分析一下源碼實現過程。

(1)ViewModelProviders.of(this).get(MyViewModel.class)

首先是調用了ViewModelProvidersof()方法:源碼如下

    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        return of(activity, null);
    }

    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @Nullable Factory factory) {
        //獲取Application實例
        Application application = checkApplication(activity);
        /**
         * 若沒有自定義Factor'y的情況下默認使用viewModel提供的AndrodiViewModelFactory
         * of該方法也是androidx.lifecycle:lifecycle-extensions依賴給我們封裝好
         * 若不添加上面依賴的情況下 使用的時候需要手動加入該factory
         */
        if (factory == null) {
            //詳細看 》2
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        //詳細看 》3
        return new ViewModelProvider(activity.getViewModelStore(), factory);
    }

(2)ViewModelProvider.AndroidViewModelFactory.getInstance(application)

/**
* AndroidViewModelFactory 是ViewModelProvider的一個靜態內部類
* 繼承NewInstanceFactory類并重寫了create方法
* 主要是判斷ViewModel是不是繼承與AndroidVeiwMode,
* 如果是通過傳入application實例,使繼承于AndroidViewMode的實例可以拿到application
* 區別:
* 繼承ViewModel拿不到Application實例而繼承于AndroidViewMode可以拿到Application實例
* 所以AndroidViewModelFactory作用:是封裝了一層可以得到Application實例的viewmodel
*/
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
        private static AndroidViewModelFactory sInstance;
        /**
         * 使用單例模式 獲取AndroidViewModelFactory實例
         */
        @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);
        }
    }
/**
 * AndroidViewModelFactory 繼承了 NewInstanceFactory類 并重寫了create方法
 * NewInstanceFactory 主要作用是通過create方法利用反射創建一個類的實例
 */
public static class NewInstanceFactory implements Factory {

        @SuppressWarnings("ClassNewInstance")
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            //noinspection TryWithIdenticalCatches
            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);
            }
        }
    }

(3)new ViewModelProvider(activity.getViewModelStore(), factory)

    /**
    * 創建了ViewModelProvider實例,這樣就可以使用ViewModelProvider里面的方法
    */
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }

(4)getViewModelStore()

   /**
    * 當配置發生更改時一般會造成數據丟失 而NonConfigurationInstances實例則可以在
    * 配置發生變化時保存一些數據和狀態,在oncreate方法恢復使數據不會丟失。 
    * 當配置發生變化的時候它保存了ViewModelStore。
    * 所以這里先從NonConfigurationInstance實例中獲取ViewModelStore
    * 否則就新建一個ViewModelStore
    */
 @NonNull
    @Override
    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) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

/**
 * ViewModelStore 用于緩存ViewModel的一個操作類
 */
public class ViewModelStore {
    //用于存儲ViewModel的集合
    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();
    }
}

(5)ViewModelProviders.of(this).get(MyViewModel.class)

——of方法拿到了ViewModelProvider實例就可以使用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);
    }

    /**
    * 獲取ViewModel實例
    */
    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        //從集合中獲取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.
            }
        }
        //通過of()方法得到的Factory實例,調用create方法。利用反射獲取到ViewModel的實例
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        //保存viewModel實例
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }

其實主要還是利用了反射得到了ViewModel的實例。我們才可以使用ViewModel里面的方法。

那么什么是反射呢? 這里簡單介紹一下:

反射機制:反射機制允許程序在執行期借助于Reflection API取得任何類的內部信息,并能直接操作任意對象的內部屬性及方法

優點:可以實現動態創建對象和編譯,體現出很大的靈活性

缺點:對性能有影響,此類操作總是慢于直接執行相同的操作

(6)最后說一下VierModel是如何銷毀的

    /**
    * 在FragmentActivity的onDestroy方法中調用了mViewModelStore.clear()
    */
     @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mViewModelStore != null && !isChangingConfigurations()) {
            mViewModelStore.clear();
        }
        mFragments.dispatchDestroy();
    }

    /**
     * 先遍歷ViewModel實例 調用各自的clear() 
     * 再清除集合中的ViewModel實例
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
    /**
    * 清除一些標記 并調用onCleared()
    */
    @MainThread
    final void clear() {
        mCleared = true;
        // Since clear() is final, this method is still called on mock objects
        // and in those cases, mBagOfTags is null. It'll always be empty though
        // because setTagIfAbsent and getTag are not final so we can skip
        // clearing it
        if (mBagOfTags != null) {
            synchronized (mBagOfTags) {
                for (Object value : mBagOfTags.values()) {
                    // see comment for the similar call in setTagIfAbsent
                    closeWithRuntimeException(value);
                }
            }
        }
        onCleared();
    }
    //通過繼承ViewModel 可以重寫該方法。 該方法會在ViewMode被銷毀前調用
    protected void onCleared() {
    }

四、總結

通過源碼分析ViewModel有三個重要的類:ViewModel 、ViewModelProvider 、 ViewModelStore

  • ViewModel :負責準備和管理數據的類,該抽象類其實是聲明一些通用方法

  • ViewModelProvider :ViewModel 的核心類,主要是利用反射實例化出ViewModel 對象。利用工廠模式生產出具體的ViewModel 實例。

  • ViewModelStore:緩存ViewModel實例的一些操作(存儲、獲取、清除)

核心原理簡單通俗描述如下:

  1. ViewModel類存儲了Actvity的UI數據

  2. ViewModelStore又存儲了ViewModel實例

  3. 在配置發生變化的時候在FragmentActivity.onRetainNonConfigurationInstance()方法中利用NonConfigurationInstances保存了ViewModelStore實例

  4. 并在FragmentActivtiy.oncreate()方法中恢復了ViewModelStore。也就是保存了ViewModel。所以數據才不會在配置更改時丟失

  5. 最后在FragmentActivtiy.onDestroy()方法中清除存儲在ViewModelStore中的ViewModel對象。

五、內容推薦

六、項目參考

自己整理的一個工具演示項目,有興趣可以看下

Github / apk下載體驗地址

若您發現文章中存在錯誤或不足的地方,希望您能指出!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容