学习资料-源码部分

源码问题解答步骤

1实现原理(关键类和方法名要记得)
2用到了那些数据结构和设计模式(或者有弱引用什么的,也要记得)
3优缺点(设计方法有那些值得借鉴的地方,参考后拿来实现其他功能,还有那些改进优化的地方以及缺点)

DataBinding源码解析

资料来源:

https://www.jianshu.com/p/5496a2e62842
下面这两个视频都是一个老师不同时期讲的,内容差不多,看其中一个就好
https://www.bilibili.com/video/BV1iX4y1F7Nw?p=1
https://www.bilibili.com/video/BV1164y1i7a6

1、实现原理

DataBinding使用了apt技术,在我们build(构建)项目时DataBinding会生成多个文件,
我们可以在build文件中的相关路径查看
build/generated/source/kapt/debug+包名

DataBinding将原有的activity_main.xml文件进行了解析和拆分,
分别拆成activity_mian.xml和activity_main-layout.xml。
路径为:
app/build/intermediates/incremental/mergeDebugResources/stripped.dir/layout/activity_main.xml
app/build/intermediates/data_binding_layout_info_type_merge/debug/out/activity_main-layout.xml

DataBinding将原有的layout和data标签去除了。
并为根布局声明了一个layout/文件名_0的tag,
为其他使用到@{}或@={}的控件按顺序添加了一个binding_X的tag

然后在另外一个activity_main-layout.xml配置文件中
用不同的标签进行属性的声明
代表数据变量
代表绑定了数据的View
里面的标签代表是否是双向绑定

然后在setContentView方法里面
内部通过bind方法,
调用DataBinderMapperImpl这个实现类的getDataBinder方法
根据传入的layoutId和xml里面添加的tag,来返回不同的ActivityMainBindingImpl实现类
这个实现类的构造方法
创建了一个数组
长度为带有tag标签的View数量
然后把这些View放入数组里
最后调用invalidateAll方法
回到主线程调用mRebindRunnable
里面最终执行了executeBindings方法
这个方法就是执行将传进来的变量赋值到View上的最后功能

最后是数据绑定的流程
拿一个简单的SetViewmodel来说
进入到ActivityMainBindingImpl实现类后
核心方法就是updateRegistration和notifyPropertyChanged
第二个其实就是状态更新后刷新View的
内部最终调用的还是mRebindRunnable的executeBindings方法

关键在于注册的这个updateRegistration方法
他将这个属性变量的id(id在BR文件中生成)和属性变量传进去
内部定义了一个弱引用WeakListener的数组集合
id就作为这个集合的标记
去搜索对应的WeakListener
搜不到则注册
WeakListener通过setTarget
把被观察者(也就是属性变量)存为自己的Target变量
同时内部调用WeakPropertyListener.addListener
对自己的Target变量添加状态变化的监听
在这里插入图片描述

**notifyPropertyChanged(BR.xxx)**方法
是BaseObservable类的方法
BaseObservable实现了Observable接口
属性变量和ActivityMainBindingImpl实现类都继承了BaseObservable这个类
说明他们都是可以被观察者的

notifyPropertyChanged(BR.xxx)方法
里面调用的是notifyCallbacks方法
执行了WeakPropertyListener的onPropertyChanged方法
告诉变量的状态改变了
onPropertyChanged方法内部调用的是实现类ActivityMainBindingImp的onFieldChange方法
里面根据传入的BR的id,也就是属性变量的id
做一个或运算得到对应的标志位
如果找到了这个标志位
就调用requestRebind方法
回到主线程调用mRebindRunnable
最终执行了实现类ActivityMainBindingImpl的executeBindings方法
这个方法用刚刚得到的标志位dirtyFlags去做与运算
找到属性变化了的的属性变量
并最终赋值给view

如果有双向绑定的view,比如edittext控件
里面最终也是在executeBindings方法里对edittext添加了一个setTextWatcher变化的监听
对edittext的内容变化进行监听
在监听到变化后
最终在onchange方法里调用viewmodel的setXXX方法
去对viewmodel的属性变量进行进行设值
最后因为setXXX方法里面肯定执行了notifyPropertyChanged(BR.xxx)方法
这样就可以继续做一个数据刷新了

