Jetpack(二)—— ViewModel

1 ViewModel简介

Android平台上之所以会出现诸如MVPMVVM之类的项目架构,就是因为在传统的开发模式下,Activity的任务实在是太重 了,既要负责逻辑处理,又要控制UI展示,甚至还得处理网络回调,等等。在一个小型项目中这样写或许没有什么问题,但是如果在大型项目中仍然使用这种写法的话,那么这个项目将会变得非常臃肿并且难以维护,因为没有任何架构上的划分。

以下是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类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel类让数据可在发生屏幕旋转等配置更改后继续留存。

**ViewModel的作用是存放和界面相关的数据,分担Activity/Fragment的一部分工作,同时维护自己独立的生命周期。**也就是说,只要是界面上能看得到的数据,它的相关变量都应该存放在ViewModel中,这样可以在一定程度上减少Activity中的逻辑。

**另外,Activity/Fragment经常需要处理一些异步调用,要管理这些调用,并确保在其销毁后清理这些调用以避免潜在的内存泄漏。**此项管理需要大量的维护工作,并且在为配置更改重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出已经发出过的调用。

**还有就是当系统配置发生变化(如切换语言)或者横竖屏切换时,导致Activity销毁重建,与界面相关的数据都会丢失。**对于简单的数据,Activity可以使用 onSaveInstanceState() 方法从 onCreate() 中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。这个时候就可以使用ViewModel来解决问题。

2 ViewModel的生命周期

下图说明了Activity经历屏幕旋转而后结束时所处的各种生命周期状态,该图还在关联的Activity生命周期的旁边显示了ViewModel的生命周期。此图说明了Activity的各种状态,这些基本状态同样适用于Fragment的生命周期。

ViewModel

ViewModel的生命周期方法只有一个onCleared(),在ViewModel实例被清除的时候回调。

ViewModel实例存在的时间范围是:获取ViewModel实例时传递给ViewModelProviderLifecycleViewModel将一直留在内存中,直到限定其存在时间范围的Lifecycle永久消失:对于activity,是在activity完成时;而对于fragment,是在fragment分离时。

ViewModelProvider(<Activity或Fragment实例>).get(<定义的ViewModel>::class.java)

通常在系统首次调用Activity对象的 onCreate() 方法时请求ViewModel。系统可能会在activity的整个生命周期内多次调用 onCreate(),如在旋转设备屏幕时。因此,这里没有使用new来创建ViewModel实例。

另外,当手机发生横竖屏旋转的时候, Activity会被重新创建,同时存放在Activity中的数据也会丢失。**而ViewModelActivity不同, 它可以保证在手机屏幕发生旋转的时候不会被重新创建,只有当Activity退出的时候才会跟着Activity一起销毁。**因此,将与界面相关的变量存放在ViewModel当中, 这样即使旋转手机屏幕,界面上显示的数据也不会丢失。

3 ViewModel的基本用法

第一步:添加依赖

由于Jetpack中的组件通常是以AndroidX库的形式发布的,因此一些常用的Jetpack组件会在创建Android项目时自动被包含进去。 不过如果想要使用ViewModel组件,还需要在app/build.gradle文件中添加如下依赖:

dependencies {
	....
	implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
}

第二步:创建ViewModel

通常来讲,比较好的编程规范是给每一个ActivityFragment都创建一个对应的ViewModel,因此这里为TimerActivity创建一个对应的TimerViewModel类,并让它继承自ViewModel,代码如下所示:

class TimerViewModel: ViewModel() {
}

根据前面所学,所有与界面相关的数据都应该放在ViewModel。要实现 一个计数器的功能,就可以在ViewModel中加入一个counter变量用于计数,如下所示:

class TimerViewModel : ViewModel() {
    var counter = 0
}

第三步:获取ViewModel

在界面上添加一个按钮,每点击一次按钮就让计数器加1,并且把最新的计数显示在界面上。布局文件非常简单,一个TextView用于显示当前的计数,一个Button用于对计数器加1。 接着开始实现计数器的逻辑,修改TimerActivity中的代码,如下所示:

class TimerActivity : AppCompatActivity() {

    lateinit var viewModel: TimerViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_timer)

        viewModel = ViewModelProvider(this).get(TimerViewModel::class.java)
        plusOneBtn.setOnClickListener {
            viewModel.counter++
            refreshCounter()
        }
        refreshCounter()
    }

    private fun refreshCounter() {
        infoText.text = viewModel.counter.toString()
    }
  
}

