说说Android Jetpack中的ViewModel

Jetpack是Android提供的一系列的工具和组件的集合(套件),它主要目的是为了让开发人员能专注于核心业务,为公司做出更大的商业贡献(省钱)。看看google官方是怎么表述的吧

Jetpack 是一个由多个库组成的套件,可帮助开发者遵循最佳做法、减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码,让开发者可将精力集中于真正重要的编码工作

ViewModel 主要是用来保存应用UI数据的类。

比如说屏幕旋转事件中,重新创建Activity实例时,可根据ViewModel中的数据进行恢复,使得用户看上去没什么变化。实际上在技术层面,页面上是一个全新的对象。

最初Android中的数据都是直接放在Activity或者是Fragment中的,随着项目体量增加会出现各种各样的问题。最明显的一个就是不易于维护了,各种代码杂糅在一起,很混乱。Google开发团队也意识到这个问题,开始遵循单一责任原则去设计了ViewModel,当然还有一系列的其他组件,也就是整个Jetpack体系。

ViewModel遵循单一责任原则,按照规范,每一个类中只包含一个责任。

注意

1. ViewModel只负责存储UI数据,因此不要将Context传入到ViewModel中来。

也就是View、Fragement、Activity都不可传入到ViewModel中来。否则可能导致内存泄露,进而可能导致OOM问题,更重要的是破坏了ViewModel的规范。

2. ViewModel不应取代onSaveInstanceState的使用。

ViewModel: 在进程被关闭时会被销毁,主要用于存储大量数据的。

onSaveInstanceState: 存储少量数据且必须是可序列化的类型。进程被关闭时,数据不受影响。

3. ViewModel默认构造器是无参的。通过ViewModelProvider.Factory 可以创建ViewModel的自定义构造函数。

Activity负责显示UI,接收用户互动,处理UI数据交给Presenter来做。

ViewModel可以通过Repository来存储和加载应用数据的API。 

ViewModel之于反应式UI

override fun onCreate(savedInstanceState: Bundle?){

    val binding = ActivityMainBinding.inflate(layoutInflater)
    binding.viewmodel = userVeiwModel
    binding.setLifecycleOwner(this)
    setContentView(binding.root)
}

看看对应的xml文件

<layout>
    <data>
        <variable name="viewmodel"
                  type="android.eg.com.UserProfileViewModel" />
    </data>
    <TextView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{viewmodel.user.name}" />
</layout>

ViewMOdel结合LiveData、Databinding构建反应式UI,在layout中绑定数据,从而实现数据更新,界面自动更新的效果。这个应该是google开发团队也借鉴了其他语言中一些流行库的吧,与时俱进。

实现 ViewModel

举个例子,通过ViewModel来获取用户列表。

class MyViewModel : ViewModel() {
    private val users: MutableLiveData<List<User>> by lazy {
        MutableLiveData<List<User>>.also {
            loadUsers()
        }
    }
    
    fun getUsers(): LiveData<List<User>> {
        return users
    }

    fun loadUsers(): MutableLiveData<List<User>> {
        // 后台加载用户数据操作
    }
}

然后在Activity中访问该用户列表

class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?){
        val model: MyViewModel by viewModels()
        model.getUsers().observe(this, Observer<List<User>>{ users ->
            // update UI
        })
    }
}

如果重新创建了该 Activity,它接收的 MyViewModel 实例与第一个 Activity 创建的实例相同。当所有者 Activity 完成时,框架会调用 ViewModel 对象的 onCleared() 方法,以便它可以清理资源。

如果 ViewModel 需要 Application 上下文(例如,为了查找系统服务),它可以继承AndroidViewModel 类并设置用于接收 Application 的构造函数,因为 Application 类会扩展 Context

ViewModel的生命周期

先来看一张图

说明 ViewModel 随着 Activity 状态的改变而经历的生命周期。

 从图中可以看出,ViewModel的生命周期从第一次调用onCreate开始到结束而结束。

在 Fragment 之间共享数据

Fragment之间共享数据是个非常常见的场景。现在有了ViewModel使得这种共享数据通信的实现方式变得更加的简单。

//不同Fragment共享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
        })
    }
}

对应的Java代码如下

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}

public class ListFragment extends Fragment {
    private SharedViewModel model;

    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {

    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        model.getSelected().observe(getViewLifecycleOwner(), item -> {
           // Update the UI.
        });
    }
}

不同的Fragment通过new获取的SharedViewModel实例是同一个对象(其范围限定为该 Activity),逻辑看起来也更加的简洁

此方法具有以下优势:

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

 ViewModel 与 Room 和 LiveData 一起使用可替换加载器

在使用加载器的一种常见方法中,应用可能会使用 CursorLoader 观察数据库的内容。当数据库中的值发生更改时,加载器会自动触发数据的重新加载并更新界面。

看流程图如下:

现在使用ViewModel和Room、LiveData来解决问题。

ViewModel 确保数据在设备配置更改后仍然存在。

Room 在数据库发生更改时通知 LiveData

LiveData 进而使用修订后的数据更新界面。

如图所示:

 参考资料icon-default.png?t=LBL2https://developer.android.google.cn/topic/libraries/architecture/viewmodel#java

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值