《深入解析Android 5.0系统》——第6章,第6.5节进程间的消息传递

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介:

本节书摘来自异步社区《深入解析Android 5.0系统》一书中的第6章,第6.5节进程间的消息传递,作者 刘超,更多章节内容可以访问云栖社区“异步社区”公众号查看

6.5 进程间的消息传递
深入解析Android 5.0系统
Android的消息可以在进程之间传递。进程间消息传递是建立在Binder通信基础之上的。Binder本身用来在进程间传递信息已经足够了,这里介绍的进程间消息传递方法只是让应用在设计上更加便利,并不是架构上大的改进。

我们知道,只要有了Binder的引用对象就可以调用其功能。Android中如果希望向另一个进程的Handler发送消息,一定要通过某个Binder对象来代理完成。在Handler类中,方法getIMessage()会创建一个Binder对象,代码如下:

final IMessenger getIMessenger() {

synchronized (mQueue) {

    if (mMessenger != null) {

        return mMessenger;

    }

    mMessenger = new MessengerImpl();

    return mMessenger;

}
}
AI 代码解读

getIMessenger()中创建的对象的类型是MessengerImpl,它是一个Binder的服务类,从IMessenger.Stub类派生。MessengerImpl中的send()方法的作用就是给Handler发消息。代码如下:

private final class MessengerImpl extends IMessenger.Stub {

public void send(Message msg) {

    Handler.this.sendMessage(msg);

}
}
AI 代码解读

因此,调用某个Handler对象的getIMessenger()方法将得到能给这个Handler对象发送消息的Binder对象。但是要跨进程发送消息,还需要把这个Binder对象传递到调用进程中。

6.5.1 理解Messenger类
Messenger类实际上是对IMessenger对象的包装,它里面包含了一个Handler对象关联的MessengerImpl对象的引用。Messenger类的构造方法如下:

public Messenger(Handler target) {

mTarget = target.getIMessenger();   // 获得MessengerImpl对象的引用
}
AI 代码解读

使用Messenger类的好处是隐藏了通信中使用Binder的细节,让整个过程看起来就像在发送一个本地消息一样简单。Messenger类的send()方法用来发送消息,它也是通过Binder对象在传递消息,代码如下:

public void send(Message message) throws RemoteException {

mTarget.send(message);
}
AI 代码解读

注意  send()方法并不是在本地调用的,通常要把Messager对象传递到另一个进程后再使用。Messenger类实现了Parcelable接口,因此,它能很方便地传递到另一个进程。

如果只需要在两个进程间进行简单的消息往来,上面介绍的知识已经够用了。为了方便应用使用,Android还提供了AsyncChannel类来完成建立双向通信的过程。

6.5.2 建立通信通道——AsyncChannel类的作用
AsyncChannel类用来建立两个Handler之间的通信通道。这两个Handler可以在一个进程中,也可以在两个进程中。但是通信双方的地位并不是对等的,一方要充当Service并响应AsyncChannel类中定义的消息。另一方则充当client的角色,主动去连接对方。

使用AsyncChannel类首先要确定通信双方使用“半连接模式”还是“全连接模式”。所谓的“半连接模式”是指连接建立后,只能客户端主动给服务端发送消息,服务端可以在收到客户端的消息后利用消息中附带的Messenger对象来给客户端“回复”消息,但是不能主动给客户端发送消息。“全连接模式”则是双方都能主动向对方发送消息。显然,“全连接模式”比“半连接模式”占用更多的系统资源。

1.半连接模式

AsyncChannel类中提供很多个连接方法,客户端需要根据情况使用。但是在连接前,还是要先得到服务端的Messenger对象。前面介绍过,Jave层中Binder的传递有两种方式,一种方式是通过已经建立好的Binder通道来传递Binder对象。另一种方式是通过组件Service来获得Service中包含的Binder对象。AsyncChannel类同时支持这两种方式。

如果客户端和服务已经建立了Binder通道,那么服务的Messenger对象就可以通过它传递到客户端,这样我们就可以使用AsyncChannel类的connect()方法来建立两者之间的联系了。