这里最需要注意的是,不可以直接去创建ViewModel的实例,而是一定要通过ViewModelProvider来获取ViewModel的实例, 具体语法规则如下:

ViewModelProvider(<Activity或Fragment实例>).get(<定义的ViewModel>::class.java)

之所以要这么写,是因为ViewModel有其独立的生命周期,并且其生命周期要长于Activity。 如果在onCreate()方法中创建ViewModel的实例,那么每次onCreate()方法执行的时候,ViewModel都会创建一个新的实例,这样当手机屏幕发生旋转的时候,就无法保留其中的数据了。

除此之外的其他代码应该都是非常好理解的,refreshCounter()方法用来显示当前的计数,然后每次点击按钮的时候对计数器加1,并调用refreshCounter()方法刷新计数。

如果尝试通过侧边工具栏旋转一下模拟器的屏幕,就会发现Activity虽然被重新创建了,但是计数器的数据却没有丢失。

ViewModel

4 向ViewModel传递参数

由于所有ViewModel的实例都是通过ViewModelProvider来获取的,因此没有任何地方可以向ViewModel的构造函数中传递参数。这个问题需要借助ViewModelProvider.Factory就可以实现了。

现在的计数器虽然在屏幕旋转的时候不会丢失数据,但是如果退出程序之后再重新打开,那么之前的计数就会被清零了。接下来就对这一功能进行升级,保证即使在退出程序后又重新打开的情况下,数据仍然不会丢失。

实现这个功能需要在退出程序的时候对当前的计数进行保存,然后在重新打开程序的时候读取之前保存的计数,并传递TimerViewModel。因此,这里修改TimerViewModel中的代码,如下所示:

class TimerViewModel(countReserved: Int) : ViewModel() {
    var counter = countReserved
}

现在给TimerViewModel的构造函数添加了一个countReserved参数,这个参数用于记录之前保存的计数值,并在初始化的时候赋值给counter变量。

接下来就是借助ViewModelProvider.FactoryTimerViewModel的构造函数传递数据了。

新建一个TimerViewModelFactory类,并让它实现ViewModelProvider.Factory接口, 代码如下所示:

class TimerViewModelFactory(private val countReserved: Int) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return TimerViewModel(countReserved) as T
    }
}

可以看到,MainViewModelFactory的构造函数中也接收了一个countReserved参数。另外ViewModelProvider.Factory接口要求必须实现create()方法,因此这里在create()方法中创建了MainViewModel的实例,并将countReserved参数传了进去。 为什么这里就可以创建MainViewModel的实例了呢?因为create()方法的执行时机和Activity的生命周期无关,所以不会产生之前提到的问题。

另外,在界面上添加一个清零按钮,方便用户手动将计数器清零。修改TimerActivity中的代码,如下所示:

class TimerActivity : AppCompatActivity() {

  lateinit var viewModel: TimerViewModel
  lateinit var sp: SharedPreferences

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_timer)

    sp = getPreferences(Context.MODE_PRIVATE)
    val countReserved = sp.getInt("count_reserved", 0)

    viewModel = ViewModelProvider(
      this,
      MainViewModelFactory(countReserved)
    ).get(TimerViewModel::class.java)


    clearBtn.setOnClickListener {
      viewModel.counter = 0
      refreshCounter()
    }
    
    ...
    
		refreshCounter()
  }

  override fun onPause() {
    super.onPause()
    sp.edit {
      putInt("count_reserved", viewModel.counter)
    }
  }
  
  ...
}

onCreate()方法中,首先获取了SharedPreferences的实例,然后读取之前保存的计数值,如果没有读到的话,就使用0作为默认值。接下来在ViewModelProvider中,额外传入了一个TimerViewModelFactory参数,这里将读取到的计数值传给了 TimerViewModelFactory的构造函数。注意,只有用这种写法才能将计数值最终传递给TimerViewModel的构造函数。

剩下的代码就比较简单了,在Clear按钮的点击事件中对计数器进行清零,并且在onPause()方法中对当前的计数进行保存,这样可以保证不管程序是退出还是进入后台,计数都不会丢失。

现在重新运行程序,点击数次Plus One按钮,然后退出程序并重新打开,会发现,计数器的值是不会丢失的。

ViewModel

5 源码分析

5.1 ViewModel