2、用到了那些数据结构和设计模式(或者有弱引用什么的,也要记得)

观察者模式,多对多关系(属性变量:不同的activity)用数组结构处理,
WeakListener使用弱引用,确保view被回收后监听的引用也被回收

3、优缺点(设计方法有那些值得借鉴的地方,参考后拿来实现其他功能,还有那些改进优化的地方以及缺点)

优点:帮我们实现了 View 与 ViewModel 之间的交互, 让使用者更加专注于业务代码的开发
缺点:比较消耗内存(View都存在数组里面,还有生成的弱引用数组等等),其次编译时生成类在一定程度上降低了编译速度

lifecycle原理

第一步:添加观察者

activity父类ComponentActivity实现了LifecycleOwner接口
这个接口有一个待实现的getLifecycle方法
ComponentActivity实现这个方法返回了一个LifecycleRegistry类
并且让LifecycleRegistry持有了自身的弱引用对象
这样activity里就获得了一个LifecycleRegistry的实例
通过这个实例的addObserver方法可以用来添加不同观察者LifecycleObserver
addObserver方法对添加进来的观察者对象做了一个包装
放入包装类ObserverWithState中
ObserverWithState构造方法创建了一个LifecycleEventObserver类的实现类ReflectiveGenericLifecycleObserver
这个实现类的构造方法里面
通过反射拿到观察者的所有方法
并且将带有生命周期注解的方法全部存入一个mInfo对象中
然后返回给了ObserverWithState
然后回到addObserver方法
将包装类ObserverWithState放入到一个map集合mObserverMap中
key为观察者LifecycleObserver对象,value为ObserverWithState对象

第二步:事件通知

当activity生命周期发生变化,如何通知观察者呢?
答案在ComponentActivity的oncreate方法里
初始化的方法添加进了一个没有UI的ReportFragment
ReportFragment的生命周期方法里面
都会执行一个dispatch方法用来分发事件

第三步:dispatch原理

dispatch方法内部,
调用自身activity持有的LifecycleRegistry类
然后调用LifecycleRegistry的handleLifecycleEvent方法
LifecycleRegistry里面使用了状态模式来维护activity的不同生命周期所对应的状态
比如oncrete和onstop对应的状态就是created
handleLifecycleEvent方法根据传入的生命周期
调用getStateAfter方法,根据事件Event找状态State
然后把状态传入moveToState方法
moveToState方法用mState保存了状态,然后执行了sync方法
sync里面根据保存的mState确定当前页面是前进还是后退
前进则执行forwardPass方法
后退则执行backwardPass方法
这两个方法内部调用upEvent或者downEvent,根据状态State找到事件Event
(比如状态为STARTED,前进则是ON_RESUME事件
后退则是ON_STOP事件)
在这里插入图片描述

forwardPass和backwardPass这两个方法都是遍历mObserverMap集合
找到集合里状态符合当前状态mState前一步或者后一步的value,
也就是找到集合里有对应的注册事件的value
也就是ObserverWithState包装类,
这一步就相当于根据事件找到了观察者
ObserverWithState类调用dispatchEvent方法
里面调用自己的LifecycleEventObserver的实现类ReflectiveGenericLifecycleObserver的onStateChanged方法
ReflectiveGenericLifecycleObserver就是存入了观察者所有带注解方法的一个对象
它的变量mInfo就是一个CallbackInfo类
里面把观察者里面的事件和对应的方法做了一个Map映射
ReflectiveGenericLifecycleObserver的onStateChanged方法
就是根据传入的事件,对mInfo里面的Map进行遍历
遍历后找到事件所对应的生命周期注解方法,
这一步就相当于根据事件找到了观察者里面的生命周期方法
然后调用观察者里面的生命周期注解方法即可
所以Lifecycle有两个集合:
1:mObserverMap,key为观察者LifecycleObserver对象,value为ObserverWithState对象
2:ObserverWithState包装类里面的mInfo,把观察者里面的生命周期事件和对应的方法做了一个Map映射

livedata原理

第一点:绑定关系

