移动开发最新响应式编程在Android 中的一些探索,字节跳动超高难度三面Android程序员面经

最后

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长。而不成体系的学习效果低效漫长且无助。时间久了,付出巨大的时间成本和努力,没有看到应有的效果,会气馁是再正常不过的。

所以学习一定要找到最适合自己的方式,有一个思路方法,不然不止浪费时间,更可能把未来发展都一起耽误了。

如果你是卡在缺少学习资源的瓶颈上,那么刚刚好我能帮到你。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

所以到这里我们基本上已经知道了生命周期感知这套东西的原理,接下来我们就可以来看看LiveData的实现原理了,下我把LiveData的源码抽象为一张流程图来展示,下面的其他问题都可以在这张图中找到答案

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以看到,在LiveData所依附的Activity/Fragment生命周期发生改变或者通过setValue()改变LiveData数据的时候都会触发notify,但是触发后,真正要走到最终的响应(即我们注册进去的onChanged()回调)则中间要经历很多判断条件,这也是为什么LiveData能具有自己那些特点的原因.

2.LiveData为什么可以避免内存泄漏?

通过上面,我们可以知道,当Activity/Fragment的生命周期发生改变时,LiveData中的监听都会被回调,所以避免内存泄漏就变得十分简单,可以看上图,当LiveData监听到Activity onDestory时则removeObserve,使自己与观察者自动解绑。这样就避免了内存泄漏。 源码上体现如下:

@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}

3.LiveData为什么可以解决View空异常问题?

这个问题很简单,看上图,因为LiveData响应(比如更新界面操作View)只会在界面可见的时候,如果当前见面不可见,则会延迟到界面可见的时候再响应,所以自然就不会有View空异常的问题了。

那么LiveData是如何实现:

  1. 只在界面可见的时候才响应的
  2. 如果当前界面不可见,则会延迟到界面可见的时候再响应

关于问题1,因为LiveData是能感知到生命周期的,所以在它回调响应的时候会加一个额外的条件,就是当前的生命周期必须是可见状态的,才会继续执行响应,源码如下:

private void considerNotify(ObserverWrapper observer) {
//如果界面不可见,则不进行响应
if (!observer.mActive) {
return;
}
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
//如果mVersion不大于mLastVersion,说明数据没有发生变化,则不进行响应
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
//noinspection unchecked
observer.mObserver.onChanged((T) mData);
}

@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}

关于问题2,在LiveData中有一个全局变量mVersion,而每个observer中有一个变量mLastVersion。当我们每次setValue()修改一次LiveData的值的时候,全局的mVersion就会+1,这样mVersion就大于mLastVersion:

@MainThread
protected void setValue(T value) {
assertMainThread(“setValue”);
mVersion++;
mData = value;
dispatchingValue(null);
}

而当界面重新可见的时候,只要判断到mVersion大于mLastVersion,则就会进行响应刷新View,响应后才会更新mLastVersion=mVersion。

4.LiveData为什么是粘性的?

所谓粘性,也就是说消息在订阅之前发布了,订阅之后依然可以接受到这个消息,像EventBus实现粘性的原理是,把发布的粘性事件暂时存在全局的集合里,之后当发生订阅的那一刻,遍历集合,将事件拿出来执行。

而LiveData之所以本身就是粘性的,结合上面的原理图我们来分析一下,比如有一个数据(LiveData)在A页面setValue()之后,则该数据(LiveData)中的全局mVersion+1,也就标志着数据版本改变,然后再从A页面打开B页面,在B页面中开始订阅该LiveData,由于刚订阅的时候内部的数据版本都是从-1开始,此时内部的数据版本就和该LiveData全局的数据版本mVersion不一致,根据上面的原理图,B页面打开的时候生命周期方法一执行,则会进行notify,此时又同时满足页面是从不可见变为可见、数据版本不一致等条件,所以一进B页面,B页面的订阅就会被响应一次。这就是所谓的粘性,A页面在发消息的时候B页面是还没创建还没订阅该数据的,但是一进入B页面一订阅,之前在A中发的消息就会被响应。

那么有些业务场景我们是不想要这种粘性的,我们希望只有当我们订阅了该数据之后,该数据的改变才通知我们,通过上面的分析,这一点应该还是比较好办到的,只要我们订阅的时候将全局的mVersion同步到内部的数据版本,这样订阅时候就不会出现内部数据版本与全局的mVersion不一致,也就去除了粘性。我这里自定义了一个可以控制是否需要粘性的LiveData。

具体代码见: CustomStickyLiveData

三. RxJava

RxJava是可以实现响应式编程的另外一个手段,Rxjava也是热度非常高的一个开源库,当然我们都知道RxJava一个是有订阅发布模式解耦的优点,还有其线程模型、链式写法都是其优点。

当然我个人认为不管是链式写法,还是线程模型,异或是解决回调问题都谈不上是RxJava的核心优点,有很多人引入RxJava后项目里只是利用RxJava方便的线程模型来做简单的异步任务,其实如果只是做异步任务,有非常多种的方式可以替代RxJava。链式写法的话就更只是编码上的糖果了。如果在没有正确的理解RxJava的核心优势基础上在代码里对RxJava进行跟风式的滥用,很多时候你会发现,代码并没有变简洁,甚至有时候很简单的事情被搞的变复杂了。

