Android组件通信方式

1,419 阅读5分钟

本文正在参加「金石计划 . 瓜分6万现金大奖」

前言

因为之前被问到过,最近突然有点兴趣上头,所以想简单做一个总结,现在开发基本都会用到组件化,那么大家有没有想过在不使用别人写的框架的情况下,自己如何去实现组件化。

本次主要讲两个互不依赖的组件之间的通信,而如果两个组件之间存在依赖,那就可以直接去调用,这种情况就不说了。本次主要讲的是技术层面的实现,不讲架构层面的,因为这个功能如果要做架构设计,那将会是一个大工程,对于组件间的架构设计,很多大佬也有讲。但是具体的技术实现,却很少有人提到,我这里就简单做个总结。

组件间控件的跳转

比如说组件A的一个Activity跳转到组件B的一个Activity,具体是如何实现的。

这里我们可以用ARouter来分析,我之前也有一篇文章简单写过,感兴趣的可以去看看juejin.cn/post/714936…

这里就直接给出结论:显式跳转

简单来说就是组件A拿到组件B的Class之后进行显示跳转。

afce0a8ae518bea4ba8efcbba0c4f43.png

资源调用

资源调用就更简单了,虽然在不同的模块, 但是AssetManager会统一加载路径,所以直接用Resource就能加载资源了。

组件通信

这个才是我这次要讲的重点,举个栗子,互不依赖的模块A调用模块B的一个方法,如何去调用?

其实说到这个,很多人第一反应就是EventBus,那除了EventBus,还有其它的能实现这个功能的方式吗?他们究竟是如何实现的?

反射

让我们请出今天的一号选手,反射

反射选手驰骋Android界可谓是无所不能,如果时间回到很早很早以前,没有ARouter,没有各种Bus,你让我来做,我可能第一个想到的就是反射。

ea87d60ba5ff56b9a358f4c085357ad.png

反射可以实现这两个模块不引用相同模块的情况下,能让其中一个模块调用另一个模块的方法、属性等。

只要能拿到Class对象,你就可以做到任何你想做的事,这里我们的组件同属一个包的情况下,你可以通过类名等方式去获取到Class对象,哪怕是不同包,你也可以通过ClassLoader去拿到Class对象。

EventBus

让我们请出二号选手EventBus,EventBus可谓是Android界的当红炸子鸡,它试用简单,只需要一个方法一个注释,便能快速的实现组件间的跳转。

但是它也有坏处,如果你只会滥用EventBus的话,我相信接手你项目的那位老兄十分的想感谢你的全家。当项目变得越来越大的时候,功能变得越来越大的时候,漫天乱飞的EventBus会让开发者活着煎熬中。

接下来可以简单看看EventBus是如何在组件间进行穿梭的(我现在项目没用到这个,因为我这今天网慢,下不下来,我直接github上拿代码讲)。

直接看post,不用管细节,这里不是源码解析EventBus,只是为了找核心,所以跟进主线任务

8763e446a0ea65424e3a289c027cbb1.png

6d3f10f08493c983b830b3b09525ce5.png

5bbc08ee0794bfa59a7f04f000e064e.png

6668af900bfca8c5589b305cac6b2dc.png

71e6f86bc33d41a7d9282e712e9e41f.png

简单瞟一眼,发现有关线程什么的,然后最后调到subscription.subscriberMethod.method.invoke(subscription.subscriber, event),我第一眼就感觉是用了反射,没关系,可以再进去详细看看。

e6f9d4e6f8f6b9b4f1749be59fa4d70.png

果然是反射,所以结论就是:EventBus是基于反射来实现组件间通信
没想到这2号选手是1号选手的小弟。

ARouter

有请3号选手ARouter,嗯?不对,3号选手不是做跳转用的吗?当然不是,ARouter同样是可以实现组件通信的,可不只是能做Activity跳转。

我们可以看看Warehouse,它还有provider和interceptor

class Warehouse {
    // Cache route and metas
    static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
    static Map<String, RouteMeta> routes = new HashMap<>();

    // Cache provider
    static Map<Class, IProvider> providers = new HashMap<>();
    static Map<String, RouteMeta> providersIndex = new HashMap<>();

    // Cache interceptor
    static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
    static List<IInterceptor> interceptors = new ArrayList<>();