observe方法
内部用传入的activity调用其中的getLifecycle().addObserver方法
把传入的observer对象进行包装,然后将包装类作为观察者放入addObserver方法的参数内
这部分原理其实就是lifecycle的原理

接下来说说这个包装类LifecycleBoundObserver
他继承了ObserverWrapper类
于是有了一个mLastVersion的版本变量属性,初始值为-1
他实现了LifecycleObserver接口,也就是变成了一个观察者

这一步完成后activity就作为了被观察者,addObserver里传入的observer就是观察者
实现了二者的绑定

然后会把这些存入到liveData的一个mObservers的Map集合里
add进的observer作为key,包装类的实例对象wrapper作为value

第二点:seValue方法

postValue和seValue用来修改livedata的数据
postValue内部也是切换到主线程调用seValue来修改数据

liveData里面有一个用volatile修饰的Object对象mData
在seValue方法里,就用这个mData保存set进来的value值
再对livaData的mVersion变量进行自增操作,mVersion初始值为-1
完成后调用dispatchingValue(null)方法
dispatchingValue入参如果为空
就开始遍历livaData的集合mObservers
调用considerNotify方法
方法里面判断activity是否为started状态(也就是lifecycle的生命周期监听原理)
确保页面是显示的,才继续执行下去
同时再判断一下LifecycleBoundObserver包装类的版本是否大于等于livaData的版本
mLastVersion >= mVersion
如果是,就返回
否则才继续执行下去
先让LifecycleBoundObserver包装类的版本等于livaData的版本
observer.mLastVersion = mVersion
最后调用observer.mObserver.onChanged方法
onChanged方法也就是用户真正写东西的那个方法
这样就真正走完了整个流程

这个mLastVersion和mVersion其实就是起到一个版本控制的功能

第三点:生命周期监听导致的粘性事件

seValue方法是用户去主动设置变量值得时候,最终才会让livaData得到通知
去调用observer.mObserver.onChanged方法

那么刚刚进入页面,数据是如何刷新的呢?
其实关键点隐藏在getLifecycle().addObserver方法方法里面
activity里面的ReportFragment在执行不同的生命周期的时候
会分发事件下去
而我们在livaData的observe方法里面
创建的包装类LifecycleBoundObserver
恰恰就实现了LifecycleObserver接口
并且被addObserver方法add进去了
也就是成为activity的观察者了
这样一来
activity的页面生命周期发生变化
包装类LifecycleBoundObserver也会得到感知
并且最终调用自己的onStateChanged方法
执行activeStateChanged方法
这个方法里最终执行的是dispatchingValue(this)方法
然后走considerNotify方法
接下来的步骤就和之前setValue一样了
区别不过是setValue需要遍历LivaData的Map
对每个处于started状态的观察者进行onchange
而这个则是直接传入作为观察者的自身
最终去调用自身的onchange方法就好了

这样一来我们就可以看到
在A页面做了数据的修改
来到新的B页面,也会看到修改后的数据
相当于一个粘性事件了
当然,也有人叫这个为数据倒灌。

第四点 hook源码解决粘性问题

既然有了这个粘性事件,就有一个问题
如果B页面刚进入,并不想接受之前A页面的粘性事件
也就是不想执行onchange方法
这时候怎么处理呢?
其实很简单
利用反射
在观察者被加入的时候
也就是重写liveData的observe方法

对于刚刚加入的观察者
把他的observer.mLastVersion属性的值
改为livaData的mVersion属性的值
这样刚进入去执行considerNotify的时候,
在判断版本的时候,就会发现版本一样
就不会继续执行下面的onchange方法了

第五点:dispatchingValue容错机制

假设A页面做了数据修改,将某个变量的值改为1
B先收到,C后收到
但是B收到后,又将值改为2
C会调用两遍onchange嘛?
答案是不会,而是会调用最后的一遍得到值为2的结果
这是因为dispatchingValue方法里面做了容错机制
这个方法里面有一个mDispatchInvalidated和mDispatchingValue两个boolean值
在遍历观察者Map的时候
如果mDispatchInvalidated为true,就跳出遍历的循环
mDispatchInvalidated为true的条件
是B页面收到后,递归调用了setValue方法进行变量的设值