以下是ViewModel的相关源码,ViewModel是一个抽象类,其中有一个onCleared()方法,当与之相关的Lifecycle被销毁的时候,该方法会被系统调用,我们可以在这个方法中做一些释放资源的相关操作:

public abstract class ViewModel {
    @Nullable
    private final Map<String, Object> mBagOfTags = new HashMap();
    private volatile boolean mCleared = false;

    public ViewModel() {
    }

    protected void onCleared() {
    }

    @MainThread
    final void clear() {
        this.mCleared = true;
        if (this.mBagOfTags != null) {
          synchronized(this.mBagOfTags) {
            Iterator var2 = this.mBagOfTags.values().iterator();

            while(var2.hasNext()) {
              Object value = var2.next();
              closeWithRuntimeException(value);
            }
          }
        }

        this.onCleared();
    }

}

当系统配置或者切换横竖屏时,虽然Activity调用了onDestroy()方法,但是ViewModel.onCleared()方法并不会被调用,这是因为:

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

    public ComponentActivity() {
      	...
        this.getLifecycle().addObserver(new LifecycleEventObserver() {
        public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Event event) {
          if (event == Event.ON_DESTROY) {
            ComponentActivity.this.mContextAwareHelper.clearAvailableContext();
            if (!ComponentActivity.this.isChangingConfigurations()) { // 注释1
              ComponentActivity.this.getViewModelStore().clear();
            }
          }

        }
      });
      ...
 	 }
}

public class ViewModelStore {
    private final HashMap<String, ViewModel> mMap = new HashMap();

    public final void clear() {
        Iterator var1 = this.mMap.values().iterator();

        while(var1.hasNext()) {
          ViewModel vm = (ViewModel)var1.next();
          vm.clear();
        }

        this.mMap.clear();
    }
}

在注释1处有关于系统参数的一些判断,因此在满足event == Event.ON_DESTROY且参数没有改动的前提下才可以调用ViewModelStore.clear() -> ViewModel.clear() -> ViewModel.onCleared()方法。

以下是ComponentActivity.this.getViewModelStore()的相关方法:

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
  LifecycleOwner, ViewModelStoreOwner {
  
      @NonNull
      public ViewModelStore getViewModelStore() {
          if (this.getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the Application instance. You can't request ViewModel before onCreate call.");
          } else {
            this.ensureViewModelStore();
            return this.mViewModelStore;
          }
      }

      void ensureViewModelStore() {
          if (this.mViewModelStore == null) {
            ComponentActivity.NonConfigurationInstances nc = (ComponentActivity.NonConfigurationInstances)this.getLastNonConfigurationInstance(); // 注释1
            if (nc != null) {
              this.mViewModelStore = nc.viewModelStore;
            }

            if (this.mViewModelStore == null) {
              this.mViewModelStore = new ViewModelStore();
            }
          }

      }
  	}
}

对于注释1处的ComponentActivity.NonConfigurationInstancesActivity.getLastNonConfigurationInstance()

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

    static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;

        NonConfigurationInstances() {
        }
    }
}


public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2 {

    NonConfigurationInstances mLastNonConfigurationInstances;
  
    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,
              IBinder shareableActivityToken) {
      	...
				mLastNonConfigurationInstances = lastNonConfigurationInstances;
      	...
    }

    public Object getLastNonConfigurationInstance() {
      	return mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.activity : null;
    }

    static final class NonConfigurationInstances {
        Object activity;
        HashMap<String, Object> children;
        FragmentManagerNonConfig fragments;
        ArrayMap<String, LoaderManager> loaders;
        VoiceInteractor voiceInteractor;
    }
}

Activity内部维护着一个NonConfigurationInstances类型的变量mLastNonConfigurationInstances,在Activity.attach方法中被赋值,其中的lastNonConfigurationInstances则是从前一个被销毁的Activity中传递过来的。而getLastNonConfigurationInstance的作用就是为了从先前的Activity实例获取维护的动态的状态。

从上面的逻辑上可知,androidx.activity.ComponentActivity.mViewModelStore本质上对应着android.app.Activity.mLastNonConfigurationInstances.activity.viewModelStore

ViewModel实现的关键是ViewModelStore,其内部维护着一个HashMap<String, ViewModel>用来存储多个ViewModel,即本质上就是一个用来维护ViewModelmap容器:

public class ViewModelStore {
    private final HashMap<String, ViewModel> mMap = new HashMap();

