在上一次https://www.cnblogs.com/webor2006/p/12179301.html咱们对于EventBus进行了原理性剖析,但是!!!它的使用只限于同一个进程中,如果想要在多个进程之间也能像EventBus一样这样使用,此时可以借用饿了么开源的HermesEventBus这个框架,但是实际商用我还木有用过它,但是从学习的角度对于探究它的底层原理对于自已的技能提升还是很有必要的。
HermesEventBus开源库了解:
在正式分析它的原理之前肯定得要学会使用,所以接下来先来了解它的基本使用,这里操作参考该博主:https://www.jianshu.com/p/f34c3336296f
这里还是以上一次分析EventBus的例子进行HermesEventBus的入门,简单回顾一下,有两个Activity:
其中MainActivity是用来接收EventBus的消息的,而SecondActivity则是用来发送消息的,接下来要模拟多进程的程序,咱们将SecondActivity声明在另一个进程,如下:
此时运行再看一下如果使用EventBus肯定是无法进行消息传递了,如下:
其中可以看到已经是有两个进程了:
好,接下来咱们使用HermesEventBus来解决跨进程发消息的问题,先来瞅一下为啥这个开源库是命名为HermesEventBus,看官方的解释就晓得了:
而Hermes是饿了么开源的一个IPC框架:
而Hermes貌似是个品牌。。
好吧,这技术够奢侈,费话不多说,准备开始按官网进行集成它:
其中对Binder跨进程有研究的一看这个Service的声明就知道为啥呢,等于框架底层肯定是用了AIDL来进行通信的,这个在之后的原理剖析中再进行验证,继续集成:
这一步木问题,直接再往下看步骤:
这里为了方便起见,不在Application当中写了,直接在MainActivity,反正它就是属于main app当中的:
然后在SecondActivity中再进行:
那咱们来照着替换一下:
好,下面来验证一下是否能跨进程进行通讯了?
我擦,这库是有多不稳定,这不是完全按照官方集成的步骤来的么,直接崩了。。于是乎上官方github的issue找了下,原因是:
好吧,我的智商被碾压了,看样子商用时还得谨慎用它,纯技术研究还是木问题,好吧,咱们来照着搞一下,只不过咱们的包名得用“androidx.appcompat.app”:
再运行:
嗯~~确实解决了跨进程发消息的问题,而且这种使用方式很明显比我们传统用AIDL的方式要简单很多,而且使用习惯跟EventBus几乎一样,所以未来商用时可能还是有实用价值的。
HermesEventBus原理分析:
init():
就是初始化了一个Context,继续往下:
其中大致瞅一下MainService这个类:
具体来看一下注册这个类的细节:
先检查该类是否合法,具体检测细节就不看了,主要看主流程。
其中缓存的key是根据方法来生成的,具体瞅一下:
最后再回到主干:
register():
其中mEventBus的初始化在:
很明显确实它的底层有用到EventBus,所以这个注册没啥可说的。就是标准的EventBus注册。
connectApp():
接下来则是跳到了第二个进程了:
而这个监听的调用其实就是Binder服务连接成功与解绑时进行调用的:
回到主流程:
看到我们在清单文件中注册的服务了:
大致瞅一下该服务,肯定有stub啦:
猜都能猜到准备要绑定服务了:
其中intent很明显就是一个隐式的方式来启动服务的,因为HermesService是注册在主进程中的,而非在SecondActivity这个进程中,所以要注意到这个细节。
下面来具体看一下当远程服务连接成功之后在客户端上做了啥处理:
这里需要特意清楚的是我们的服务是存在于主进程中,从清单文件的注册就能知道:
所以在SecondActivity中来绑定远程的服务很明显就是一个AIDL的跨进程调用过程。
post()【核心】:
好,接下来则重点分析一下发消息以及消息是怎么来传达给MainActivity的,流程比较复杂,需要睁大眼睛。。
其中最终会调用IMainService.post()方法,而IMainService是一个接口,它的具体实现类为MainService:
此时看一下mRemoteApis是在哪初始化的:
然后定位到它的action()方法:
此时就来看一下它里面的object是在哪里设置的,还记得我们在connect()时会绑定远程服务么?其实就是在绑定远程服务成功时设置的,下面挼一下:
好,继续来往下看:
此时就会回到了:
此时又往上调用了:
其中的o就是我们之前看到的那个对象,最终再调用该对象的post()方法,定位瞅一下:
是个抽象接口。。所以要继续往下分析post的细节之前,首先得要搞清楚这个对象的实例是哪个,所以,咱们又得回到之前的这个地方来了,好绕:
其实生成的是一个动态代理对象,你怎么知道?咱们往里分析就晓得了~~
具体再确认一下:
妥妥的了,关于动态代理这块当时https://www.cnblogs.com/webor2006/p/10502230.html在分析retrofit原理时也对它介绍过了,对于执行该代理对象的具体方法最终都会由InvocationHandler来进行拦截处理,也就是:
关于这个对象具体是啥这里就暂且分析到这,回到主流程来,免得走丢了:
所以肯定会转由HermesInvocationHandler来拦截处理,所以咱们要想分析出post的实现就得看这个地方了:
此时就需要看一下mSender是啥?
所以此时再看一下send()方法的细节:
转由CHANNEL处理了,跟进去:
所以:
最终则会调用咱们服务端服务这块了:
接着来分析它里面的细节:
先得看一下receiver对象具体是哪一个:
所以咱们再来看一下经action()中具体的实现细节:
纵览其实现,一看就知道准备通过反射来调用要订阅的方法了,也就是:
那目前只是一个猜测,下面具体看下是否如所想:
所以,来ObjectReceiver来瞅瞅:
回忆一下:
好,此时已经初始化Receiver对象中的method字段了,接下来继续:
设置参数类型:
具体设置参数的细节这里就不多看了,总之经过这个方法之后Receiver中的方法参数信息字段也被初始化了:
好,方法和方法参数都确定了之后,下面不直接可以调用了:
果真如之前的预想一样,那么接下来咱们就可以在主进程中注册的订阅方法中进行消息处理:
至此!!整个跨进程消息发送的机制就完整分析完了,总的来说还是借助于AIDL来实现跨进程调用的,不过封装得还是挺复杂的。
destroy():
最后就是销毁的操作了,比较简单,大致瞅一下:
此时又转由CHANNEL来调用了,它主要是Binder通信的桥梁,跟进去瞅一下:
将绑定的服务给注销掉,此时就会回调这个方法:
然后再往上回调:
最终就会调到:
至此关于HermesEventBus的核心原理的分析就完了,在下一次得来一个挑战,手动从0开始来实现它的这样一个核心功能~~