setValue方法走到dispatchingValue方法
将mDispatchInvalidated改为true然后返回
这时候mDispatchInvalidated为true,就跳出遍历的循环
走到while语句mDispatchInvalidated的判断
因为mDispatchInvalidated为true
所以重新遍历Map
调用最后的一遍得到值为2的onchange方法

总结就是B页面调用setValue后,导致遍历循环被跳出
然后重新执行do while循环
全部走完,中间没有被再次setValue截断后
mDispatchingValue和mDispatchInvalidated最后又恢复到false
个人总结:
mDispatchingValue:记录修改状态
在修改时为true,修改完了为false
mDispatchInvalidated:记录是否有重复修改
刚进来的时候是false
二次修改的时候为true
二次修改完了为false

第六点:企业级livedataBus封装

其实就是利用第四点,做了一个封装

用一个boolean值控制是否发送粘性事件
否的话就hook源码
不然就和之前一样
代码如下(NonStickyMutableLiveData就是使用了反射取消粘性的自定义LiveData)

public class LiveDataBus {
    // 存放订阅者
    private Map<String, MutableLiveData<Object>> bus;
    private static LiveDataBus LiveDataBus = new LiveDataBus();

    private LiveDataBus() {
        bus = new HashMap<>();
    }

    public static LiveDataBus getInstance() {
        return LiveDataBus;
    }

    // 注册订阅者,(存入map)sticky 为false代表取消粘性
    public synchronized <T> MutableLiveData<T> with(String key, Class<T> type, boolean sticky) {
        if (!bus.containsKey(key)) {
            if (sticky) {
                bus.put(key, new MutableLiveData<Object>());
            } else {
                bus.put(key, new NonStickyMutableLiveData<Object>());
            }
        }
        return (MutableLiveData<T>) bus.get(key);

    }
}

NonStickyMutableLiveData源码如下:

public class NonStickyMutableLiveData<T> extends MutableLiveData {
    private boolean stickFlag = false;//非粘性

    @Override
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) {
        super.observe(owner, observer);
        if (!stickFlag) {
            hook(observer);
        }
    }

    //在这里去改变onChange的流程
    private void hook(Observer<? super T> observer) {
        try {
            //得到mLastVersion
            //获取到LiveData类中的mObservers对象
            Class<LiveData> liveDataClass = LiveData.class;
            Field mObserversField = liveDataClass.getDeclaredField("mObservers");
            mObserversField.setAccessible(true);
            //获取到这个成员变量的对象
            Object mObserversObject = mObserversField.get(this);
            //得到Map对应的class对象
            Class<?> mObserversClass = mObserversObject.getClass();
            //获取到mObservers对象的get方法
            Method get = mObserversClass.getDeclaredMethod("get", Object.class);
            get.setAccessible(true);
            //执行get方法
            Object invokeEntry = get.invoke(mObserversObject, observer);
            //定义一个空的对象
            Object observerWraper = null;

            if (invokeEntry != null && invokeEntry instanceof Map.Entry) {
                observerWraper = ((Map.Entry) invokeEntry).getValue();
            }
            if (observerWraper == null) {
                throw new NullPointerException("observerWraper is null");
            }
            //得到observerWrapper的类对象, 编译擦除问题会引起多态冲突所以用getSuperclass
            Class<?> superclass = observerWraper.getClass().getSuperclass();
            Field mLastVersion = superclass.getDeclaredField("mLastVersion");
            mLastVersion.setAccessible(true);
            //2.得到mVersion
            Field mVersion = liveDataClass.getDeclaredField("mVersion");
            mVersion.setAccessible(true);
            //3.把mVersion的数据填入到mLastVersion中
            Object mVersionValue = mVersion.get(this);
            mLastVersion.set(observerWraper, mVersionValue);
            //            stickFlag = true;//打开这一行,代表下次添加观察者的时候调用observe方法时保持粘性
        } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }

    }
}

使用也很简单:
发送消息:

LiveDataBus.getInstance().with("start", String.class, false).postValue("MainActivity1消息1");                    

订阅消息:

LiveDataBus.getInstance().with("start", String.class, false).observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Toast.makeText(MainActivity2.this, s, Toast.LENGTH_SHORT).show();
            }
        });
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值