public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
    connected(srcContext, srcHandler, dstMessenger);
    replyHalfConnected(STATUS_SUCCESSFUL);
}
AI 代码解读

connect()方法的参数srcContext是客户端的上下文,srcHandler是客户端的Handler对象,dstMessenger是从服务端传递来的Messenger对象。

AsyncChannel还为通信双方定义了非常简单的握手协议,connect()方法会调用replyHalf Connected()方法来发送CMD_CHANNEL_HALF_CONNECTED消息给客户端的srcHandler。为什么消息是发给客户端中的Handler对象而不是发到服务器呢?其实这个简单的握手协议主要是为全连接模式准备的,半连接只要得到了对方的Messenger就可以通信了,但是全连接模式下必须先确保通信双方都准备好了才能开始通信过程,因此需要一个简单的握手协议。在半连接情况下的srcHandler对象不需要去处理CMD_CHANNEL_HALF_CONNECTED消息,调用完connect()方法后就可以开始发送消息了。

如果客户端和服务端之间没有现成的Binder通道,可以通过启动组件Service的方式来建立一个Binder通道,当然,在服务端实现的Service中包含的Binder对象必须是MessengerImpl对象。如果不愿意重新写一个Service,Android中提供一个类AsyncService,服务端可以直接使用它来创建一个Service。在这种情况下,客户端需要使用AsyncChannel类的另一个connect()方法来建立连接。

public void connect(Context srcContext, Handler srcHandler, String dstPackageName, String dstClassName)
AI 代码解读

connect()方法会根据传入的包名和类名去启动Service,启动的时间可能比较长,因此,connect()方法中使用了线程去启动Service。

new Thread(ca).start();
这个线程的执行方法如下:

public void run() {
    int result = connectSrcHandlerToPackageSync(mSrcCtx, mSrcHdlr, mDstPackageName,
                        mDstClassName);
    replyHalfConnected(result);
}
方法run()中调用了connectSrcHandlerToPackageSync()方法,它会调用binderService()来启动一个组件Service,代码如下:。

public int connectSrcHandlerToPackageSync(Context srcContext, Handler srcHandler, 
                    String dstPackageName, String dstClassName) {
    ......
    Intent intent = new Intent(Intent.ACTION_MAIN);
    intent.setClassName(dstPackageName, dstClassName);
    boolean result = srcContext.bindService(intent, mConnection, 
                                    Context.BIND_AUTO_CREATE);
    return result ? STATUS_SUCCESSFUL : STATUS_BINDING_UNSUCCESSFUL;
}
Service启动后,还必须实现ServiceConnection类来接收Service传递回来的Binder对象。AsyncChannel中的定义如下:

    class AsyncChannelConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            mDstMessenger = new Messenger(service);
            replyHalfConnected(STATUS_SUCCESSFUL);
        }
        ......
    }
AI 代码解读

在onServiceConnected()方法中把传递回来的Binder引用对象包装成了Messenger对象并保存在mDstMessenger变量中,如果需要给服务端发送消息,调用mDstMessenger对象的send()方法就可以了。onServiceConnected()方法中也调用了replyHalfConnected()方法来给客户端的Handler对象发送CMD_CHANNEL_HALF_CONNECTED消息,在目前这种情形下这条消息就有意义了,客户端的Handler对象收到消息才可以和服务端通信。

2.全连接模式

全连接模式是建立在半连接模式基础上的,当客户端的Handler对象收到消息CMD_ CHANNEL_HALF_CONNECTED以后,如果希望建立全连接,需要再向服务端发送CMD_CHANNEL_ FULL_CONNECTION消息,同时在消息中附上客户端的Messenger对象。服务端收到消息后,还需要给客户端回复CMD_CHANNEL_FULLY_CONNECTED消息,如果服务端同意建立全连接,会将消息的第一个参数msg.arg1的值设置为0,否则设置为非0值。图6.3是全连接模式的消息交互图。


18fc175cc9b1bb114cd1fbe3275c1e47c965c5ad

下面以Android中的WifiService和客户端WifiManager为例来了解全连接模式的建立过程。

先看客户端的实现,下面是类WifiManager中定义的Handler:

