Android Jetpack架構組件-ViewModel的使用及原理

一、什麼是ViewModel

ViewModel顧名思義,是以感知生命週期的形式來存儲和管理視圖相關的數據。ViewModel主要有以下的特點:

1.當Activity被銷燬時,我們可以使用onSaveInstanceState()方法恢復其數據,這種方法僅適用於恢復少量的支持序列化、反序列化的數據,不適用於大量數據,如用戶列表或位圖。而ViewModel不僅支持大量數據,還不需要序列化、反序列化操作。

2.Activity/Fragment(視圖控制器)主要用於顯示視圖數據,如果它們也負責數據庫或者網絡加載數據等操作,那麼一旦邏輯過多,會導致視圖控制器臃腫,ViewModel可以更容易,更有效的將視圖數據相關邏輯和視圖控制器分離開來。

3.視圖控制器經常需要一些時間纔可能返回的異步調用,視圖控制器需要管理這些調用,在合適的時候清理它們,以確保它們的生命週期不會大於自身,避免內存泄漏。而ViewModel恰恰可以避免內存泄漏的發生。

二、如何使用ViewModel

  • 2.1 繼承ViewMode,實現自定義ViewModel。
class MyViewModel : ViewModel() {
    private var users: MutableLiveData<String>? = null

    fun getUsers(): LiveData<String>? {
        if (users == null) {
            users = MutableLiveData<String>()
            setName()
        }
        return users
    }

    fun setName() {
        users?.value = "onexzgj"
    }
}

getName方法中創建一個MutableLiveData,並通過MutableLiveData的setValue方法來更新數據。

  • 2.2 使用ViewModel
    然後就可以在Activity中使用MyViewModel了,如下所示。
/**
 * ViewModel的使用
 * @author onexzgj
 */
class ViewModelActivity : AppCompatActivity() {
  lateinit var viewmodel: MyViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_view_model)
        setTitle("ViewModel的使用")

      	viewModel =  ViewModelProvider(this).get(MyViewModel::class.java)
        viewModel.getUsers()?.observe(this, Observer {
            Log.d("ViewModel", it)
        })
    }
}

通過獲取一個MyViewModel的實例,然後配合LiveData就可以觀察Name的變化,輸出結果爲:

在這裏插入圖片描述

如果重新創建了該 Activity,它接收的 MyViewModel 實例與第一個 Activity 創建的實例相同。當所有者 Activity 完成時,框架會調用 ViewModel 對象的 onCleared() 方法,以便它可以清理資源。

三、ViewModel 的生命週期

ViewModel對象存在的時間範圍是獲取ViewModel時傳遞給 ViewModelProviderLifecycle示例中則爲Activity,之前創建ViewModel的實例是通過

    var	viewModel =  ViewModelProvider(this).get(MyViewModel::class.java)

ViewModel將一直留在內存中,直到限定其存在時間範圍的Lifecycle 永久消失:對於 Activity,是在 Activity 完成時;而對於 Fragment,是在 Fragment 分離時。

