[Android]你不知道的Android进程化(5)--进程通信Messenger框架

大家好,我系苍王。
以下是我这个系列的相关文章,有兴趣可以参考一下,可以给个喜欢或者关注我的文章。

[Android]如何做一个崩溃率少于千分之三噶应用app--章节列表

Android组件化架构热卖中

组件化群1已经满员,进来的可以加群2 763094035



上一节,介绍了使用AIDL的进程通信框架。
这一节给大家介绍Messenger的通信框架,而Messenger其意思是“信使”的意思
使用Messenger的优势在于
1.实际传递的是Message,可以复用信息池
2.支持信息回调
3.不需要编写aidl

Messenger通信原理图

Messenger继承了Parcelable接口,可以作为序列化对象用于传输。
这里可以传入Handler,Handler里面有包含IMessenger对象

     /**
     * Create a new Messenger pointing to the given Handler.  Any Message
     * objects sent through this Messenger will appear in the Handler as if
     * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
     * been called directly.
     * 
     * @param target The Handler that will receive sent messages.
     */
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }

或者传入IBinder对象,Stub当中存在IMessenger对象

    /**
     * Create a Messenger from a raw IBinder, which had previously been
     * retrieved with {@link #getBinder}.
     * 
     * @param target The IBinder this Messenger should communicate with.
     */
    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

实际上Handler中IMessager实现对象是MessengerImpl

    final IMessenger getIMessenger() {
        synchronized (mQueue) {
            if (mMessenger != null) {
                return mMessenger;
            }
            mMessenger = new MessengerImpl();
            return mMessenger;
        }
    }

这里IMessenger是调用Handler的send方法来发送消息的。

    private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }

每个Message当中也包含了一个replyTo的变量用户回调

    /**
     * Optional Messenger where replies to this message can be sent.  The
     * semantics of exactly how this is used are up to the sender and
     * receiver.
     */
    public Messenger replyTo;

就这几个步骤Messenger独立的实现了Parcelable和使用aidl的通信方式

接下来我们介绍一下Modular框架是用Messenger通信设计。
不同于上一节介绍的ModularArchitecture的aidl中通信是1对1的通信,Modular提供的通信框架是通过1对多的发送方式来传递的。

以下是Messenger的注册流程图


Messenger注册流程图

1.每个模块初始化时启动一个消息队列来监听消息。

    @Override
    public void init() {
        mBaseModule = this;
        //启动线程接收消息
        mWorkThread = new WorkThread();
        mWorkThread.start();
        //注册跳转路由
        OkBus.getInstance().register(Constants.ROUTER_OPEN_URL, new Event() {
            @Override
            public void call(Message msg) {
                String url = (String) msg.obj;
               //实际跳转使用的Router               
               Router.openLocalUrl(BaseAppModuleApp.getBaseApplication(), url);
            }
        }, Bus.UI); //线程参数
    }

    public class WorkThread extends Thread {
        Handler mHandler;
        public Messenger clientHandler;

        @Override
        public void run() {
            Looper.prepare();
            //每个module都有接收消息处理ClientHandler
            mHandler = new ClientHandler();
            clientHandler = new Messenger(mHandler);
            if(resultRef!=null){
                try {
                    resultRef.set(clientHandler);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    //使用CountDownLatch唤醒机制保证线程安全
                    latch.countDown();
                }
            }
            Looper.loop();
        }

        public void quit() {
            mHandler.getLooper().quit();
        }
    }

2.绑定MessengerService作为进程间管理,并且绑定每个模块的ServiceConnection

/**
     * 连接服务器
     */
    public void connectService() {
        Intent intent = new Intent(MessengerService.class.getCanonicalName());// 5.0+ need explicit intent
        intent.setPackage(Constants.SERVICE_PACKAGE_NAME); // the package name of Remote Service
        //绑定MessengerService作为进程间管理,并且绑定每个模块的ServiceConnection
        boolean mIsBound = bindService(intent, mBaseModule.mConnection, BIND_AUTO_CREATE);
        LogUtils.i(Constants.TAG + " connectService", " ServiceConnection-->bindService  mIsBound: " + mIsBound);
    }

