Android进阶笔记:Messenger源码详解

简介:

Messenger可以理解为一个是用于发送消息的一个类用法也很多,这里主要分析一下再跨进程的情况下Messenger的实现流程与源码分析。相信结合前面两篇关于aidl解析文章能够更好的对aidl有一个认识。(Android进阶笔记:AIDL内部实现详解 (一)Android进阶笔记:AIDL内部实现详解 (二)

用法说明

先来看一下Messenger在跨进程通讯时的使用方法,代码如下:

Service的代码

//用来传递Messenger中IMessenger
public class ServerService extends Service { public static final String TAG = "ServerService"; private Messenger messenger; @Override public void onCreate() { super.onCreate(); //创建一个Messenger对象 messenger = new Messenger(new MessengerHandler()); } @Nullable @Override public IBinder onBind(Intent intent) { //返回IMessenger return messenger.getBinder(); } } //用于创建Messenger的Handler class MessengerHandler extends Handler { public static final String TAG = "ServerService"; @Override public void handleMessage(Message msg) { if (msg.what == 10001) { String data = msg.getData().getString("data"); data = data == null ? "null" : data; Log.e(TAG, "handleMessage: get msg from client = (" + data + ")"); } } }
AI 代码解读

上面就是Service的代码,分析一下其实总共做了3步:

  1. 定义了一个MessengerHandler的内部类并且实现了handleMessage的内部回调;
  2. 创建了一个Messenger的对象,在Messenger的构造函数中传入刚刚创建的handler实例;
  3. 在Service的onBind方法中回调messenger的getBinder()方法;

Activity的代码

public class MainActivity extends AppCompatActivity { private ServiceConnection serviceConnection; private Messenger messenger; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.e("MainActivity", "onServiceConnected: connection success !!!"); //用返回的Ibinder对象来构造一个Messenger实例 messenger = new Messenger(service); //创建一个msg Message msg = new Message(); msg.what = 10001; Bundle bundle = new Bundle(); bundle.putString("data", "hello Server"); msg.setData(bundle); try { //调用messenger的send方法 messenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { messenger = null; } }; bindService(new Intent("com.coder_f.messengerdemo.ServerService"), serviceConnection, BIND_AUTO_CREATE); } @Override protected void onDestroy() { super.onDestroy(); unbindService(serviceConnection); } }
AI 代码解读

Activity中做的工作其实也不复杂也是3步:

  1. 通过绑定Service来获得IBinder对象;
  2. 通过IBinder对象重新构造一个Messenger;
  3. 通过Messenger的send方法来发送消息;

以上就是Messenger的使用方法。好了现在就可以根据上面的使用方法来看看Messenger内部到底是怎么来运作的。

源码解析

首先先来看看参数为Handler的Messenger的构造函数

    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
AI 代码解读

很简单,就是保存传入Handler的getIMessenger()方法返回的东西。那接下来就去看看getIMessenger()方法返回的是什么东西。

final IMessenger getIMessenger() {
   synchronized (mQueue) {
        if (mMessenger != null) {
           return mMessenger; } mMessenger = new MessengerImpl(); return mMessenger; } }
AI 代码解读

也不复杂,无非就是判断一下mMessenger是不是空然后返回一下。那mMessenger到底是什么呢。看看它的构造函数new MessengerImpl()代码如下:

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

看到这里相信已经摸的差不多了,这个结构就是aidl的结构。这里猜一下应该就明白了,源码里面肯定有定义了一个IMessage.aidl,而且里面还声明了一个send的方法。而这个seng的方法在Handler里面被实现了,具体就是通过Handler来发送一条消息。那么可以得到结论,最后mTarget获得的其实就是一个IMessenger.Stub的实例,里面已经实现了接口中的方法(send(Message msg))

事实证明确实如此,源码的位置:platform\frameworks\base\core\java\android\os\IMessenger.aidl

package android.os;

import android.os.Message;

/** @hide */
oneway interface IMessenger {
    void send(in Message msg); }
AI 代码解读

上面只是对构造函数的源码进行了分析,但是其实已经把Messenger的结构摸的八九不离十了;

好了既然构造函数分析的差不多了,根据流程下一步应该是通过messenger.getBinder()方法取出一个Ibinder对象通过Service来返回给Activity。那么接下来再来看看messenger.getBinder()方法:

    public IBinder getBinder() {
        return mTarget.asBinder();
    }
AI 代码解读

很清楚,调用了IInterface(mTarget就是IMessenger.Stub的实例继承了IMessenger,而IMessenger继承了IInterface)的asBInder方法返回了一个Binder(这里简单的理解其实就是返回了它自己,因为stub内部类也继承了Binder)。

service这边的代码只有这些,那根据上面的使用方法,继续来看Activity这边的代码吧。 
Activity这边也有一个构造函数,参数是一个IBinder对象,这个构造函数的源码如下:

    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }
AI 代码解读

很清楚返回的其实就是一个proxy类也就是一个binder驱动(不明白的可以看之前的博客)。 
然后接下来就是调用了Messenger的send方法;那继续再来看看这个send方法的源码是怎么样的。

public void send(Message message) throws RemoteException {
        mTarget.send(message);
}
AI 代码解读

其实就是调用了刚刚proxy的send方法吧message当参数传进去。这里面的逻辑其实就是通过这个proxy类中的IBinder对象来远程调用service中已经实现的send方法。 
一目了然Messenger就是一个典型的aidl的例子。

总结

  1. Server端就是Handler里面实现的MessengerImpl内部类,然后在Service里面被实例化了。而这个aidl也是只有一个方法(send(Message)),就是通过当前Handler来发送一个消息。

  2. Client端就是通过Service返回过来的IBinder类来获取一个proxy对象,通过proxy对象远程调用send方法来完成通讯。

补充:如果要实现Service那边处理完消息返回给activity的话只要在activity里面也创建一个Messenger,然后把这个Messenger通过Message赋值给参数message.replyTo传过去就好了,同样Service就可以通过这个参数里面的Messenger来发送消息给activity通过activity里面handler来处理消息来完成双向通讯。

注意:这里有一点就是如果不是跨进的的话Service和Activity都运行在主线程,那么Service中用于处理消息的Handler里面不能执行耗时的工作,不然会导致ActivityUI界面卡住,因为Handler是创建在Service线程(主线程)用的是主线程的Looper。如果是跨进程的话Activity这边的主线程就不会卡住(Service所在的线程会卡住)。因为在普通的aidl在proxy调用的时候(其实就是调用IBinder.transact方法时)会挂起当前线程因此在Service端执行耗时操作时activity的UI线程会卡住。而messenger和普通的aidl不同之处在于它又添加了一个Handler,而这个Handler是运行在Service所在线程(默认为Service所在进程的主线程)而真正的Messenger.send方法只执行了一个Handler的sengmessage方法(这个方法是运行在底层binder的工作线程中,只要在这个线程中不执行耗时操作调用方所在的线程就不会被挂起太久)。因此不会卡住(Service线程可能会卡住)。这一点我感觉IntentService的实现非常的相似。




    本文转自 一点点征服   博客园博客,原文链接:http://www.cnblogs.com/ldq2016/p/8418914.html,如需转载请自行联系原作者



目录
打赏
0
0
0
0
348
分享
相关文章
布谷一对一直播源码android版环境配置流程及功能明细
部署需基于 CentOS 7.9 系统,硬盘不低于 40G,使用宝塔面板安装环境,包括 PHP 7.3(含 Redis、Fileinfo 扩展)、Nginx、MySQL 5.6、Redis 和最新 Composer。Swoole 扩展需按步骤配置。2021.08.05 后部署需将站点目录设为 public 并用 ThinkPHP 伪静态。开发环境建议 Windows 操作系统与最新 Android Studio,基础配置涉及 APP 名称修改、接口域名更换、包名调整及第三方登录分享(如 QQ、微信)的配置,同时需完成阿里云与腾讯云相关设置。
Repo下载AOSP源码:基于ubuntu22.04 环境配置,android-12.0.0_r32
本文介绍了在基于Ubuntu 22.04的环境下配置Python 3.9、安装repo工具、下载和同步AOSP源码包以及处理repo同步错误的详细步骤。
533 0
Repo下载AOSP源码:基于ubuntu22.04 环境配置,android-12.0.0_r32
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
多年的互联网寒冬在今年尤为凛冽,坚守安卓开发愈发不易。面对是否转行或学习新技术的迷茫,安卓程序员可从三个方向进阶:1)钻研谷歌新技术,如Kotlin、Flutter、Jetpack等;2)拓展新功能应用,掌握Socket、OpenGL、WebRTC等专业领域技能;3)结合其他行业,如汽车、游戏、安全等,拓宽职业道路。这三个方向各有学习难度和保饭碗指数,助你在安卓开发领域持续成长。
139 1
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
|
7月前
|
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
220 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
FFmpeg开发笔记(四十七)寒冬下安卓程序员的几个技术转型发展方向
IT寒冬使APP开发门槛提升,安卓程序员需转型。选项包括:深化Android开发,跟进Google新技术如Kotlin、Jetpack、Flutter及Compose;研究Android底层框架,掌握AOSP;转型Java后端开发,学习Spring Boot等框架;拓展大前端技能,掌握JavaScript、Node.js、Vue.js及特定框架如微信小程序、HarmonyOS;或转向C/C++底层开发,通过音视频项目如FFmpeg积累经验。每条路径都有相应的书籍和技术栈推荐,助你顺利过渡。
205 3
FFmpeg开发笔记(四十七)寒冬下安卓程序员的几个技术转型发展方向
repo sync 更新源码 android-12.0.0_r34, fatal: 不能重置索引文件至版本 ‘v2.27^0‘。
本文描述了在更新AOSP 12源码时遇到的repo同步错误,并提供了通过手动git pull更新repo工具来解决这一问题的方法。
329 1
docker中编译android aosp源码,出现Build sandboxing disabled due to nsjail error
在使用Docker编译Android AOSP源码时,如果遇到"Build sandboxing disabled due to nsjail error"的错误,可以通过在docker run命令中添加`--privileged`参数来解决权限不足的问题。
1624 1
Android源码下载
Android源码下载
902 0
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
本文分享了下载AOSP源码的方法,包括如何使用repo工具和处理常见的repo sync错误,以及配置Python环境以确保顺利同步特定版本的AOSP代码。
1240 0
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
本文介绍了如何将基于全志H713芯片的AOSP Android源码导入Android Studio以解决编译和编码问题,通过操作步骤的详细说明,展示了在Android Studio中利用代码提示和补全功能快速定位并修复编译错误的方法。
476 0
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
AI助理

你好,我是AI助理

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