    public ViewModelStore() {
    }

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = (ViewModel)this.mMap.put(key, viewModel);
        if (oldViewModel != null) {
          oldViewModel.onCleared();
        }
    }

    final ViewModel get(String key) {
      	return (ViewModel)this.mMap.get(key);
    }

    Set<String> keys() {
      	return new HashSet(this.mMap.keySet());
    }

    public final void clear() {
        Iterator var1 = this.mMap.values().iterator();

        while(var1.hasNext()) {
          ViewModel vm = (ViewModel)var1.next();
          vm.clear();
        }

        this.mMap.clear();
    }
}

ViewModelStore关系紧密的类是ViewModelStoreOwner接口:

public interface ViewModelStoreOwner {
    @NonNull
    ViewModelStore getViewModelStore();
}

androidx.core.app.ComponentActivityFragment都实现了ViewModelStoreOwner接口:

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

public class Fragment implements LifecycleOwner, ViewModelStoreOwner { }  

当系统配置发生变更(如切换语言)或者横竖屏切换等导致Activity销毁重建时,此时会调用以下逻辑:

Activity销毁重建

上述流程是在应用进程中进行的,主要分为两部分:旧的Activity销毁和新的Activity重建,主要逻辑集中在Activity.handleRelaunchActivityInner中:

public final class ActivityThread extends ClientTransactionHandler implements ActivityThreadInternal {

    @Override
    public void handleRelaunchActivity(ActivityClientRecord tmp,
                    PendingTransactionActions pendingActions) {
        ...
        handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
             pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
        ...
    }

    private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
              List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
              PendingTransactionActions pendingActions, boolean startsNotResumed,
               Configuration overrideConfig, String reason) {
      	...
        handleDestroyActivity(r, false, configChanges, true, reason);
      	// destory上一个activity,然后,launch目标activity
     		...
        handleLaunchActivity(r, pendingActions, customIntent);
    }

    @Override
    public void handleDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges,
                           boolean getNonConfigInstance, String reason) {
      	performDestroyActivity(r, finishing, configChanges, getNonConfigInstance, reason);
      	...
    }

    void performDestroyActivity(ActivityClientRecord r, boolean finishing,
                   int configChanges, boolean getNonConfigInstance, String reason) {
      	...
        r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances(); // 1
      	...
    }


  @Override
  public Activity handleLaunchActivity(ActivityClientRecord r,
           PendingTransactionActions pendingActions, Intent customIntent) {
    	...
      final Activity a = performLaunchActivity(r, customIntent);
    	...
  }

  private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    	...
      Activity activity = null;
      try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent);
        ...
      } catch (Exception e) {
        ...
      }

    	try {
          ...
          if (activity != null) {
            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.shareableActivityToken);

            if (customIntent != null) {
              activity.mIntent = customIntent;
            }
            r.lastNonConfigurationInstances = null;
            ...
          } catch (SuperNotCalledException e) {
            throw e;

          } catch (Exception e) {
            ...
          }

        return activity;
    }
  }

performDestroyActivity并不是单纯的处理Activity.onDestory的,而是为了destory activity处理一系列的逻辑。注释1r.activity即需要被销毁的Activity实例,该处的逻辑是得到android.app.Activity.NonConfigurationInstances实例对象,而该对象会间接持有之前被销毁的Activity维护的一直被传递着的ViewModelStore实例,并将其赋值给r.lastNonConfigurationInstances

public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2 {

    NonConfigurationInstances retainNonConfigurationInstances() {
        Object activity = onRetainNonConfigurationInstance();
        ...
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.activity = activity;
        ...
        return nci;
    }

}

public class ComponentActivity extends androidx.core.app.ComponentActivity implements ContextAware, LifecycleOwner, ViewModelStoreOwner, HasDefaultViewModelProviderFactory, SavedStateRegistryOwner, OnBackPressedDispatcherOwner, ActivityResultRegistryOwner, ActivityResultCaller {
  
    @Nullable
    public final Object onRetainNonConfigurationInstance() {
        Object custom = this.onRetainCustomNonConfigurationInstance();
        ViewModelStore viewModelStore = this.mViewModelStore;
        ComponentActivity.NonConfigurationInstances nci;
        if (viewModelStore == null) {
          nci = (ComponentActivity.NonConfigurationInstances)this.getLastNonConfigurationInstance();
          if (nci != null) {
            viewModelStore = nci.viewModelStore;
          }
        }

        if (viewModelStore == null && custom == null) {
          return null;
        } else {
          nci = new ComponentActivity.NonConfigurationInstances();
          nci.custom = custom;
          nci.viewModelStore = viewModelStore;
          return nci;
        }
    }
}