3.启动MessengerService,启动消息循环

 @Override
    public void onCreate() {
        super.onCreate();
        LogUtils.i(Constants.TAG + " essengerService", "MessengerService -->onCreate");
        mWorkThread = new WorkThread();
        mWorkThread.start();
    }

    public class WorkThread extends Thread {
        public ServiceHandler mHandler;

        @Override
        public void run() {
            Looper.prepare();
            LogUtils.i(Constants.TAG + " essengerService", "MessengerService -->new ServiceHandler");
            //通过ServiceHandler来处理收到的消息
            mHandler = new ServiceHandler();
            Messenger mMessenger = new Messenger(mHandler);
          //  OkBus.getInstance().mServiceMessenger = mMessenger;
            try {
                resultRef.set(mMessenger);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                latch.countDown();
            }
            Looper.loop();
        }

        public void quit() {
            mHandler.getLooper().quit();
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        try {
            latch.await(10, TimeUnit.SECONDS); //最多等待10秒
        } catch (Exception e) { //等待中断
            e.printStackTrace();
        }
        Messenger mMessenger = resultRef.get();
        return mMessenger.getBinder();
    }

4.初始化OkBus并发送消息注册

    public void initModule(BaseModule mBaseModule, Messenger mServiceMessenger, int mModuleId, Messenger mClientMessenger) {
        this.mServiceMessenger = mServiceMessenger;
        this.mModuleId = mModuleId;
        this.mBaseModule = mBaseModule;
        isModule.set(true);
        mBaseModule.isConnected.set(true);
        //线程池获取信息
        Message msg = Message.obtain();
        Bundle data = new Bundle();
        data.putInt(Constants.REGISTER_ID, mModuleId);//注册模块信息
        msg.setData(data);
        msg.replyTo = mClientMessenger;   //将处理消息的Messenger绑定到消息上带到服务端
        try {
            //发送到MessengerService中处理
            mServiceMessenger.send(msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

5.ServiceHandler中维护一个对Service Messenger到多个Client Messenger的关系

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        try {
            Bundle bundle = msg.getData();
            int registerId = bundle.getInt(Constants.REGISTER_ID, -1);
            if (registerId > 0) {//注册模块类型的消息
                LogUtils.logOnUI(Constants.TAG, "handleMessage: msg = [收到注册模块类型的消息]: registerId: " + Integer.toHexString(registerId));
                //每个模块对应的ClientHandler
                Messenger client = msg.replyTo; 
                mClientMessengers.put(registerId, client);//存储Client端接受处理消息的Messenger来发送Message到Client

                Message data = Message.obtain();
                Bundle mBundle = new Bundle();
                mBundle.putInt(Constants.REGISTER_RES, Constants.REGISTER_SEC);    //通知Client模块注册成功
                data.setData(mBundle);
                try {
                    client.send(data);  //回调注册状态给模块
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

6.介绍的模块注册结果

public class ClientHandler extends Handler {

    @Override
    public void handleMessage(Message msg) {
        ……
        int resCode = bundle.getInt(Constants.REGISTER_RES, -1);
        if (resCode < 0) {//收到普通消息
        ……
        } else {//收到模块注册结果消息
            boolean isRegisterSec = resCode == Constants.REGISTER_SEC;
            if (isRegisterSec) {
                LogUtils.logOnUI(Constants.TAG, "handleMessage() : reply = [注册成功]");
            }
        }
    }
}

7.其绑定完毕之后,将module信息注册到OkBus,之后会使用afterConneted()来初始化想要接收的消息

public ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            LogUtils.logOnUI(Constants.TAG, "ServiceConnection-->onServiceConnected 已自动唤醒服务器");
            Messenger mServiceMessenger = new Messenger(service);
            OkBus.getInstance().initModule(mBaseModule, mServiceMessenger, getModuleId(), mWorkThread.clientHandler);
            afterConnected();
        }
    

8.通过ServiceBus来注册其他module会跨module调用过来的消息。

@Override
    public void afterConnected() {

        ServiceBus.getInstance().registerService(Constants.SERVICE_A_UID, msg -> {
            LogUtils.logOnUI(Constants.TAG, "afterConnected  a 进程收到[服务请求]消息:ServiceMessage-->hello:  " + Integer.toHexString(Math.abs(msg.what)));
            return "10086";
        });
    }

9.服务注册后,可以看到其会通过onEvent返回回调的msg对象(CallBack接口)

    /**
     * 注册服务
     *
     * @param serviceId 服务id
     * @param callback  服务调用的回调
     * @param <T>       服务返回的数据范型
     */
    public <T> void registerService(final int serviceId, final CallBack<T> callback) {
        LogUtils.logOnUI(Constants.TAG, "注册服务  " + Integer.toHexString(Math.abs(serviceId)));
        okBus.unRegister(serviceId);//服务提供者只能有一个
        okBus.register(serviceId, msg -> {
            //TODO 优化到子线程
            okBus.onEvent(serviceId - 1, callback.onCall(msg));
        });
    }

到这里就介绍完初始化和注册流程了。

接下来介绍一下消息发送的架构,注意一下的模块服务规则

    //==================模块间的服务定义============//
    /**
     * 服务定义规则:
     * 1、服务的请求ID必须是负值(正值表示事件)
     * 2、服务的请求ID必须是奇数,偶数表示该服务的返回事件,
     * 即:   requestID-1 = returnID
     * 例如  -0xa001表示服务请求  -0xa002表示-0xa001的服务返回
     */
    public static final int SERVICE_A_UID = -0xa001;

        /**
         * 异步调用远端服务
         */
        findViewById(R.id.bt_1).setOnClickListener(v -> {
        //异步调用,
           ServiceBus.getInstance().fetchService(Constants.SERVICE_A_UID, msg -> {
                LogUtils.logOnUI(Constants.TAG, "b 进程收到[异步服务返回]消息:  获取到的UID-->" + msg.obj);
                Toast.makeText(BModuleActivity.this,
                        "b 进程收到[异步服务返回]消息:  获取到的UID-->" + msg.obj,
                        Toast.LENGTH_SHORT).show();
            });
        });

具体步骤
1.对ID的请求限制
2.module连接的判断,没有连接就尝试连接
3.唤醒目标进程
4.注册回调
5.通知目标模块

跨模块通信
    /**
     * 异步调用服务
     *
     * @param serviceId 服务id
     * @param callback  回调
     */
    public void fetchService(final int serviceId, final Event callback) {
        if (serviceId > 0 || serviceId % 2 == 0) {
            assert false : "请求ID必须是负奇值!";
            return;
        }
        if (okBus.isModule() && !okBus.isModuleConnected()) {
            LogUtils.logOnUI(Constants.TAG, "请求失败,服务已经断开链接,尝试重新打开服务,进行请求");
            BaseAppModuleApp.getBaseApplication().connectService();
            return;
        }

        //自动唤醒目标进程
        if (okBus.isModule()) {
            String module_name = Integer.toHexString(Math.abs(serviceId)).substring(0, 1);
            noticeModule(module_name, serviceId, null);
        }

        //1、先注册回调
        okBus.register(serviceId - 1, msg -> {
            callback.call(msg);
            okBus.unRegister(serviceId - 1);//服务是单次调用,触发后即取消注册
        });
        //2、通知目标模块
        okBus.onEvent(serviceId);
    }

唤醒目标进程

    /**
     * 唤醒目标进程
     *
     * @param module_name 模块名
     * @param serviceId   服务ID
     * @param url         要打开的url
     */
    public void noticeModule(String module_name, int serviceId, String url) {
        Intent ait = new Intent(NoticeService.class.getCanonicalName());// 5.0+ need explicit intent        //唤醒目标进程的服务Action名
        ait.setPackage(Constants.MODULE_PACKAGE_PRE + module_name);   //唤醒目标进程的包名
        //绑定包名
        BaseAppModuleApp.getBaseApplication().bindService(ait, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                if (service != null) {
                    LogUtils.logOnUI(Constants.TAG, "已经自动唤醒" + module_name);
                    Messenger moduleNameMessenger = new Messenger(service);
                    Message _msg = Message.obtain();
                    Bundle _data = new Bundle();
                    _data.putBoolean(Constants.NOTICE_MSG, true);
                    _msg.setData(_data);
                    _msg.replyTo = okBus.mServiceMessenger;//把服务器的信使给目标组件的信使,让他俩自己联系,这里仅仅是通知
                    try {
                        moduleNameMessenger.send(_msg);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    try {
                        Thread.sleep(200);//给服务器和目标组件500ms联系的时间
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } else {
                    LogUtils.logOnUI(Constants.TAG, module_name + "进程,本来就是醒的");
                }

                if (serviceId < 0) {  //唤醒成功,继续发送异步请求,通知目标模块
                    okBus.onEvent(serviceId);
                }
                if (!TextUtils.isEmpty(url)) {  //目标url不为空,继续打开目标
                    OkBus.getInstance().onEvent(Constants.ROUTER_OPEN_URL, url);
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                LogUtils.logOnUI(Constants.TAG, "自动唤醒目标进程失败 module_name:" + module_name);
            }
        }, BIND_AUTO_CREATE);
    }

启动模块,并传递绑定对象

    /**
     * 收到唤醒通知之后,初始化模块,并自动去服务器注册
     *
     * @param intent
     * @return
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        LogUtils.logOnUI(Constants.TAG, getPackageName() + " 收到唤醒通知");
        //获取模块的module
        BaseModule mBaseModule = BaseAppModuleApp.getBaseApplication().mBaseModule;
        if (!mBaseModule.isConnected.get()) {
            LogUtils.logOnUI(Constants.TAG, getPackageName() + " 我被唤醒啦");
            //初始化module,启动module的ClientHandler(Messenger)
            mBaseModule.init(latch, resultRef);
            mBaseModule.afterConnected();
            try {
                //超时限制
                latch.await(2000, TimeUnit.SECONDS);
            } catch (Exception e) { //等待中断
                e.printStackTrace();
            }
        }
        //返回ClientHandler的Binder对象
        return mBaseModule.mWorkThread.clientHandler.getBinder();
    }

发送消息,其最终是通过ServiceBus转发到每个模块OkBus,然后Binder传递的Messenger关联来完成信息传递。

/**
     * @param tag  发送消息的事件ID
     * @param data 发送消息的数据
     * @return
     */
    public OkBus onEvent(int tag, Object data) {
        //发送时间,所以tag小于0
        String hex = Integer.toHexString(Math.abs(tag));
        LogUtils.i("Message OkBus", "onEvent  " + (tag > 0 ? "[普通]" : "[服务]") + "  tag: " + hex);

        //1、本地先处理非服务消息
        if (tag >= 0) onLocalEvent(tag, data);

        //2、如果是组建化,向服务器发消息
        if (isModule.get()) {
            //保证发送时服务启动
            if (!isModuleConnected()) {
                LogUtils.i("Message OkBus", "发消息失败,服务已经断开链接,尝试重新打开服务,进行发消息");
                BaseAppModuleApp.getBaseApplication().connectService();
                return this;
            }
           //数据为空,即为事件
            if (data == null || data instanceof Serializable) {
                Message newMsg = new Message();
                if (data != null) {
                    Bundle bundle = new Bundle();
                    bundle.putSerializable(Constants.MESSAGE_DATA, (Serializable) data);
                    newMsg.setData(bundle);
                }
                newMsg.arg1 = mModuleId;
                newMsg.what = tag;
                try {
                    //发送信息到目标Service
                    mServiceMessenger.send(newMsg);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                assert false : "跨进程时,你传递的对象没有序列化!";
            }
        } else if (tag < 0) {//非组件化时本地处理服务消息
            onLocalEvent(tag, data);
        }
        return this;
    }

使用Messenger通信框架设计就介绍到这里。
1.Modular框架,模块内传输使用了OkBus的路由传输,而在跨模块则使用Messenger的方式来完成
2.Messenger实际是一个封装好的IBinder对象
3.Modular通过合理设置跨模块的传输的协议逻辑来完成信息传输

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,681评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,710评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,623评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,202评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,232评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,368评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,795评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,461评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,647评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,476评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,525评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,226评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,785评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,857评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,090评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,647评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,215评论 2 341

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,330评论 25 707
  • 序:很多都是自己的个人理解,不一定非常准确,供大家参考学习 大家应该都用过进程间的通讯,那有没有想过一个问题,进程...
    _水蓝阅读 934评论 0 3
  • Android跨进程通信IPC整体内容如下 1、Android跨进程通信IPC之1——Linux基础2、Andro...
    隔壁老李头阅读 3,506评论 3 12
  • 有时候如果情绪低落了,我就好想到动物园里去。它们其实都很乖,不会多说话。于是经常我们就像很熟悉的朋友一样,隔着不远...
    四月芳菲五月红泥阅读 165评论 0 2
  • 我是一个喜欢写信的人,从小学到大学,给老师写过,给同学写过,给父母写过,给男朋友写给,甚至给陌生人写过,然而想想,...
    idea伟阅读 507评论 3 6