    static void clear() {
        routes.clear();
        groupsIndex.clear();
        providers.clear();
        providersIndex.clear();
        interceptors.clear();
        interceptorsIndex.clear();
    }
}

我们主要是讲组件通信,这里就不源码解析ARouter了。

那一般ARouter怎么实现呢,其实ARouter内部有个IProvider,我们可以写一个接口继承IProvider,这个就相当于一个服务的意思,我们给这个组件写一个或多个服务,然后将服务提供给其它组件去调用,服务里面实现自己想要实现的功能。

d39d6a85297f67802f1446322a9656e.png

image.png

然后ARouter有一部分操作就是经典的添加到路由表的操作,这个我也不多说了,看到这里的如果还不知道ARouter的路由表的操作的话,可以先去了解一下,如果想简单了解,可以看我之前写的这篇文章juejin.cn/post/714936… ,这里还是主要讲重点代码

3e2d2b2938e7a5825965fb8cb540901.png

看到上面的代码,拿到Class,然后调用Class的newInstance,这里就是反射创建一个对象。所以可以得出结论:ARouter实现组件间通信的方式就是反射
好家伙,没想到3号选手也是1号选手的小弟。
有点不一样的是,navigation方法的返回值return (T) postcard.getProvider();可以看出,虽然它是用了反射,但是还是会让一个模块拿到另一个模块的接口,而要拿到接口,除了两个模块有依赖关系的做法之外,还有一个做法就是两个模块都共同依赖一个模块,接口定义在那个模块里面。

那有人可能会想,用反射不都可以实现互不依赖的情况下通信了吗,为什么还要这样做。其实这就是一个架构层面的设计,我们这里只讲技术实现层面的,就不详细去说。但你可以想想他和2号选手eventBus有什么不同,为什么当功能多时,当代码多时,eventBus可能会看起来觉得很乱,但是ARouter不会。

局部广播

4号选手局部广播LocalBroadcastManager,也能实现组件间的通信,它不会也是1号选手反射的小弟吧?

来看看它的做法,首先要有个概念,局部广播,是不能和普通的广播一样做到进程间通信的,其实,它是一个单例。

public void registerReceiver(@NonNull BroadcastReceiver receiver,
        @NonNull IntentFilter filter) {
    synchronized (mReceivers) {
        ReceiverRecord entry = new ReceiverRecord(filter, receiver);
        ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
        if (filters == null) {
            filters = new ArrayList<>(1);
            mReceivers.put(receiver, filters);
        }
        filters.add(entry);
        for (int i=0; i<filter.countActions(); i++) {
            String action = filter.getAction(i);
            ArrayList<ReceiverRecord> entries = mActions.get(action);
            if (entries == null) {
                entries = new ArrayList<ReceiverRecord>(1);
                mActions.put(action, entries);
            }
            entries.add(entry);
        }
    }
}

看到注册广播的做法,就是把广播放到一个HashMap里面
private final HashMap<BroadcastReceiver, ArrayList> mReceivers = new HashMap<>();
至于是什么HashMap,你可以先不用管,好奇的话可以自己去分析源码,它的源码也很简单,这里只为了讲重点。再看看发送消息的操作

069b182c869f2e603f72bcc70ffa8d2.png

方法是sendBroadcast,太长了我就截取一部分,从中看得出,它是用了Handler。所以可以得出结论:局部广播实现组件间通信的方法是Handler+数组+单例

image.png

总结

这次主要从技术层面去查看一些实现组件间通信的方式具体是如何实现的,总共有4位选手:反射、EventBus、ARouter、局部广播。他们形成了正义的三打一,其实这说明实现组件间通信的方式无外乎两种,反射和Handler。

反射作为Android中的万金油,这是类和类加载机制的体现。而Handler是Android进程内通信的桥梁。这些组件哪怕互不依赖,也存在于同一个进程中,所以Handler对他们有效,如果在不同的进程,那当然就要用Binder了。

技术层面的实现,就是这两种方式,而架构层面去实现组件间的通信,那方式就多了,也更为复杂,虽然直接用反射、EventBus、ARouter都是用了反射,但他们在架构上的设计不同,最终实现的效果也非常的不同。

如果你还有什么其它组件间的通信方式,可以留言告诉我,如果我感兴趣的话,也会再去看看它内部是怎么去实现。