ComponentActivity.onRetainNonConfigurationInstance方法中,主要是为了得到当前需要被销毁的Activity维护的mViewModelStore,如果为null,则进一步通过父类方法android.app.Activity.getLastNonConfigurationInstance去获取android.app.Activity.mLastNonConfigurationInstances.activity

android.app.Activity.mLastNonConfigurationInstances.activity是从上一次被销毁的Activity传递过来的,而mLastNonConfigurationInstances即对应着上一次被销毁的Activity维护的mViewModelStore

因此androidx.activity.ComponentActivity.onRetainNonConfigurationInstance关于ViewModel实现的意义,简单的说就是拿到之前被销毁的Activity维护的且一直在传递的ViewModelStore实例,使其接着继续传递到下一个被重建的Activity中。

ActivityThrad.handleDestroyActivity的作用就是为了处理要被销毁掉的Activity,以及在销毁之前,获取到之前被销毁的Activity维护的一直传递着的ViewModelStore实例。

对于handleLaunchActivity会进一步调用performLaunchActivity,关键参数是r,其是最开始是从handleRelaunchActivityInner中传递过来的,且在handleLaunchActivity之前,r被传入performDestroyActivity中,实现了r.lastNonConfigurationInstances持有要被传递到重建Activity中的ViewModel实例。

接着是performLaunchActivity,内部处理关于重建Activity的逻辑,如创建新的Activity实例等,其中,关键的是,会调用新的Activity实例的attach方法,并传入r.lastNonConfigurationInstances,从而实现将之前的ViewModelStore实例传入到新的Activity中,与之前的Activity.attach相呼应。

ActivityThread.handleLaunchActivity的作用则是处理新的Activity的重建流程,并把之前维护的ViewModelStore实例传递到新的Activity中。

5.2 ViewModel.onCleared()的调用时机

5.2.1 ComponentActivity正常销毁的时候

androidx.activity.ComponentActivity正常destroy的时候(不是因为系统配置发生变化或者横竖屏切换):

public class ComponentActivity extends androidx.core.app.ComponentActivity implements ContextAware, LifecycleOwner, ViewModelStoreOwner, HasDefaultViewModelProviderFactory, SavedStateRegistryOwner, OnBackPressedDispatcherOwner, ActivityResultRegistryOwner, ActivityResultCaller {
    public ComponentActivity() {
        ...
        Lifecycle lifecycle = this.getLifecycle();
        ...
        this.getLifecycle().addObserver(new LifecycleEventObserver() {
          public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Event event) {
            if (event == Event.ON_DESTROY) {
              ComponentActivity.this.mContextAwareHelper.clearAvailableContext();
              if (!ComponentActivity.this.isChangingConfigurations()) { // 不是因为系统配置发生变化
                ComponentActivity.this.getViewModelStore().clear();
              }
            }

          }
        });
      	...
    }
}

public class ViewModelStore {
  
    public final void clear() {
        Iterator var1 = this.mMap.values().iterator();

        while(var1.hasNext()) {
          ViewModel vm = (ViewModel)var1.next();
          vm.clear();
        }

        this.mMap.clear();
    }
}

public abstract class ViewModel {
    @MainThread
    final void clear() {
        this.mCleared = true;
        if (this.mBagOfTags != null) {
          synchronized(this.mBagOfTags) {
            Iterator var2 = this.mBagOfTags.values().iterator();

            while(var2.hasNext()) {
              Object value = var2.next();
              closeWithRuntimeException(value);
            }
          }
        }

        this.onCleared();
    }
}
5.2.2 添加ViewModel被覆盖时
public class ViewModelProvider {
    @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");
        } else {
          return this.get("androidx.lifecycle.ViewModelProvider.DefaultKey:" + canonicalName, modelClass);
        }
    }

    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = this.mViewModelStore.get(key);
        if (modelClass.isInstance(viewModel)) { // 1
          if (this.mFactory instanceof ViewModelProvider.OnRequeryFactory) {
            ((ViewModelProvider.OnRequeryFactory)this.mFactory).onRequery(viewModel);
          }

          return viewModel;
        } else {
          if (viewModel != null) {
          }

          if (this.mFactory instanceof ViewModelProvider.KeyedFactory) {
            viewModel = ((ViewModelProvider.KeyedFactory)this.mFactory).create(key, modelClass);
          } else {
            viewModel = this.mFactory.create(modelClass);
          }

          this.mViewModelStore.put(key, viewModel);
          return viewModel;
        }
    }

}