圖 1 說明了 Activity 經歷屏幕旋轉而後結束的過程中所處的各種生命週期狀態。該圖還在關聯的 Activity 生命週期的旁邊顯示了 ViewModel`的生命週期。此圖表說明了 Activity 的各種狀態。這些基本狀態同樣適用於 Fragment 的生命週期。

在這裏插入圖片描述

可以看到,通常在系統首次調用 Activity 對象的 onCreate() 方法時請求 ViewModel。系統可能會在 Activity 的整個生命週期內多次調用 onCreate(),如在旋轉設備屏幕時。ViewModel存在的時間範圍是從您首次請求 ViewModel直到 Activity 完成並銷燬。

所以當前Activity的生命週期不斷變化,經歷了被銷燬重新創建,而ViewModel的生命週期沒有發生變化。

四、在 Fragment 之間共享數據

Activity 中的兩個或更多 Fragment 需要相互通信是一種很常見的情況。想象一下主從 Fragment 的常見情況,假設您有一個 Fragment,在該 Fragment 中,用戶從列表中選擇一項,還有另一個 Fragment,用於顯示選定項的內容。這種情況不太容易處理,因爲這兩個 Fragment 都需要定義某種接口描述,並且所有者 Activity 必須將兩者綁定在一起。此外,這兩個 Fragment 都必須處理另一個 Fragment 尚未創建或不可見的情況。

可以使用 ViewModel 對象解決這一常見的難點。這兩個 Fragment 可以使用其 Activity 範圍共享 ViewModel 來處理此類通信,如以下示例代碼所示:

    class SharedViewModel : ViewModel() {
        val selected = MutableLiveData<Item>()

        fun select(item: Item) {
            selected.value = item
        }
    }

	  //主Fragemnt的邏輯
    class MasterFragment : Fragment() {

        private lateinit var itemSelector: Selector

        private lateinit var model: SharedViewModel

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            model = activity?.run {
                ViewModelProviders.of(this)[SharedViewModel::class.java]
            } ?: throw Exception("Invalid Activity")
            itemSelector.setOnClickListener { item ->
                // Update the UI
            }
        }
    }

    class DetailFragment : Fragment() {

        private lateinit var model: SharedViewModel

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            model = activity?.run {
                ViewModelProviders.of(this)[SharedViewModel::class.java]
            } ?: throw Exception("Invalid Activity")
            model.selected.observe(this, Observer<Item> { item ->
                // Update the UI
            })
        }
    }

請注意,這兩個 Fragment 都會檢索包含它們的 Activity。這樣,當這兩個 Fragment 各自獲取 ViewModelProvider 時,它們會收到相同的 SharedViewModel 實例(其範圍限定爲該 Activity)。

此方法具有以下優勢:

  • Activity 不需要執行任何操作,也不需要對此通信有任何瞭解。
  • 除了 SharedViewModel 約定之外,Fragment 不需要相互瞭解。如果其中一個 Fragment 消失,另一個 Fragment 將繼續照常工作。
  • 每個 Fragment 都有自己的生命週期,而不受另一個 Fragment 的生命週期的影響。如果一個 Fragment 替換另一個 Fragment,界面將繼續工作而沒有任何問題。

五、ViewModel原理

要講解原理,我們需要先從一個點入手,那就是第2節例子中的:

   viewmodel =  ViewModelProvider(this).get(MyViewModel::class.java)

因爲我們是在Activity中調用的,因此this的值爲Activity,我們還可以在Fragment中調用上面的方法,那麼this的值爲Fragment,因此ViewModelProviders(this)構造方法,我們以在Activity中調用爲例。

跟蹤到androidx.lifecycle.ViewModelProvider代碼示例如下所示:

    private final Factory mFactory;
    private final ViewModelStore mViewModelStore;
 
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()  //註釋1
                : NewInstanceFactory.getInstance());
    }

		
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
        this(owner.getViewModelStore(), factory);
    }

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

可以看到在構造函數中,相當於實例化了Factory和ViewModelStore對象,因爲ViewModelActivity間接繼承子ComponentActivity,由於ComponentActivity實現了HasDefaultViewModelProviderFactory,即這裏factoryHasDefaultViewModelProviderFactory,我們再查看註釋1處的getDefaultViewModelProviderFactory()方法的實現,這裏以

跟蹤到androidx.activity.ComponentActivity中的具體代碼如下所示:

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner,
        ViewModelStoreOwner,
        HasDefaultViewModelProviderFactory,
				...
       {
          
    @Override
    public ViewModelStore getViewModelStore() {
		...
        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;
    }


    @Override
    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
      	...
        if (mDefaultFactory == null) {
          	//註釋2
            mDefaultFactory = new SavedStateViewModelFactory(
                    getApplication(),
                    this,
                    getIntent() != null ? getIntent().getExtras() : null);
        }
        return mDefaultFactory;
    }

通過查看getDefaultViewModelProviderFactory()方法,裏面間接調用了androidx.lifecycle.SavedStateViewModelFactory中SavedStateViewModelFactory(…)構造方法,接下來查看SavedStateViewModelFactory()的具體實現

  @SuppressLint("LambdaLast")
    public SavedStateViewModelFactory(@NonNull Application application,
            @NonNull SavedStateRegistryOwner owner,
            @Nullable Bundle defaultArgs) {
        mSavedStateRegistry = owner.getSavedStateRegistry();
        mLifecycle = owner.getLifecycle();
        mDefaultArgs = defaultArgs;
        mApplication = application;
        mFactory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }

在這裏可以看到mApplication和mFactory的初始化,而mFactory是通過ViewModelProvider.AndroidViewModelFactory.getInstance(application)的實現,可以看到具體的實現方式如下,得到了一個AndroidViewModelFactory工廠。

      @NonNull
        public static AndroidViewModelFactory getInstance(@NonNull Application application) {
            if (sInstance == null) {
                sInstance = new AndroidViewModelFactory(application);
            }
            return sInstance;
        }

到這裏可以將ViewModelProvider(this)這部分分析完了,即初始化了一個mFactory和ViewModelStore對象,

接着回頭看ViewModelProvider.get方法的實現:

@NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();//1
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);//2
    }
    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);//3

        if (modelClass.isInstance(viewModel)) {
            //noinspection unchecked
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }

        viewModel = mFactory.create(modelClass);
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }

註釋1處得到類的名稱,對這個名稱進行字符串拼接,作爲註釋2處方法的參數,DEFAULT_KEY的值爲: “androidx.lifecycle.ViewModelProvider.DefaultKey”。
因此,註釋3處的key值實際上就是”androidx.lifecycle.ViewModelProvider.DefaultKey”+類名。根據這個key值從ViewModelStore獲取ViewModel(ViewModel的實現類)。如果ViewModel能轉換爲modelClass類的對象,直接返回該ViewModel。

否則會通過Factory創建一個ViewModel,並將其存儲到ViewModelStore中。這裏的Factory指的是AndroidViewModelFactory,它在ViewModelProvider創建時作爲參數傳進來。

到此爲止,我們已經知道了ViewModel的實現類是如何創建的了。
當創建完ViewModel的實現類後,在第2小節我們還會調用如下代碼。

    viewmodel.getUsers()?.observe(this, Observer {
            Log.d("ViewModel", it)
        })

model.getName()會返回一個MutableLiveData,接着調用了MutableLiveData的observe方法,這個在Android Jetpack架構組件-LiveData使用 這篇文章中講過,就不再贅述。

項目中所有的示例代碼及Jetpack全套框架均已上傳至OnexZgj_Github

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章