Android 基于Jetpack的MVVM架构入门指南

Posted xiaoqiang_0719

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 基于Jetpack的MVVM架构入门指南相关的知识,希望对你有一定的参考价值。

android 基于Jetpack的MVVM架构入门指南

目录

Android MVVM架构入门指南

架构组件简介

View Binding

简介

View Binding 的优点

用法

ViewModel

简介

ViewModel 的生命周期

用法

LiveData

简介

LiveData 的优势

LiveData的子类

用法

Data Binding

简介

用法

Lifecycles

简介

主要类

用法

实现了MVVM架构的Demo


架构组件简介

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

  1. 调用生成的绑定类中包含的静态 inflate() 方法。此操作会创建该绑定类的实例以供 Activity 使用

  2. 通过调用 getRoot() 方法获取对根视图的引用

  3. 调用 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 的优势

  1. 观察者会绑定到 Lifecycle 对象(所有实现了所有实现LifecycleOwner的生命周期对象),并在其关联的生命周期遭到销毁后进行自我清理。

  2. 如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。

  3. 如果由于配置更改(如设备旋转)而重新创建了 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

用法

  1. 创建 LiveData 的实例以存储某种类型的数据。这通常在 ViewModel 类中完成。

class MyViewModel : ViewModel() {
​
    // Create a LiveData with a String
    val livedata: MutableLiveData<String> =  MutableLiveData<String>()
    
    }
}
​
  1. 在界面控制器(如 Activity 或 Fragment)中创建 Observer 对象。

  2. 使用 observe() 方法将 Observer 对象附加到 LiveData 对象。

        myViewModel.livedata.observe(this, Observer {
​
        })
  1. 可以使用 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

用法

  1. 使用 Data Binding ,首先需要在 app moudle 下的 build.gradle 中添加:

android {
    ...
    dataBinding {
        enabled = true
    }
    ...
}
  1. 创建一个实体类,例: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>
  1. 此时编译器会自动根据这个布局生成相应的绑定类,这里会生成一个 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

两种方式可以实现生命周期的观察

  1. 实现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")
    }
}
  1. 实现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

Demo下载地址(更新中)

以上是关于Android 基于Jetpack的MVVM架构入门指南的主要内容,如果未能解决你的问题,请参考以下文章

Android 基于Jetpack的MVVM架构入门指南

Android Jetpack MVVM封装及使用

Android Jetpack系列之MVI架构

Android Jetpack架构组件——什么是Jetpack?

Android Jetpack架构组件——什么是Jetpack?

Android Jetpack之MVVM使用及封装