private static class ServiceHandler extends Handler {
    @Override
    public void handleMessage(Message message) {
        Object listener = removeListener(message.arg2);
        switch (message.what) {
            case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
                if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
                    sAsyncChannel.sendMessage(
                            AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
                } else {
                    sAsyncChannel = null;
                }
                sConnected.countDown();
                break;
            case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
                // Ignore
                break;
          ......
}
AI 代码解读

WifiManager收到了消息CMD_CHANNEL_HALF_CONNECTED后,调用sendMessage()方法给WifiService发送了一条CMD_CHANNEL_FULL_CONNECTION的消息。sendMessage()方法会把本端的Messenger对象附到消息中。

现在看看服务端是如何处理收到的消息的。下面是WifiService类中定义的Handler:

private class ClientHandler extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
                if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
                    mTrafficPoller.addClient(msg.replyTo);
                } else {
                    Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
                }
                break;
            }
            case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
                AsyncChannel ac = new AsyncChannel();
                ac.connect(mContext, this, msg.replyTo);
                break;
            }
            ......
}
AI 代码解读

服务端收到CMD_CHANNEL_FULL_CONNECTION消息后,创建了自己的AsyncChannel对象,然后调用它的connect()方法和客户端的Messenger进行绑定。其实,AsyncChannel对象也只能向一个方向发送消息,所谓的双向通信就是双方都有自己的AsyncChannel来发送消息。前面介绍了,调用connect()方法会给本端的Handler对象发送CMD_CHANNEL_HALF_CONNECTED消息,因此,服务端也对这条消息进行了处理,处理的方式是把客户端的Messenger对象保存到了成员变量mTrafficPoller数组中。对于服务端而言,它连接的客户端可能是多个,因此,它要用数组保存所有客户端的Messenger。这样整个过程就完毕了。这里注意,WifiService并没有向WifiManager回复CMD_CHANNEL_FULLY_CONNECTED消息。实际上这个回复的确有点多余,WfiiService并没有使用它。

从上面的分析不难看出,AsyncChannel的作用就是实现了建立双向Binder连接的过程,方便应用使用。不过AsyncChannel类也提供了新的功能:同步消息发送。

3.发送同步消息

AsyncChannel的sendMessageSynchronously()方法可以用来发送同步消息。sendMessageSynchronously()方法在发送完消息后会挂起线程进入等待状态,收到回复的消息后再恢复线程的运行。AsyncChannel中定义了一个嵌入类SyncMessenger来完成同步消息的发送。SyncMessenger类中定义的同步消息发送方法sendMessageSynchronously()的代码如下:

