ViewModel

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

场景一: 如果系统销毁或重新创建界面控制器,则存储在其中的任何瞬态界面相关数据都会丢失。例如,应用可能会在它的某个 Activity 中包含用户列表。为配置更改重新创建 Activity 后,新 Activity 必须重新提取用户列表。对于简单的数据,Activity 可以使用 onSaveInstanceState() 方法从 onCreate() 中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。

场景二:界面控制器经常需要进行可能需要一些时间才能返回的异步调用。界面控制器需要管理这些调用,并确保系统在其销毁后清理这些调用以避免潜在的内存泄漏。此项管理需要大量的维护工作,并且在为配置更改重新创建对象的情况下,会造成资源的浪费(重新网络请求数据),因为对象可能需要重新发出已经发出过的调用。

场景三:诸如 Activity 和 Fragment 之类的界面控制器主要用于显示界面数据、对用户操作做出响应或处理操作系统通信(如权限请求)。如果要求界面控制器也负责从数据库或网络加载数据,那么会使类越发膨胀。为界面控制器分配过多的责任可能会导致单个类尝试自己处理应用的所有工作,而不是将工作委托给其他类。以这种方式为界面控制器分配过多的责任也会大大增加测试的难度。
从界面控制器逻辑中分离出视图数据所有权的操作更容易且更高效。

场景四:Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的现象。想象一下拆分视图 (master-detail) Fragment 的常见情况,假设您有一个 Fragment,在该 Fragment 中,用户从列表中选择一项,还有另一个 Fragment,用于显示选定项的内容。这种情况不太容易处理,因为这两个 Fragment 都需要定义某种接口描述,并且所有者 Activity 必须将两者绑定在一起。此外,这两个 Fragment 都必须处理另一个 Fragment 尚未创建或不可见的情况。
可以使用 ViewModel 对象解决这一常见的难点。这两个 Fragment 可以使用其 Activity 范围共享 ViewModel 来处理此类通信,如以下示例代码所示:

以上几种种情况描述摘自官网;从中得出ViewModel 目标:

1、让状态管理独立于 视图层(activity或fragment) 之外,保存状态数据,避免数据丢失导致的数据重建;
2、通过作用域可控的 共享ViewModel ,进行跨界面通信
3、及时叫停异步任务,避免造成不必要的内存泄漏;

ViewModel 的生命周期

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

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

viewmodel-lifecycle.png

实例一:简单实用ViewModel

/**
 * 视图层MainFragment
 */
class MainFragment : Fragment() {
    companion object {
        fun newInstance() = MainFragment()
    }

    private lateinit var viewModel: MainViewModel

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        return inflater.inflate(R.layout.main_fragment, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        //TODO:通过 ViewModelProvider 获取作用域在 MainFragment 的 ViewModel
        viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
        // TODO: Use the ViewModel
        viewModel.getUsers().observe(this) {
            //TODO:可执行刷新recycleview adapter 的 操作
        }
    }
}


/**
 * 状态管理MainViewModel
 */
class MainViewModel : ViewModel() {
    private val users: MutableLiveData> by lazy {
        MutableLiveData>().also {
            loadUsers()
        }
    }

    fun getUsers(): LiveData> {
        return users
    }

    private fun loadUsers() {
        // Do an asynchronous operation to fetch users.
    }

    //TODO:需要单独额外维护,这里只是示例
    data class User(val namt: String)
}

示例是如何使用 ViewModel,如何理解 ViewModel 的作用域,下面以MainFragment为例,带着问题看源码

1、class MainFragment : Fragment() {//MainFragment 继承了 Fragment}

2、public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner,
        ViewModelStoreOwner, SavedStateRegistryOwner {
    //TODO: Fragment 实现了 ViewModelStoreOwner
}

3、public interface ViewModelStoreOwner {
     //TODO: 
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
   }

4、getViewModelStore() //用于获取 ViewModelStore

5、ViewModelStore 类:
public class ViewModelStore {
    //TODO: 维护了一个mMap 用于存储ViewModel
    private final HashMap 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 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();
    }
}

6、通过上面5个步骤:
   MainFragment-->Fragment()-->ViewModelStoreOwner-->ViewModelStore{
   private final HashMap mMap = new HashMap<>();
   }
  //TODO:可以理解为: MainFragment 维护了一个 mMap 用于存储ViewModel

7、继续看ViewModel的初始化:
  viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
  |
  public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }
  |
  private final ViewModelStore mViewModelStore;//TODO:注意这里
  public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;//赋值
    }
  
8、回过头再看: viewModel = ViewModelProvider(this).get(MainViewModel::class.java) 里面的get(MainViewModel::class.java)
看 源码:
 @NonNull
 @MainThread
 public  T get(@NonNull Class 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 get(@NonNull String key, @NonNull Class modelClass) {
      //TODO: 这里mViewModelStore 既第7步中的 private final ViewModelStore mViewModelStore
        ViewModel viewModel = mViewModelStore.get(key);
      //……省略部分代码
}
|
//TODO: 在此回到了ViewModelStore 里面
final ViewModel get(String key) {
        return mMap.get(key);
} 

通过源码解读,分析出ViewModel的作用域是受创建它的视图层所控的也就是

//TODO: 当this 是 Fragment ,那么它的作用域是fragment ,
viewModel = ViewModelProvider(this@MainFragment).get(MainViewModel::class.java)
//TODO: 当this 是 Activity ,那么它的作用域是activity ,
viewModel = ViewModelProvider(this@MainActivity).get(MainViewModel::class.java)

//TODO: 当Activity内的多个Fragment要共享ViewModel 可以通过传入Activity 进行初始化共享ViewModel
viewModel = ViewModelProvider(requireActivity()).get(MainViewModel::class.java)

上面通过源码分析了,ViewModel的作用域是可控,以及多Fragment共享VIewModel的实现和原理

下一个问题,ViewModel 何时调用 clear 移除操作呢?看源码:

public final void clear() {
        for (ViewModel vm : mMap.values()) {
        //TODO: 遍历移除所有ViewModel
            vm.clear();
        }
        mMap.clear();
 }
最简单的方式,看谁调用了clear方法:
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner,
        ViewModelStoreOwner,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner {
      //TODO: 省略大部分代码……

public ComponentActivity() {
        //……省略大部分代码
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    if (!isChangingConfigurations()) {
                        //TODO:清理移除操作,避免内存泄漏
                        getViewModelStore().clear();
                    }
                }
            }
        });
      //……省略大部分代码
    }
}

你可能感兴趣的:(ViewModel)