<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Jetpack 組件之 ViewModel 使用與淺析

          共 31682字,需瀏覽 64分鐘

           ·

          2021-06-25 15:58

          ViewModel 是什么?

          官方解釋:

          The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.

          個人理解:
          ViewModel 是 UI 和數(shù)據(jù)的橋接層,承擔(dān) UI 層(Activity / Fragment) 的數(shù)據(jù)處理邏輯,同時擁有維護自己的生命周期,而且可以在屏幕旋轉(zhuǎn)時仍保存數(shù)據(jù)。下面是官方提供的一張屏幕旋轉(zhuǎn)時 ViewModel 的生命周期圖。

          怎么使用 ViewModel?

          這里以 Activity 中使用為例。

          1. 自定義 ViewModel 類,繼承自 ViewModel。該類將用作數(shù)據(jù)處理,并返回數(shù)據(jù)給UI
          // 這里僅是示例,推薦結(jié)合 LiveData 一起使用。
          class CustomViewModelViewMdoel(){
              private val data: List<String> = mutableListOf()
              
              fun getData(): List<String> {
                  retrun data
              }
          }
          1. 在 Activity 中獲取自定義的 ViewModel 類,調(diào)用相應(yīng)方法獲取處理后的數(shù)據(jù)
          //這里以 Activity 示例,F(xiàn)ragment 類似。
          class MyActivity : AppCompatActivity() {

              override fun onCreate(savedInstanceState: Bundle?) {
              
                  // 通過ktx 獲取自定義 ViewModel 類
                  // 需要添加依賴 implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:$viewmodel_version'
                  //val viewModel: CustomViewModel by viewModels()
                  
                  // 通過“傳統(tǒng)方式” 獲取自定義 ViewModel 類
                  val viewModel = ViewModelProvider(this).get(CustomViewModel::class.java)
                  
                  //獲取數(shù)據(jù),實現(xiàn)后續(xù)UI邏輯
                  val data = viewModel.getData()
              }
          }

          ViewModel 的原理是什么?

          原理僅講解 Activity 部分。源碼部分基于 lifecycle 2.2.0 版本 與 acitivity 1.1.0 版本。

          源碼分析分為兩部分,先從調(diào)用方法出發(fā),大體知道內(nèi)部邏輯,再從疑問入手,解答心里疑惑。

          實現(xiàn)原理

          從調(diào)用方法出發(fā)

          //通過 ViewModelProvider#get() 獲取自定義 ViewModel 對象,然后就可以調(diào)用相關(guān)方法
          ViewModelProvider(this).get(CustomViewModel::class.java)

          通過調(diào)用方法可以發(fā)現(xiàn),需要了解下 ViewModelProvider() 和 get() 方法的內(nèi)部實現(xiàn)。

          ViewModelProvider() 內(nèi)部實現(xiàn):

          public class ViewModelProvider {

              private final Factory mFactory;
              private final ViewModelStore mViewModelStore;

              // ... 省略 ...

              // 入?yún)?nbsp;ViewModelStoreOwner,通過 owner 獲取 ViewModelStore 和 ViewModelFactory
              public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
                  this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                      ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                      : NewInstanceFactory.getInstance());
              }

              /**
               * Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
               * {@code Factory} and retain them in the given {@code store}.
               *
               * @param store   {@code ViewModelStore} where ViewModels will be stored.
               * @param factory factory a {@code Factory} which will be used to instantiate
               *                new {@code ViewModels}
               */

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

              // ... 省略 ...
          }

          通過上面代碼可以發(fā)現(xiàn),ViewModelProvider 內(nèi)部維護的 ViewModelStore 和 Factory 都是通過傳入的 owner 創(chuàng)建的。根據(jù)調(diào)用傳入的 this 可以知道,此時的owner 為 Activity,那么看下 Activity 中是怎么實現(xiàn)創(chuàng)建的。

          public class ComponentActivity extends androidx.core.app.ComponentActivity implements
                  LifecycleOwner,
                  ViewModelStoreOwner,
                  HasDefaultViewModelProviderFactory 
          {

               // ... 省略 ...

              @NonNull
              @Override
              public ViewModelStore getViewModelStore() {
                  // ... 省略 ...
                  
                  if (mViewModelStore == null) {
                      // 從上次配置發(fā)生更改(例如屏幕旋轉(zhuǎn))中獲取
                      NonConfigurationInstances nc =
                             (NonConfigurationInstances) getLastNonConfigurationInstance();
                      if (nc != null) {
                          mViewModelStore = nc.viewModelStore;
                      }
                      if (mViewModelStore == null) {
                          // 直接創(chuàng)建
                          // ViewModelStore 內(nèi)部只有一個 Map 來存儲創(chuàng)建的 ViewModel
                          mViewModelStore = new ViewModelStore();
                      }
                  }
                  return mViewModelStore;
              }

              // ... 省略 ...

              @NonNull
              @Override
              public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
                  // ... 省略 ...
                  
                  // 這里判斷是否有創(chuàng)建過,沒有則直接創(chuàng)建。
                  if (mDefaultFactory == null) {
                      mDefaultFactory = new SavedStateViewModelFactory(
                              getApplication(),
                              this,
                              getIntent() != null ? getIntent().getExtras() : null);
                  }
                  return mDefaultFactory;
              }

              // ... 省略 ...
          }

          通過上面代碼可以知道,ComponentActivity 實現(xiàn) ViewModelStoreOwner 和 HasDefaultViewModelProviderFactory 接口,分別創(chuàng)建了 ViewModelStore 和 SavedStateViewModelFactory 對象。ViewModelStore 內(nèi)部維護了一個Map,用于存儲創(chuàng)建的 ViewModel。SavedStateViewModelFactory 內(nèi)部是維護了 ViewModel 的創(chuàng)建,是一個工廠類。Ok,ViewModelProvider 構(gòu)造函數(shù)的內(nèi)部邏輯到此為止。

          ViewModelProvider#get() 內(nèi)部實現(xiàn):

          public class ViewModelProvider {

              private static final String DEFAULT_KEY =
                          "androidx.lifecycle.ViewModelProvider.DefaultKey";
              
              private final Factory mFactory;
              private final ViewModelStore mViewModelStore;
              
              // ... 省略 ...

              @NonNull
              @MainThread
              public <T extends ViewModel> 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> get(@NonNull String key, @NonNull Class<T> modelClass) {
                  //從緩存中獲取
                  ViewModel viewModel = mViewModelStore.get(key);

                  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.
                      }
                  }
                  
                  // 緩存沒有,則進行創(chuàng)建。
                  // 根據(jù)上面 "ViewModelProvider() 內(nèi)部實現(xiàn)" 可以得知,mFactory = new SavedStateViewModelFactory(),
                  // 而 SavedStateViewModelFactory 實現(xiàn)的 ViewModelProvider.KeyedFactory 接口。
                  // 所以此時 (mFactory instanceof KeyedFactory) = true
                  if (mFactory instanceof KeyedFactory) {
                      viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
                  } else {
                      viewModel = (mFactory).create(modelClass);
                  }
                  // 將新創(chuàng)建的 ViewModel 存入緩存中
                  mViewModelStore.put(key, viewModel);
                  return (T) viewModel;
              }
              
              // ... 省略 ...
          }

          通過上面代碼可以得知,get() 方法內(nèi)部先從 ViewModelStore 中獲取是否已經(jīng)創(chuàng)建過,如果沒有創(chuàng)建過,則通過 SavedStateViewModelFactory#create() 方法創(chuàng)建,并將新創(chuàng)建的 ViewModel 存入 ViewModelStore 中。
          那么接下來就看下 SavedStateViewModelFactory#create() 內(nèi)部實現(xiàn)。

          public final class SavedStateViewModelFactory extends ViewModelProvider.KeyedFactory {

              private final Application mApplication;
              private final ViewModelProvider.AndroidViewModelFactory mFactory;
              private final Bundle mDefaultArgs;
              private final Lifecycle mLifecycle;
              private final SavedStateRegistry mSavedStateRegistry;
              
              // ... 省略 ...
              
              @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);
              }
              
              @NonNull
              @Override
              public <T extends ViewModel> create(@NonNull String key, @NonNull Class<T> modelClass) {
                  // 判斷是否ViewModel繼承自 AndroidViewModel,由于咱們自定義的 ViewModel 繼承自 ViewModel,所以這里不成立
                  boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
                  Constructor<T> constructor;
                  if (isAndroidViewModel) {
                      constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
                  } else {
                      // 內(nèi)部判斷傳入的 Class 構(gòu)造函數(shù)是否包含指定參數(shù)。
                      // 此時咱們自定義的 ViewModel 構(gòu)造函數(shù)未包含所有任何參數(shù),所以這里返回 null
                      constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
                  }
                
                  if (constructor == null) {
                      // 通過上面步驟可以的值,最后調(diào)用 mFactory#create() 進行創(chuàng)建。
                      return mFactory.create(modelClass);
                  }

                  // ... 省略 ...
              }
              
              // ... 省略 ...
          }    

          通過上面代碼可以發(fā)現(xiàn),SavedStateViewModelFactory#create() 內(nèi)部做了一些校驗,但是咱們自定義的 ViewModel 都沒有滿足,最后通過 ViewModelProvider.AndroidViewModelFactory.getInstance(application)#create() 來創(chuàng)建。那么就看看 AndroidViewModelFactory#create() 內(nèi)部實現(xiàn)。

          public class ViewModelProvider {
              // ... 省略 ...
              
              public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
                  // ... 省略 ...
              
                  @NonNull
                  @Override
                  public <T extends ViewModel> create(@NonNull Class<T> modelClass) {
                      // 判斷是否ViewModel繼承自 AndroidViewModel。咱們自定義的 ViewModel 繼承自 ViewModel。
                      // 所以最后調(diào)用 super.create(),即 ViewModelProvider.NewInstanceFactory#create()
                      if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
                          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);
                  }
              }

              // ... 省略 ...    
              
              public static class NewInstanceFactory implements Factory {
                  // ... 省略 ...
              
                  @SuppressWarnings("ClassNewInstance")
                  @NonNull
                  @Override
                  public <T extends ViewModel> create(@NonNull Class<T> modelClass) {
                      try {
                          // 直接通過 Class.newInstance() 進行創(chuàng)建。
                          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#create 又進行了校驗,最后調(diào)用到了 NewInstanceFactory#create() 方法,該方法直接通過 Class.newInstance() 創(chuàng)建了 ViewModel。至此,ViewModel 的創(chuàng)建過程就走完了。在文章的最后會對該過程有一個總結(jié)。

          從疑問出發(fā)

          1. ViewModel 是怎么實現(xiàn)屏幕旋轉(zhuǎn)時仍可保留數(shù)據(jù)的?

          關(guān)于屏幕旋轉(zhuǎn)仍可保留數(shù)據(jù)的重點,其實需要知道 Activity#onRetainNonConfigurationInstance(),通過該方法的注釋可以知道,當(dāng)有新配置導(dǎo)致重新創(chuàng)建實例時,系統(tǒng)會通過該方法回調(diào)通知。

          public class Activity{

               // ... 省略 ...
              
               /**
               * Called by the system, as part of destroying an
               * activity due to a configuration change, when it is known that a new
               * instance will immediately be created for the new configuration.  You
               * can return any object you like here, including the activity instance
               * itself, which can later be retrieved by calling
               * {@link #getLastNonConfigurationInstance()} in the new activity
               * instance.
               * 
               * ... 省略 ...
               * 
               */

              public Object onRetainNonConfigurationInstance() {
                  return null;
              }

              // ... 省略 ...
          }

          ComponentActivity 重寫了該方法,當(dāng)屏幕旋轉(zhuǎn)時將 viewModelStore 保存其中,這樣當(dāng)屏幕旋轉(zhuǎn),重新走 getViewModelStore() 時,先通過 getLastNonConfigurationInstances() 方法就可以獲取到之前的 viewModelStore。
          PS: 這里需要注意一點,ComponentActivity#onRetainNonConfigurationInstance() 提到了該方法禁止自己重寫!

          public class ComponentActivity extends androidx.core.app.ComponentActivity implements
                  LifecycleOwner,
                  ViewModelStoreOwner,
                  HasDefaultViewModelProviderFactory 
          {
              // ... 省略 ...
                  
              @NonNull
              @Override
              public ViewModelStore getViewModelStore() {
                  if (mViewModelStore == null) {
                      // 從上次配置發(fā)生更改(例如屏幕旋轉(zhuǎn))中獲取
                      NonConfigurationInstances nc =
                              (NonConfigurationInstances) getLastNonConfigurationInstance();
                      if (nc != null) {
                          mViewModelStore = nc.viewModelStore;
                      }
                      if (mViewModelStore == null) {
                          mViewModelStore = new ViewModelStore();
                      }
                  }
                  return mViewModelStore;
              }
              
              // ... 省略 ...

              /**
               * Retain all appropriate non-config state.  You can NOT
               * override this yourself!  Use a {@link androidx.lifecycle.ViewModel} if you want to
               * retain your own non config state.
               */

              @Override
              @Nullable
              public final Object onRetainNonConfigurationInstance() {
                  Object custom = onRetainCustomNonConfigurationInstance();

                  ViewModelStore viewModelStore = mViewModelStore;
                  if (viewModelStore == null) {
                      NonConfigurationInstances nc =
                              (NonConfigurationInstances) getLastNonConfigurationInstance();
                      if (nc != null) {
                          viewModelStore = nc.viewModelStore;
                      }
                  }

                  if (viewModelStore == null && custom == null) {
                      return null;
                  }

                  NonConfigurationInstances nci = new NonConfigurationInstances();
                  nci.custom = custom;
                  nci.viewModelStore = viewModelStore;
                  return nci;
              }
              
              // ... 省略 ...
          }

          2. ViewModel 是怎么管理生命周期,在 Activity 銷毀時清理數(shù)據(jù)的?

          public class ComponentActivity extends androidx.core.app.ComponentActivity implements
                  LifecycleOwner,
                  ViewModelStoreOwner,
                  HasDefaultViewModelProviderFactory 
          {
              // ... 省略 ...

              public ComponentActivity() {
                  Lifecycle lifecycle = getLifecycle();
                  
                  // ... 省略 ...
                  
                  // 監(jiān)聽 Activity 的生命周期變化,當(dāng)收到 Event = ON_DESTROY && 未發(fā)生配置更新時,此時為真正銷毀,清理所有數(shù)據(jù)。
                  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();
                              }
                          }
                      }
                  });

                  // ... 省略 ...
              }

              // ... 省略 ...
          }

          通過上面代碼可以發(fā)現(xiàn),依賴于 Lifecycle 提供的生命周期事件,通過 addObserver(),監(jiān)聽 Activity 的生命周期,當(dāng)接收到 ON_DESTROY 事件并且不是因為配置更新(屏幕旋轉(zhuǎn))導(dǎo)致的 destroy 時,調(diào)用 ViewModelStore#clear() 清理所有數(shù)據(jù)。

          3. 單Activity多Fragment時,怎么做到Fragment之間共享數(shù)據(jù)的?

          通過源碼分析這個問題其實已經(jīng)有結(jié)論了。當(dāng)調(diào)用 ViewModelProvider() 方法傳入的是 getActivity(), ViewModelStore 和 Factory 的創(chuàng)建都是在 Activity 中,所以 ViewModel 的數(shù)據(jù)也是在 Activity 中,所以相當(dāng)于用 Activity 來維護數(shù)據(jù),然后基于此 Activity 的 Fragment 就都可以訪問這些數(shù)據(jù)。

          總結(jié)

          簡單總結(jié)下 ViewModel 的實現(xiàn)原理:調(diào)用 ViewModelProvider() 方法,該方法內(nèi)部通過接口反向依賴 ViewModelStore 和 Factory 的實現(xiàn)(這里即反向依賴 ComponentActivity 來創(chuàng)建)。然后調(diào)用 ViewModelProvider#get() 方法,該方法內(nèi)部先從 ViewModelStore 中獲取緩存,如果沒有則調(diào)用 Factory#create() 進行創(chuàng)建,經(jīng)過各種條件校驗,最終調(diào)用 ViewModelProvider.NewInstanceFactory#create() 方法,通過 Class.newInstance() 創(chuàng)建出來,最后將新創(chuàng)建的 ViewModel 存入 ViewModelStore 中。下面為調(diào)用時序圖,可參照自行走一遍源碼。


          瀏覽 46
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本护士XXXX1819 | 操鸡巴网站 | 熟睡侵犯の奶水授乳在线 | www.色自拍 | 在线观看的A片 |