private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
    SyncMessenger sm = SyncMessenger.obtain();
    try {
        if (dstMessenger != null && msg != null) {
            msg.replyTo = sm.mMessenger;
            synchronized (sm.mHandler.mLockObject) {
                dstMessenger.send(msg);
                sm.mHandler.mLockObject.wait();
        }
    ........
    Message resultMsg = sm.mHandler.mResultMsg;
    sm.recycle();
    return resultMsg;
}
AI 代码解读

在发往另一端的消息中,msg.replyTo设置成了SyncMessenger对象,这样,回复消息将不会送到Asynchannel的srcHandler对象,而是被SyncMessenger对象收到。同时,发送完消息后,调用wait()方法挂起线程。

服务端发送的回复消息将在SyncMessenger中定义的SyncHandler中处理。

private class SyncHandler extends Handler {
    ......
    public void handleMessage(Message msg) {
        mResultMsg = Message.obtain();
        mResultMsg.copyFrom(msg);
        synchronized(mLockObject) {
            mLockObject.notify();
        }
    }
}
AI 代码解读

上面的代码中把回复消息保存到变量mResultMsg中后调用notify()方法解锁线程。线程恢复运行后将返回收到的消息,这样一次同步消息的发送就结束了。

目录
打赏
0
0
0
0
1819
分享
相关文章
安全监控系统:技术架构与应用解析
该系统采用模块化设计,集成了行为识别、视频监控、人脸识别、危险区域检测、异常事件检测、日志追溯及消息推送等功能,并可选配OCR识别模块。基于深度学习与开源技术栈(如TensorFlow、OpenCV),系统具备高精度、低延迟特点,支持实时分析儿童行为、监测危险区域、识别异常事件,并将结果推送给教师或家长。同时兼容主流硬件,支持本地化推理与分布式处理,确保可靠性与扩展性,为幼儿园安全管理提供全面解决方案。
Android系统SELinux安全机制详解
如此看来,SELinux对于大家来说,就像那位不眠不休,严阵以待的港口管理员,守护我们安卓系统的平安,维护这片海港的和谐生态。SELinux就这样,默默无闻,却卫士如山,给予Android系统一份厚重的安全保障。
72 18
【Android】网络技术知识总结之WebView,HttpURLConnection,OKHttp,XML的pull解析方式
本文总结了Android中几种常用的网络技术,包括WebView、HttpURLConnection、OKHttp和XML的Pull解析方式。每种技术都有其独特的特点和适用场景。理解并熟练运用这些技术,可以帮助开发者构建高效、可靠的网络应用程序。通过示例代码和详细解释,本文为开发者提供了实用的参考和指导。
70 15
Android调试终极指南:ADB安装+多设备连接+ANR日志抓取全流程解析,覆盖环境变量配置/多设备调试/ANR日志分析全流程,附Win/Mac/Linux三平台解决方案
ADB(Android Debug Bridge)是安卓开发中的重要工具,用于连接电脑与安卓设备,实现文件传输、应用管理、日志抓取等功能。本文介绍了 ADB 的基本概念、安装配置及常用命令。包括:1) 基本命令如 `adb version` 和 `adb devices`;2) 权限操作如 `adb root` 和 `adb shell`;3) APK 操作如安装、卸载应用;4) 文件传输如 `adb push` 和 `adb pull`;5) 日志记录如 `adb logcat`;6) 系统信息获取如屏幕截图和录屏。通过这些功能,用户可高效调试和管理安卓设备。
反向寻车系统怎么做?基本原理与系统组成解析
本文通过反向寻车系统的核心组成部分与技术分析,阐述反向寻车系统的工作原理,适用于适用于商场停车场、医院停车场及火车站停车场等。如需获取智慧停车场反向寻车技术方案前往文章最下方获取,如有项目合作及技术交流欢迎私信作者。
103 2
AI技术如何重塑客服系统?解析合力亿捷AI智能客服系统实践案例
本文探讨了人工智能技术在客服系统中的应用,涵盖技术架构、关键技术和优化策略。通过感知层、认知层、决策层和执行层的协同工作,结合自然语言处理、知识库构建和多模态交互技术,合力亿捷客服系统实现了智能化服务。文章还提出了用户体验优化、服务质量提升和系统性能改进的方法,并展望了未来发展方向,强调其在客户服务领域的核心价值与潜力。
115 6
DeepSeek大模型在客服系统中的应用场景解析
在数字化浪潮下,客户服务领域正经历深刻变革,AI技术成为提升服务效能与体验的关键。DeepSeek大模型凭借自然语言处理、语音交互及多模态技术,显著优化客服流程,提升用户满意度。它通过智能问答、多轮对话引导、多模态语音客服和情绪监测等功能,革新服务模式,实现高效应答与精准分析,推动人机协作,为企业和客户创造更大价值。
198 5
DeepSeek 大模型在合力亿捷工单系统中的5大应用场景解析
工单系统是企业客户服务与内部运营的核心工具,传统系统在分类、派发和处理效率方面面临挑战。DeepSeek大模型通过自然语言处理和智能化算法,实现精准分类、智能分配、自动填充、优先级排序及流程优化,大幅提升工单处理效率和质量,降低运营成本,改善客户体验。
105 2
在线教育网课系统源码开发指南:功能设计与技术实现深度解析
在线教育网课系统是近年来发展迅猛的教育形式的核心载体,具备用户管理、课程管理、教学互动、学习评估等功能。本文从功能和技术两方面解析其源码开发,涵盖前端(HTML5、CSS3、JavaScript等)、后端(Java、Python等)、流媒体及云计算技术,并强调安全性、稳定性和用户体验的重要性。

热门文章

最新文章

推荐镜像

更多
下一篇
oss创建bucket
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等