其中,方法参数key,其取值会有两种,第一种是用户自定义的,即直接调用ViewModelProvider.get(key, modelClass),此时key完全由用户自己控制。还有一种是调用ViewModelProvider.get(modelClass),此时key是默认的,是与modelClassclass name相关的关键字。

从注释1中可以看出,首先判断与key值对应的ViewModel是否存在,如果不存在的话,就要创建一个新的ViewModel保存起来,在this.mViewModelStore.put(key, viewModel);方法中,会调用ViewModel.onCleared()方法:

public class ViewModelStore {
    final void put(String key, ViewModel viewModel) {
      ViewModel oldViewModel = (ViewModel)this.mMap.put(key, viewModel);
      if (oldViewModel != null) {
        oldViewModel.onCleared();
      }
    }
}

5.3 ViewModelProvider.get方法

以下是ViewModelProvider.get方法的相关源码:

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

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull ViewModelProvider.Factory factory) {
      	this(owner.getViewModelStore(), factory);
    }
  
  public ViewModelProvider(@NonNull ViewModelStore store, @NonNull ViewModelProvider.Factory factory) {
      this.mFactory = factory;
      this.mViewModelStore = store;
  }

  @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");
      } else {
        return this.get("androidx.lifecycle.ViewModelProvider.DefaultKey:" + canonicalName, modelClass);
      }
  }

  @NonNull
  @MainThread
  public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
      ViewModel viewModel = this.mViewModelStore.get(key); // 1
      if (modelClass.isInstance(viewModel)) {
        if (this.mFactory instanceof ViewModelProvider.OnRequeryFactory) {
          ((ViewModelProvider.OnRequeryFactory)this.mFactory).onRequery(viewModel);
        }

        return viewModel;
      } else {
        if (viewModel != null) {
        }

        if (this.mFactory instanceof ViewModelProvider.KeyedFactory) {
          viewModel = ((ViewModelProvider.KeyedFactory)this.mFactory).create(key, modelClass);
        } else {
          viewModel = this.mFactory.create(modelClass);
        }

        this.mViewModelStore.put(key, viewModel);
        return viewModel;
      }
  }
  
  public interface Factory {
      @NonNull
      <T extends ViewModel> T create(@NonNull Class<T> var1);
  }

}

注释1处判断ViewModel是否存在,如果存在的话,直接读取,如果不存在则创建,并添加到ViewModelStore中。

6 在Fragment之间共享逻辑

有这样一个场景:如果某个Activity中有多个Fragment,使用宿主Activity作为ViewModelOwner创建ViewModel, 那么这些Fragment使用的是同一个ViewModel

ViewModel的另一特点就是同一个ActivityFragment之间可以使用ViewModel实现共享数据。

Activity中的两个或更多Fragment需要相互通信是一种很常见的现象。假设有一个Fragment,想象一下拆分视图 (list-detail) 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
    }
}

class ListFragment : Fragment() {

    private lateinit var itemSelector: Selector

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        itemSelector.setOnClickListener { item ->
            // Update the UI
        }
    }
}

class DetailFragment : Fragment() {

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
            // Update the UI
        })
    }
}

请注意,这两个Fragment都会检索包含它们的Activity。这样,当这两个Fragment各自获取ViewModelProvider时,它们会收到相同的 SharedViewModel 实例(其范围限定为该Activity)。

此方法具有以下优势:

  • Activity不需要执行任何操作,也不需要对此通信有任何了解;
  • 除了 SharedViewModel 约定之外,Fragment不需要相互了解。如果其中一个Fragment消失,另一个Fragment将继续照常工作;
  • 每个Fragment都有自己的生命周期,而不受另一个Fragment的生命周期的影响。如果一个Fragment替换另一个Fragment,界面将继续工作而没有任何问题;

参考

https://developer.android.google.cn/topic/libraries/architecture/viewmodel
ViewModel 简析
ViewModel 在 Activity 中的实现原理
Jetpack:ViewModel使用指南,实现原理详细解析!


版权声明:本文为xingyu19911016原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。