-
LiveData.observeForever()与observe()的区别
-
- observe
-
observeForever
-
ViewModel+LiveData实现Fragment间通信
-
- 原理
-
具体实现
-
结果
-
LiveData原理详解
-
其他杂谈
-
- postValue使用相关注意点
-
简单总结
=====================================================================
LiveData是一个可被观察的数据容器类。可以将LiveData理解为一个数据的容器,它将数据包装起来,使数据成为被观察者,当该数据发生变化时,观察者能够获得通知。
本篇文章内容结合使用了ViewModel,如果不了解ViewModel可移步查看Jetpack:ViewModel使用指南,实现原理详细解析!
ViewModel用于存放页面所需要的各种数据,不仅如此,我们还可以在其中放一些与数据相关的业务逻辑。我们可以在ViewModel中进行数据的加工、获取等操作。因此,ViewModel的数据可能会随着业务的变化而变化。
但是对于页面来说,它并不需要关心ViewModel中的业务变化,只关心数据在变化之后可以获取到通知并进行更新。LiveData就可以做到,在ViewModel中的数据发生变化时通知页面。所以,LiveData通常被放在ViewModel中用来包装那些需要被界面观察的数据。
buildscript {
//android lifecycle version
ext.lifecycle_version = “2.3.0”
}
// ViewModel
implementation “androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version”
// LiveData
implementation “androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version”
================================================================================
写一个计时器,每隔一秒钟是同LiveData通知主界面刷新。
1.LiveData是一个抽象类,不能直接使用。通常我们使用的是它的直接子类MutableLiveData。使用MutableLiveData将要发送的num包装起来,异步线程的话使用postValue设置要发送的值。ViewModel的代码如下所示:
class TimingWithLiveDataViewModel : ViewModel() {
/**
- 当前的计数 初始值为0
*/
val currentNum: MutableLiveData = MutableLiveData(0)
/**
- job
*/
var job: Job? = null
/**
- 开始计数
*/
fun startTiming() {
if (job == null) {
job = viewModelScope.launch(Dispatchers.Default) {
while (true) {
delay(1000)
var value = currentNum.value ?: 0
currentNum.postValue(++value)
}
}
}
}
override fun onCleared() {
super.onCleared()
job?.cancel()
job = null
}
}
2.在activity中,使用observe,设置监听回调。完成页面与ViewModel之间的通信。
class TimingWithLiveDataActivity : AppCompatActivity() {
lateinit var binding: ActivityCommonBinding
lateinit var timingWithLiveDataViewModel: TimingWithLiveDataViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initView()
initViewModel()
initData()
}
@SuppressLint(“SetTextI18n”)
private fun initView() {
binding = ActivityCommonBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btDoAny.visibility = View.VISIBLE
binding.btDoAny.text = “reset”
}
private fun initViewModel() {
timingWithLiveDataViewModel =
ViewModelProvider(this)[TimingWithLiveDataViewModel::class.java]
}
@SuppressLint(“SetTextI18n”)
private fun initData() {
//开始倒计时
timingWithLiveDataViewModel.startTiming()
timingWithLiveDataViewModel.currentNum.observe(this, { currentNum ->
//观察到数据变化 更新UI
binding.tvContent.text = “currentNum:$currentNum”
})
//通过LiveData重置数据
binding.btDoAny.setOnClickListener {
timingWithLiveDataViewModel.currentNum.value = 0
}
}
}
通过LiveData.observe()方法对LiveData所包装的数据进行观察。当我们希望修改LiveData所包装的数据时,则可以通过LiveData.postValue()/LiveData.setValue()方法来完成。postValue()方法用在非UI线程中,若在UI线程中,则使用setValue()方法。注意:setValue()和observe()不可以使用在非UI线程之中,否则会抛异常,稍后的源码分析会讲到,先贴上抛异常的代码。
static void assertMainThread(String methodName) {
if (!ArchTaskExecutor.getInstance().isMainThread()) {
throw new IllegalStateException(“Cannot invoke " + methodName + " on a background thread”);
}
}
LiveData.observeForever()与observe()的区别
=========================================================================================================
LiveData还有一个名为observeForever()的方法,使用起来与observe()没有太大差别。它们的区别主要生命周期的监听。
会在lifecycle大于等于Started的时候才为激活状态(可以观察到数据变化给回调),在lifecycle为Destroy的时候会自动调用removeObserver()移除观察者。可以有效的防止内存泄漏,程序异常。
无论页面处于什么状态,observeForever()都能收到通知。因此,在用完之后,一定要记得调用removeObserver()方法来移除观察者,否则会造成了内存泄漏。
ViewModel+LiveData实现Fragment间通信
==================================================================================================
ViewModel时储存在ViewModelStore里面的,而ViewModelStore是通过getViewModelStore获取的,我们只需要传入activity的ViewModelStoreOwner接口,那么多个fragment之间就获取的是同一个ViewModelStore了,那这样就能获取到同一个ViewModel了,然后利用LiveData观察者的特性即可实现数据之间的共享了。
1.定义ShareDataViewModel利用LiveData包装progress字段。
class ShareDataViewModel : ViewModel() {
val progress: MutableLiveData by lazy(LazyThreadSafetyMode.NONE) {
MutableLiveData(0)
}
}
2.定义相关布局文件,将两个fragment平放在一个布局里面。
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
android:orientation=“vertical”
android:layout_width=“match_parent”
android:layout_height=“match_parent”>
<FrameLayout
android:id=“@+id/fl_id_1”
android:layout_width=“match_parent”
android:layout_height=“0dp”
android:layout_weight=“1”/>
<View
android:layout_width=“match_parent”
android:layout_height=“1dp”
android:background=“@color/design_default_color_primary”/>
<FrameLayout
android:id=“@+id/fl_id_2”
android:layout_width=“match_parent”
android:layout_height=“0dp”
android:layout_weight=“1”/>
定义fragment布局文件
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android”
android:layout_width=“match_parent”
android:layout_height=“match_parent”>
<TextView
android:id=“@+id/tv_content”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_centerInParent=“true”
android:layout_above=“@id/seekbar”
android:text=“@string/app_name”/>
<SeekBar
android:id=“@+id/seekbar”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:layout_marginTop=“20dp”
android:max=“100”
android:layout_centerInParent=“true”/>
3.编写Fragment代码,实现具体的通信,注意ViewModelProvider传入activity。
class SeekBarFragment
@JvmOverloads
constructor(private val text: String = “”) : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return FragmentSeekbarBinding.inflate(layoutInflater).let {
fragmentSeekBarBinding ->
fragmentSeekBarBinding.tvContent.text = this.text
//注意这里使用activity,因为如果想共享数据需要使用同一个ViewModelStore。所以都是用activity的。
val liveDataProgress = ViewModelProvider(activity!!)[ShareDataViewModel::class.java].progress
//通过observe观察ViewModel中字段数据的变化,并在变化时得到通知
liveDataProgress.observe(this){
progress->
fragmentSeekBarBinding.seekbar.progress = progress
}
fragmentSeekBarBinding.seekbar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(
seekBar: SeekBar?,
progress: Int,
fromUser: Boolean
) {
//当用户操作SeekBar时,更新ViewModel中的数据
liveDataProgress.value = progress
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
}
})
fragmentSeekBarBinding.root
}
}
}
5.编写activity代码,将fragment实例化,并放入activity中。
class ShareFragmentActivity : AppCompatActivity(){
lateinit var binding: ActivityFragmentContentBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initView()
}
private fun initView(){
binding = ActivityFragmentContentBinding.inflate(layoutInflater)
setContentView(binding.root)
supportFragmentManager.beginTransaction().add(binding.flId1.id,SeekBarFragment(“One-Fragment”)).commit()
supportFragmentManager.beginTransaction().add(binding.flId2.id,SeekBarFragment(“Two-Fragment”)).commit()
}
}
无论滑动上面的SeekBar还是下面的SeekBar,另一个Fragment的SeekBar一定会跟着滑动。在滑动SeekBar时,通过LiveData.setValue()方法,修改了ViewModel中LiveData包装的数据(progress字段)。由于Fragment通过LiveData.observe()方法监听了数据的变化,因此progress字段被修改后,Fragment能够第一时间收到通知并更新UI。
===============================================================================
LiveData.observe的源码:
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread(“observe”);
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException(“Cannot add the same observer”
- " with different lifecycles");
}
if (existing != null) {
return;
}
owner.getLifecycle().addObserver(wrapper);
}
上面说到过observe只能在主线程使用,observe里面第一步就用assertMainThread进行了主线程判断,如果当前运行的不是主线程的话,直接就抛异常了(assertMainThread内部实现在上面贴过了)。同理,LiveData的observeForever、removeObserver、removeObservers与setValue,也是一样的道理,在方法的第一步就用assertMainThread进行了判断,只能在主线程使用,否则就抛异常。
接下来看和生命周期相关的内容,如果当前生命周期是DESTROYED的话就直接return。LifecycleOwner传入到了LifecycleBoundObserver里面进行了包装,将包装后的对象和observer传入了mObservers(一个key value集合)进行保存,最后注册生命周期。所以看看LifecycleBoundObserver做了什么操作。
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
@Override
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://img-blog.csdnimg.cn/img_convert/0299261b82e827e3156792798d1b01d6.jpeg)
如何做好面试突击,规划学习方向?
面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。
学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。
我搜集整理过这几年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。
在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
成一个系统化的知识体系。
学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。
我搜集整理过这几年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。
在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!