Android 基于Jetpack的MVVM架构入门指南
Posted xiaoqiang_0719
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 基于Jetpack的MVVM架构入门指南相关的知识,希望对你有一定的参考价值。
android 基于Jetpack的MVVM架构入门指南
目录
架构组件简介
Android 架构组件是一系列的组件库,可以帮助我们设计稳健、可测试且易维护的应用,主要和常用的组件包括:View Binding 、Data Binding、Lifecycle、LiveData、ViewModel
如上Android架构图,有四部分主要组件,每个组件都有其责任
-
Activity 和Fragment代表的View层,不处理业务逻辑只显示视图、处理用户交互、观察数据并展示
-
ViewModel+LiveData层,ViewModel向数据仓库Repository中请求数据,然后通过LiveData发送到View层
-
Repository层,它不是一个Android组件,而是一个普通的类,它负责从所有可用的源来获取数据
-
数据层,提供数据源的一层,主要是网络请求框架、Room数据库等
View Binding
简介
主要作用是为了替代 findViewById 在启动View Binding后,系统会为该模块下的每个xml生成一个绑定类,这个类的类名是以xml布局文件名去掉下划线后,单词首字母大写加上Binding命名的。
例如:activity_main.xml ---> ActivityMainBinding
View Binding 的优点
-
空安全:View Binding会创建对视图的直接引用,因此不存在因视图 ID 无效而引发空指针异常的风险。(我们手动findeViewById()时容易传入无效的id)
-
类型安全:每个绑定类中的字段均具有与它们在 XML 文件中引用的视图相匹配的类型。这意味着不存在发生类转换异常的风险。 val button:Button=findViewById<RecyclerView>(R.id.recyclerview)
详细介绍请参见Google官方文档: https://developer.android.google.cn/topic/libraries/view-binding
用法
View Binding 按模块启用,在app模块下的build.gradle添加如下配置
android {
...
viewBinding {
enabled = true
}
}
或者使用buildFeatures方式但需要满足如下条件:
1、Android Studio 版本 : 最低 4.1
2、Gradle 版本 : 最低版本 6.6.1
3、Gradle 插件版本配置 : 最低版本 4.1.0
android {
...
buildFeatures {
viewBinding true
}
}
如果想忽略某个布局文件,可将 tools:viewBindingIgnore="true" 属性添加到相应布局文件的根视图中
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:viewBindingIgnore="true"
tools:context=".MainActivity">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
...
</androidx.constraintlayout.widget.ConstraintLayout>
1. Activity中使用View Binding
-
调用生成的绑定类中包含的静态 inflate() 方法。此操作会创建该绑定类的实例以供 Activity 使用
-
通过调用 getRoot() 方法获取对根视图的引用
-
调用 setContentView()方法将根视图传入
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
}
binding.name.text ="Wilfried"
binding.name.setOnClickListener { }
2. Fragment中使用View Binding
Fragment中使用View Binding的步骤根Activity类似,主要区分在于是在哪个方法中进行绑定操作onCreateView、onViewCreated
onCreateView
lateinit var binding: FragmentMainBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentMainBinding.inflate(inflater, container, false)
return binding.root
}
onViewCreated
lateinit var binding: FragmentMainBinding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentMainBinding.bind(view)
}
3. ListAdapter中使用View Binding
class MyListAdapter : ListAdapter<Word, MyListAdapter.WordViewHolder>(WORDS_COMPARATOR) {
class WordViewHolder(private val viewBinding: RecyclerviewItemBinding) :
RecyclerView.ViewHolder(viewBinding.root) {
fun bind(text: Word?) {
viewBinding.textView.text = text?.word
}
companion object {
fun create(parent: ViewGroup): WordViewHolder {
val bind = RecyclerviewItemBinding
.bind(LayoutInflater.from(parent.context)
.inflate(R.layout.recyclerview_item, parent, false)
)
return WordViewHolder(bind)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WordViewHolder {
return WordViewHolder.create(parent)
}
override fun onBindViewHolder(holder: WordViewHolder, position: Int) {
holder.bind(getItem(position))
}
companion object {
private val WORDS_COMPARATOR = object : DiffUtil.ItemCallback<Word>() {
override fun areItemsTheSame(oldItem: Word, newItem: Word): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: Word, newItem: Word): Boolean {
return oldItem.word == newItem.word
}
}
}
}
ViewModel
简介
ViewModel 主要是用来存储和管理与UI相关的数据的,将一个Activity或Fragment组件相关的数据逻辑提取出来,并能适配UI组件的生命周期,如当屏幕旋转Activity重建后,ViewModel中的数据依然有效 ViewModel一般都是会配合LiveData使用
ViewModel 的生命周期
ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider 的 Lifecycle(Activity、Fragment、所有实现LifecycleOwner的生命周期对象)
我们在首次调用 Activity 对象的 onCreate() 方法时请求 ViewModel。系统有可能会在 Activity 的整个生命周期内多次调用 onCreate(),但是ViewModel只要一个,比如在旋转设备屏幕时重走生命周期,此时依然可以拿到ViewModel中最新的数据。
由上图可以看出,ViewModel 存在的时间范围是从首次请求 ViewModel 直到 Activity 完成并销毁。
详细介绍请参见Google官方文档: https://developer.android.google.cn/topic/libraries/architecture/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
}
private fun loadUsers() {
// Do an asynchronous operation to fetch users.
...
users.value = usersInfoList
}
}
Activity 中使用ViewModel
1. 默认实现方式
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
val viewModel = ViewModelProviders
.of(this).get(MainViewModel::class.java)
model.getUsers().observe(this, Observer<List<User>>{ users ->
// update UI
})
}
}
2. 通过activity-ktx扩展库
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Use the 'by viewModels()' Kotlin property delegate
// from the activity-ktx artifact
val model: MyViewModel by viewModels()
model.getUsers().observe(this, Observer<List<User>>{ users ->
// update UI
})
}
}
3. 过activity-ktx扩展库,自定义 ViewModelProvider.Factory(用于ViewModel需要传参数的情况)
-
MainActivity
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
private val wordViewModel: WordViewModel by viewModels {
WordViewModelFactory((application as WordsApplication).repository)
}
model.getUsers().observe(this, Observer<List<User>>{ users ->
// update UI
})
}
}
-
WordViewModel
class WordViewModel(private val repository: WordRepository) : ViewModel() {
val allWords: LiveData<List<Word>> = repository.allWords.asLiveData()
fun insert(word: Word) = viewModelScope.launch {
repository.insert(word)
}
}
class WordViewModelFactory(private val repository: WordRepository) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(WordViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return WordViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
LiveData
简介
LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
LiveData 的优势
-
观察者会绑定到 Lifecycle 对象(所有实现了所有实现LifecycleOwner的生命周期对象),并在其关联的生命周期遭到销毁后进行自我清理。
-
如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。
-
如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。
LiveData的子类
MutableLiveData
实现接口LiveData,并提供两个方法 setValue(T) 和 postValue(T) 用于将数据分发出去
setValue(T) 只能在主线程中调用,postValue(T) 可以在任何线程中调用。
MediatorLiveData
继承自MutableLiveData,它可以作为中间人的角色监听其他LiveData,通过addSource进行注册LiveData,当数据更新时通过MediatorLiveData倒一手再进行处理。提供如下两个方法,
addSource(LiveData< S> source, Observer<? super S> onChanged)方法
removeSource(LiveData< S> toRemote)方法
案例:如果我们只需要liveData1发出的10个值,并将其合并到liveDataMerger中。收到10个值之后,我们就停止监听liveData1并将其删除。
liveDataMerger.addSource(liveData1, new Observer() {
private int count = 1;
@Override public void onChanged(@Nullable Integer s) {
count++;
liveDataMerger.setValue(s);
if (count > 10) {
liveDataMerger.removeSource(liveData1);
}
}
});
详细介绍请参见Google官方文档: https://developer.android.google.cn/topic/libraries/architecture/livedata
用法
-
创建 LiveData 的实例以存储某种类型的数据。这通常在 ViewModel 类中完成。
class MyViewModel : ViewModel() {
// Create a LiveData with a String
val livedata: MutableLiveData<String> = MutableLiveData<String>()
}
}
-
在界面控制器(如 Activity 或 Fragment)中创建 Observer 对象。
-
使用 observe() 方法将 Observer 对象附加到 LiveData 对象。
myViewModel.livedata.observe(this, Observer {
})
-
可以使用 observeForever(Observer) 方法在没有关联的 LifecycleOwner 对象的情况下注册一个观察者。在这种情况下,观察者会被视为始终处于活跃状态
myViewModel.livedata.observeForever {
}
5.更新LiveData:LiveData 是一个抽象类,MutableLiveData 类将公开 setValue(T) 和 postValue(T) 方法,修改存储在 LiveData 对象中的值,则必须使用这些方法。
Data Binding
简介
Data Binding 简单的解释就是,之前我们需要通过id获取到控件,然后通过控件设置数据,现在有了Data Binding之后 我们可以直接在布局文件中直接绑定数据,它的侧重点是在绑定数据。
注意:在许多情况下, Data Binding可简化实现,提高性能,提供与数据绑定相同的好处。如果使用数据绑定的主要目的是取代 findViewById() 调用,请考虑改用 View Binding。
详细介绍请参见Google官方文档: https://developer.android.google.cn/topic/libraries/data-binding
用法
-
使用 Data Binding ,首先需要在 app moudle 下的 build.gradle 中添加:
android {
...
dataBinding {
enabled = true
}
...
}
-
创建一个实体类,例:Info 类
class Info(var name: String = "Wilfried",
var job: String = "Android Engineer",
var company: String = "noboAuto")
3.在这个Activity的xml(activity_my_info.xml)中的根布局下,通过Alt+Enter快捷键创建databinding的布局,同时导入 Info 类
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="myInfo"
type="com.noboauto.example.Info" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.slideshow.SlideshowFragment">
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{myInfo.name}"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
-
此时编译器会自动根据这个布局生成相应的绑定类,这里会生成一个 ActivityMyInfoBinding 的类,然后在对应的Activity获取Data Binding并绑定数据
class MyInfoActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val bindingBinding : ActivityMyInfoBinding = DataBindingUtil.setContentView(this, R.layout.activity_my_info)
bindingBinding.myInfo = Info()//已实现默认参数
}
}
Lifecycles
简介
生命周期感知型组件可执行操作来响应另一个组件(如 Activity 和 Fragment)的生命周期状态的变化
在生命周期拥有者LifecycleOwner与 生命周期的观察者LifecycleObserver之间快速方便的建立一种联系。在生命周期拥有者的生命周期变化时,观察者会收到对应的通知。
简单的说就是用来监听Activity与Fragment的生命周期变化
主要类
lifecycleRegister:
lifecycle的唯一子类,在生命周期拥有者的生命周期发生变化时触发自身状态和相关观察者的订阅回调逻辑。
lifecycleOwner
该接口的实现类可以提供lifecycleRegister的实例,主要实现类就是AppCompatActivity和Fragment。 (android.app.Activity.Activity默认不实现lifecycleOwner接口,所以推荐使用AppCompatActivity)
lifecycleObserver
该接口的实现类表示为关注生命周期事件的观察者。
详细介绍请参见Google官方文档: https://developer.android.google.cn/topic/libraries/architecture/lifecycle
用法
生命周期拥有者 LifecycleOwner
-
AppCompatActivity的实现原理是在的父类ComponentActivity中实现了LifecycleOwner,并创建和提供了LifecycleRegistry
private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
@Override
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
在ComponentActivity的子类ComponentActivity中实现了事件的分发
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
...
mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
}
枚举中的方法跟Activity生命周期一致,注意ON_ANY只要生命周期变化就会调用此方法
public enum Event {
/**
* Constant for onCreate event of the {@link LifecycleOwner}.
*/
ON_CREATE,
/**
* Constant for onStart event of the {@link LifecycleOwner}.
*/
ON_START,
/**
* Constant for onResume event of the {@link LifecycleOwner}.
*/
ON_RESUME,
/**
* Constant for onPause event of the {@link LifecycleOwner}.
*/
ON_PAUSE,
/**
* Constant for onStop event of the {@link LifecycleOwner}.
*/
ON_STOP,
/**
* Constant for onDestroy event of the {@link LifecycleOwner}.
*/
ON_DESTROY,
/**
* An {@link Event Event} constant that can be used to match all events.
*/
ON_ANY
}
-
自定义LifecycleOwner
实现LifecycleOwner接口,重写getLifecycle方法返回Lifecycle的唯一子类LifecycleRegistry
markState和handleLifecycleEvent都可以将生命周期状态分发出去,但是markState已经是过时的方法,建议使用handleLifecycleEvent
class MyActivity : Activity(), LifecycleOwner {
private lateinit var lifecycleRegistry: LifecycleRegistry
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleRegistry = LifecycleRegistry(this)
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
lifecycle.addObserver(MyInfoObserver())
lifecycle.addObserver(MyInfoObserver2())
}
override fun onStart() {
super.onStart()
lifecycleRegistry.markState(Lifecycle.State.STARTED)
}
override fun onResume() {
super.onResume()
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
}
override fun getLifecycle(): Lifecycle {
return lifecycleRegistry
}
}
生命周期的观察者 LifecycleObserver
两种方式可以实现生命周期的观察
-
实现LifecycleObserver接口,通过OnLifecycleEvent注解来回调各个生命周期状态
class MyInfoObserver : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun onMyInfoCreate() {
Log.i("wilfried", "onCreate")
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMyInfoStart() {
Log.i("wilfried", "onStart")
}
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onMyInfoResume() {
Log.i("wilfried", "onResume")
}
}
-
实现LifecycleEventObserver接口,通过回调方法onStateChanged来实现状态监听(本身也是实现了LifecycleObserver)
public interface LifecycleEventObserver extends LifecycleObserver {
void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event);
}
class MyInfoObserver2 : LifecycleEventObserver {
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
Log.i("wilfried", "LifecycleOwner==${source} Event==${event.name} ")
}
}
最终打印结果如下:
2021-05-27 20:37:48.820 28878-28878/com.example.android.roomwordssample I/wilfried: onCreate
2021-05-27 20:37:48.821 28878-28878/com.example.android.roomwordssample I/wilfried: LifecycleOwner==com.example.android.roomwordssample.MyActivity@c1f76a Event==ON_CREATE
2021-05-27 20:37:48.839 28878-28878/com.example.android.roomwordssample I/wilfried: onStart
2021-05-27 20:37:48.839 28878-28878/com.example.android.roomwordssample I/wilfried: LifecycleOwner==com.example.android.roomwordssample.MyActivity@c1f76a Event==ON_START
2021-05-27 20:37:48.841 28878-28878/com.example.android.roomwordssample I/wilfried: onResume
2021-05-27 20:37:48.841 28878-28878/com.example.android.roomwordssample I/wilfried: LifecycleOwner==com.example.android.roomwordssample.MyActivity@c1f76a Event==ON_RESUME
实现了MVVM架构的Demo
以上是关于Android 基于Jetpack的MVVM架构入门指南的主要内容,如果未能解决你的问题,请参考以下文章
Android Jetpack架构组件——什么是Jetpack?