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

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