ViewBinding用法
Posted ximen502_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ViewBinding用法相关的知识,希望对你有一定的参考价值。
视图绑定
通过视图绑定功能,您可以更轻松地编写可与视图交互的代码。在模块中启用视图绑定之后,系统会为该模块中的每个 XML 布局文件生成一个绑定类。绑定类的实例包含对在相应布局中具有 ID 的所有视图的直接引用。
在大多数情况下,视图绑定会替代 findViewById
。
设置说明
注意:视图绑定在 android Studio 3.6 Canary 11 及更高版本中可用。
视图绑定功能可按模块启用。要在某个模块中启用视图绑定,请将 viewBinding 元素添加到其 build.gradle 文件中,如下例所示:
android
...
viewBinding
enabled = true
然而,在后续的as版本中这个引用又变了,不然build可能报错,需要如下引用才行:
android
buildFeatures
viewBinding true
如果您希望在生成绑定类时忽略某个布局文件,请将 tools:viewBindingIgnore=“true” 属性添加到相应布局文件的根视图中:
<LinearLayout
...
tools:viewBindingIgnore="true" >
...
</LinearLayout>
用法
为某个模块启用视图绑定功能后,系统会为该模块中包含的每个 XML 布局文件生成一个绑定类。每个绑定类均包含对根视图以及具有 ID 的所有视图的引用。系统会通过以下方式生成绑定类的名称:将 XML 文件的名称转换为驼峰式大小写,并在末尾添加“Binding”一词。
例如,假设某个布局文件的名称为 result_profile.xml
:
<LinearLayout ... >
<TextView android:id="@+id/name" />
<ImageView android:cropToPadding="true" />
<Button android:id="@+id/button"
android:background="@drawable/rounded_button" />
</LinearLayout>
所生成的绑定类的名称就为 ResultProfileBinding
。此类具有两个字段:一个是名为 name
的 TextView
,另一个是名为 button
的 Button
。该布局中的 ImageView
没有 ID
,因此绑定类中不存在对它的引用。
每个绑定类还包含一个 getRoot()
方法,用于为相应布局文件的根视图提供直接引用。在此示例中,ResultProfileBinding
类中的 getRoot()
方法会返回 LinearLayout
根视图。
以下几个部分介绍了生成的绑定类在 Activity 和 Fragment 中的使用。
在 Activity 中使用视图绑定
如需设置绑定类的实例以供 Activity 使用,请在 Activity 的 onCreate() 方法中执行以下步骤:
- 调用生成的绑定类中包含的静态 inflate() 方法。此操作会创建该绑定类的实例以供 Activity 使用。
- 通过调用 getRoot() 方法或使用 Kotlin 属性语法获取对根视图的引用。
- 将根视图传递到 setContentView(),使其成为屏幕上的活动视图。
private lateinit var binding: ResultProfileBinding
override fun onCreate(savedInstanceState: Bundle)
super.onCreate(savedInstanceState)
binding = ResultProfileBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
您现在即可使用该绑定类的实例来引用任何视图:
binding.name.text = viewModel.name
binding.button.setOnClickListener viewModel.userClicked()
在 Fragment 中使用视图绑定
如需设置绑定类的实例以供 Fragment 使用,请在 Fragment 的 onCreateView() 方法中执行以下步骤:
- 调用生成的绑定类中包含的静态 inflate() 方法。此操作会创建该绑定类的实例以供 Fragment 使用。
- 通过调用 getRoot() 方法或使用 Kotlin 属性语法获取对根视图的引用。
- 从 onCreateView() 方法返回根视图,使其成为屏幕上的活动视图。
注意:inflate() 方法会要求您传入布局膨胀器。如果布局已膨胀,您可以调用绑定类的静态 bind() 方法。如需了解详情,请查看视图绑定 GitHub 示例中的例子。
private var _binding: ResultProfileBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View?
_binding = ResultProfileBinding.inflate(inflater, container, false)
val view = binding.root
return view
override fun onDestroyView()
super.onDestroyView()
_binding = null
您现在即可使用该绑定类的实例来引用任何视图:
binding.name.text = viewModel.name
binding.button.setOnClickListener viewModel.userClicked()
注意:Fragment 的存在时间比其视图长。请务必在 Fragment 的 onDestroyView() 方法中清除对绑定类实例的所有引用。
GitHub示例如下代码所示:
class BindFragment : Fragment(R.layout.fragment_blank)
// Scoped to the lifecycle of the fragment's view (between onCreateView and onDestroyView)
private var fragmentBlankBinding: FragmentBlankBinding? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
super.onViewCreated(view, savedInstanceState)
val binding = FragmentBlankBinding.bind(view)
fragmentBlankBinding = binding
binding.textViewFragment.text = getString(string.hello_from_vb_bindfragment)
override fun onDestroyView()
// Consider not storing the binding instance in a field, if not needed.
fragmentBlankBinding = null
super.onDestroyView()
与 findViewById 的区别
与使用 findViewById 相比,视图绑定具有一些很显著的优点:
- Null 安全:由于视图绑定会创建对视图的直接引用,因此不存在因视图 ID 无效而引发 Null 指针异常的风险。此外,如果视图仅出现在布局的某些配置中,则绑定类中包含其引用的字段会使用 @Nullable 标记。
- 类型安全:每个绑定类中的字段均具有与它们在 XML 文件中引用的视图相匹配的类型。这意味着不存在发生类转换异常的风险。
这些差异意味着布局和代码之间的不兼容将会导致构建在编译时(而非运行时)失败。
与数据绑定的对比
视图绑定和数据绑定均会生成可用于直接引用视图的绑定类。但是,视图绑定旨在处理更简单的用例,与数据绑定相比,具有以下优势:
- 更快的编译速度:视图绑定不需要处理注释,因此编译时间更短。
- 易于使用:视图绑定不需要特别标记的 XML 布局文件,因此在应用中采用速度更快。在模块中启用视图绑定后,它会自动应用于该模块的所有布局。
反过来,与数据绑定相比,视图绑定也具有以下限制:
- 视图绑定不支持布局变量或布局表达式,因此不能用于直接在 XML 布局文件中声明动态界面内容。
- 视图绑定不支持双向数据绑定。
考虑到这些因素,在某些情况下,最好在项目中同时使用视图绑定和数据绑定。您可以在需要高级功能的布局中使用数据绑定,而在不需要高级功能的布局中使用视图绑定。
内容来源:
https://developer.android.google.cn/topic/libraries/view-binding?hl=zh-cn
Android 基于Jetpack的MVVM架构入门指南
Android 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);
);
Transformations
//被监听数据
private val count = MutableLiveData<Int>(1)
private val mapCount: LiveData<Int> = Transformations.map(count)
it + 1
//与map()的区别是必须返回一个 MutableLiveData 对象
private val switchMapCount: LiveData<Int> = Transformations.switchMap(count)
MutableLiveData<Int>(it!! + 1)
详细介绍请参见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 对象的情况下注册一个观察者。在这种情况下,观察者会被视为始终处于活跃状态,可以通过调用
removeObserver(Observer)
方法来移除这些观察者。
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()//已实现默认参数
双向绑定
单向绑定:字段变化-->View自动更新
双向绑定:字段变化-->view自动更新、用户操作view变化-->字段同步变化(EditText)
为什么要使用双向绑定?
因为我们使用DataBinding一般数据源是ViewModel, 我们可以根据ViewModel的LiveData来赋值给EditText,但是我们从界面操作了EditText无法更新对应的LiveData,也就是EditText的数据已经由用户操作更新了,可是此时ViewModel里的LiveData数据还是原先的数据,如果此时我们要获取数据,那到LiveDate的值就是旧值,所以我们需要双向绑定,当界面更新ViewModel的数据同步更新
如何使用双向绑定?
1、自定义view必须要定义了对关键值的监听,比如CheckBox的checked属性,Android有定义接口来回调这个值的状态,这个时候就可以将navigationViewModel中定义的实现了OnCheckedChangeListener的接口作为参数传入到onCheckedChanged,所以当View有状态的变化就会通过ViewModel注册的监听将状态同步到LiveData,完成View和LiveData状态同步了
android:onCheckedChanged="@navigationViewModel.rememberMeChanged"
2、当然因为是系统控件我们直接使用 android:checked="@=navigationViewModel.booleanLiveData"也就搞定了 双向绑定的形式也就是上面的“@= ”,他可以接收属性的数据更改并同时监听用户更新
3、也可以使用可观察的数据对象,将布局变量设置为 Observable(通常为 BaseObservable)的实现,并使用 @Bindable 注释
4、注册监听,当addTextChangedListener发生变化,会通知属性调用InverseBindingAdapter注解方法
<EditText
android:id="@+id/ed_info2"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginTop="20dp"
app:bindingName='@=bannerViewModel.userDefined'
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="@+id/ed_info"
tools:ignore="Autofill,LabelFor,TextFields" />
@BindingAdapter("bindingNameAttrChanged")
@JvmStatic
fun setBindingListener(edit: EditText, listener: InverseBindingListener?)
Log.i(TAG, "setBindingListener..")
var txt = ""
edit.addTextChangedListener(object : TextWatcher
override fun afterTextChanged(p0: Editable?)
if (txt != p0.toString())
// 会通知属性调用InverseBindingAdapter注解方法
listener?.onChange()
txt = p0.toString()
edit.setSelection(txt.length)
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int)
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int)
)
InverseBindingAdapter注解,提供view的最新值,赋值给数据层
@InverseBindingAdapter(attribute = "bindingName")
@JvmStatic
fun getBindingName(edt: EditText): String
Log.i(TAG, "getBindingName..$edt.text")
return edt.text.toString()
数据层发生变化,触发单向绑定,改变view的值
@BindingAdapter("bindingName")
@JvmStatic
fun setBindingName(edt: EditText, txt: String)
Log.i(TAG, "setBindingName..$txt")
edt.setText(txt)
注意:在afterTextChanged的时候判断了txt != p0.toString(),如果不判断会无限循环,所以只有值发生改变的时候才去触发
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
生命周期感知型组件的最佳做法
-
使界面控制器(Activity 和 Fragment)尽可能保持精简。它们不应试图获取自己的数据,而应使用
ViewModel
执行此操作,并观察LiveData
对象以将更改体现到视图中。 -
设法编写数据驱动型界面,对于此类界面,界面控制器的责任是随着数据更改而更新视图,或者将用户操作通知给
ViewModel
。 -
将数据逻辑放在
ViewModel
类中。ViewModel
应充当界面控制器与应用其余部分之间的连接器。不过要注意,ViewModel
不负责获取数据(例如,从网络获取)。但是,ViewModel
应调用相应的组件来获取数据,然后将结果提供给界面控制器。 -
使用数据绑定在视图与界面控制器之间维持干净的接口。这样一来,您可以使视图更具声明性,并尽量减少需要在 Activity 和 Fragment 中编写的更新代码。如果您更愿意使用 Java 编程语言执行此操作,请使用诸如 Butter Knife 之类的库,以避免样板代码并实现更好的抽象化。
-
如果界面很复杂,不妨考虑创建 presenter 类来处理界面的修改。这可能是一项艰巨的任务,但这样做可使界面组件更易于测试。
-
避免在
ViewModel
中引用View
或Activity
上下文。如果ViewModel
存在的时间比 Activity 更长(在配置更改的情况下),Activity 将泄漏并且不会获得垃圾回收器的妥善处置。 -
使用 Kotlin 协程管理长时间运行的任务和其他可以异步运行的操作。
实现了MVVM架构的Demo
以上是关于ViewBinding用法的主要内容,如果未能解决你的问题,请参考以下文章
Android开发:关于Databinding与Viewbinding以及kotlin-android-extensions
Android开发:关于Databinding与Viewbinding以及kotlin-android-extensions
Android开发:关于Databinding与Viewbinding以及kotlin-android-extensions
Android开发:关于Databinding与Viewbinding以及kotlin-android-extensions