我所理解的RxJava的核心优势应该是它可以对复杂逻辑进行拆分成为一个一个的Observable后,RxJava的各种操作符予这些解耦的Observable能够合理的进行再组织的能力,并且它给予了你足够丰富的再组织能力。这种分拆再组织的能力是十分强大的,只有运用好RxJava这种强大的能力,才能真正意义上使你原来非常复杂的揉在一团的逻辑代码变得清晰、简洁,本质上是因为RxJava给你提供了这种强大方便的组织能力,我觉得有点像一种编程模式,你可以放心的将复杂的逻辑拆块,最后RxJava给你提供了丰富的组织、变换、串联、控制这些块的能力,只有这个时候你才会真正觉得这是个好东西,而不应该是跟风使用,但是心里也说不清楚为什么要使用。

回到文章的主题响应式,Rxjava就不继续展开了,这篇只说关于文章主题响应式的:

看一下RxJava基本使用的时候一般如下:

Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter e) throws Exception {

e.onNext(“通知观察者”);

}
}).subscribe(new io.reactivex.Observer() {
@Override
public void onSubscribe(Disposable d) {

}

@Override
public void onNext(String s) {
Log.i(“tag”, “接收到消息” + s);
}

@Override
public void onError(Throwable e) {

}

@Override
public void onComplete() {

}
});

可以看到被观察者、观察者,然后通过subscribe()把他们进行绑定。当然可能不看源码的话唯一有一点疑惑的地方是: 这里notify观察者的方式是通过e.onNext(),然后就会触发Observer中的onNext。其实如果notify观察者的方式写成observer.onNext(),就非常明了了。从源码上看e.onNext()里最后调用到的就是observer.onNext(),所以就是普通的订阅发布模式。

到这里基本上可以知道,订阅发布模式是基础,LiveData和RxJava是基于订阅发布模式去实现自己不同的特点,比如LiveData的生命周期感知能力,RxJava的话自身具备的能力就更杂更强大一点。下面来看看响应式的应用,利用这些响应式手段,我们可以来做些什么,主要举两个例子。

3.响应式的应用

一.MVVM

MVC、MVP、MVVM三者并不是说哪种模式一定优于其他模式,三者有自己各自的优缺点,主要看具体的项目和具体场景下哪种更适合你的需求,可以更加高效的提升你的项目代码质量和开发效率。

我下面所阐述的MVVM的优点和缺点,都是基于利用Google lifecycle-aware Components的LiveData+ViewModel来实现MVVM的基础上来说的。当然这些优缺点都是我基于我们项目中应用实践以及个人的一些看法,鞋适不适合只有脚知道,所以还是要结合自己的实际场景。

1.MVVM优点

目前我们产线的项目中占比最大的还是MVP,最开始说了,其实使用MVP在解决代码解耦的基础上,我们写起代码通常是顺序性思维,比较流畅,后期去维护以及代码阅读上也相对流畅,同时在实际开发中它也引起了几个主要的问题:

  1. 内存泄漏。由于Presenter里持有了Activity对象,所以当Presenter中执行了异步耗时操作时,有时候会引起Activity的内存泄漏。

解决的方案: 一个是可以将Presenter对Activity的引用设置为软引用,还有一个就是去管理你的异步耗时任务,当Activity退出时保证他们被取消掉。

  1. View空指针异常。有的时候由于各种原因,Activity已经被回收了,而此时Presenter中要更新View的话经常就会引起view空异常问题。

解决方案: 当然最简单的解决方案就是在Presenter中每次要回调更新界面的时候都判断下View(Activity)是否为空,但是这种方式显然太过烦琐可无法避免疏漏,所以我们可以利用动态代理来实现代理每个更新界面的方法,自动实现在每个更新界面方法之前都判断一下view是否为空。这样之后我们就可以大胆的写代码而不会出现view空异常。

  1. 大量繁琐的回调。不知道当页面足够复杂的时候你是否也体会过Presenter中大量的回调接口,有时候这种回调多了以后,总感觉这种方式来更新界面不是非常优雅。

总结

**其实上面说了这么多,钱是永远赚不完的,在这个知识付费的时代,知识技能提升才是是根本!我作为一名8年的高级工程师,知识技能已经学习的差不多。**在看这篇文章的可能有刚刚入门,刚刚开始工作,或者大佬级人物。

像刚刚开始学Android开发小白想要快速提升自己,最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以这里分享一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

这么重要的事情说三遍啦!点赞+点赞+点赞!

【Android高级架构师系统学习资料】高级架构师进阶必备——设计思想解读开源框架

第一章、热修复设计
第二章、插件化框架设计
第三章、组件化框架设计
第四章、图片加载框架
第五章、网络访问框架设计
第六章、RXJava 响应式编程框架设计
第七章、IOC 架构设计
第八章、Android 架构组件 Jetpack

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

止,不再深入研究,那么很难做到真正的技术提升。**

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值