android进阶——轻量级跨进程传递message利器messenger详解(代码片段)

CrazyMo_ CrazyMo_     2022-12-14     800

关键词:

文章大纲

引言

作为Android 开发者相信我们对于消息机制一定非常熟悉,对于进程内使用Handler处理Message 也一定了如执掌,而如果让你使用最简洁的方式实现进程间通信,也许有相当一部分初学者想到的是用AIDL自己实现,诚然思路是对的,但是还有更简单的机制供你使用。

一、Messenger 概述

Messenger是基于消息Message的传递的一种轻量级IPC进程间通信方式(通过在一个进程中创建一个指向Handler的Messenger,并将该Messenger传递给另一个进程),当然本质就是对Binder的封装(也是通过AIDL实现的 )。通过Messenger可以让我们可以简单地在进程间直接使用Handler进行Message传递,跨进程是通过Binder(AIDL实现),而消息发送是通过Handler#sendMessage方法,而处理则是Handler#handleMessage处理的;当然除了Handler之外还可以是自定义的相关的某些IBinder接口,简而言之,Messenger的跨进程能力是由构造时关联的对象提供的

二、Messenger 源码解析

Messenger 实现了Parcelable接口,意味着自身可以跨进程传递,同时持有IMessenger 接口引用(一个Binder对象)意味着拿到这个Binder对象就可以跨进程使用。Messenger 只是把IMessenger接口包装起来并通过Binder进行跨进程传递,真正的核心能力提供者是IMessenger的实现类——android.os.Handler.MessengerImpl。

package android.os;

/**
 * Reference to a Handler, which others can use to send messages to it.
 * This allows for the implementation of message-based communication across
 * processes, by creating a Messenger pointing to a Handler in one process,
 * and handing that Messenger to another process.
 */
public final class Messenger implements Parcelable 
    private final IMessenger mTarget;

    /**
     * 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();
    

    /**
     * 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);
    
    
    /**
     * Send a Message to this Messenger's Handler.
     * 
     * @param message The Message to send.  Usually retrieved through
     * @link Message#obtain() Message.obtain().
     */
    public void send(Message message) throws RemoteException 
        mTarget.send(message);
    
    
    /**
     * Retrieve the IBinder that this Messenger is using to communicate with
     * its associated Handler.
     * @return Returns the IBinder backing this Messenger.
     */
    public IBinder getBinder() 
        return mTarget.asBinder();
    
    
    public boolean equals(Object otherObj) 
        if (otherObj == null) 
            return false;
        
        try 
            return mTarget.asBinder().equals(((Messenger)otherObj)
                    .mTarget.asBinder());
         catch (ClassCastException e) 
        
        return false;
    

    public int hashCode() 
        return mTarget.asBinder().hashCode();
    
    
    public int describeContents() 
        return 0;
    

    public void writeToParcel(Parcel out, int flags) 
        out.writeStrongBinder(mTarget.asBinder());
    

    public static final Parcelable.Creator<Messenger> CREATOR
            = new Parcelable.Creator<Messenger>() 
        public Messenger createFromParcel(Parcel in) 
            IBinder target = in.readStrongBinder();
            return target != null ? new Messenger(target) : null;
        

        public Messenger[] newArray(int size) 
            return new Messenger[size];
        
    ;

    public static void writeMessengerOrNullToParcel(Messenger messenger,
            Parcel out) 
        out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder()
                : null);
    

    public static Messenger readMessengerOrNullFromParcel(Parcel in) 
        IBinder b = in.readStrongBinder();
        return b != null ? new Messenger(b) : null;
    


1、IMessenger接口

IMessenger是通过AIDL 自动生成的,一般在原生Android系统中I前缀的都是AIDL接口对应的实现类。对应的Messenger.aidl:

package android.os;

parcelable Messenger;

而IMessenger.aidl里就定义了一个入参为Message的方法 send(in Message msg)

package android.os;

import android.os.Message;

/** @hide */
oneway interface IMessenger 
    void send(in Message msg);

IMessenger.aidl对应的AIDL实现类:

public interface IMessenger extends android.os.IInterface 
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements
            android.os.IMessenger 
        private static final java.lang.String DESCRIPTOR = "android.os.IMessenger";

        public Stub() 
            this.attachInterface(this, DESCRIPTOR);
        

        public static android.os.IMessenger asInterface(...

        public android.os.IBinder asBinder() 
            return this;
        

        @Override
        public boolean onTransact(int code, android.os.Parcel data,
                android.os.Parcel reply, int flags)
                throws android.os.RemoteException ...

        private static class Proxy implements android.os.IMessenger ...

    public void send(android.os.Message msg)
            throws android.os.RemoteException;

IMessenger就是一个Binder 接口只提供了一个方法——send 用于跨进程发送消息Message。

2、Messenger 主要方法

2.1、Messenger(Handler target)

 public Messenger(Handler target) 
        mTarget = target.getIMessenger();
    

通过Handler构造Messenger时,就是调用了传入的Handler#getIMessenger()方法得到单例构造的MessengerImpl实例并初始化mTarget。

//Handler#getIMessenger()    
final IMessenger getIMessenger() 
        synchronized (mQueue) 
            if (mMessenger != null) 
                return mMessenger;
            
            mMessenger = new MessengerImpl();
            return mMessenger;
        
    

继续往下追

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

本质就是通过Handler#sendMessage完成通信。

2.2、Messenger(IBinder target)

而通过IBinder对象构造Messenger时,就是把传入的IBinder对象“转成”MessengerImpl实例并初始化mTarget成员变量。

并非简单地直接强转而是先检索,如果已经创建过了就直接返回IBinder对应的代理对象,否则创建对应的代理对象再返回,预知详情请后续关注Binder系列文章。

public Messenger(IBinder target) 
    mTarget = IMessenger.Stub.asInterface(target);    

2.3、send(Message message)

前面分析了send方法本质就是调用Handler#sendMessage方法,这也解释了为什么我们在服务端和客户端都需要创建Handler,因为需要在Handler去处理接收到的消息。

public void send(Message message) throws RemoteException 
        mTarget.send(message);//MessengerImpl#send
    

三、Messenger的使用

Messenger 基于Binder可以跨进程通信,为了方便我简单的把一个进程称之为服务端进程,另一个称之为客户端进程

1、首先在服务端定义一个Messenger对象

Messager.replyTo指向的客户端的Messenger,而Messenger又持有客户端的一个IBinder对象(即MessengerImpl),服务端正是利用这个IBinder对象做的与客户端的通信。

  • 创建一个Handler
  • 使用Handler初始化构建Messenger
package com.crazymo.messenger.rawmessenger;

import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;

public class MessengerService extends Service 
    public final static String TAG = "MessengerIPC";
    public final static String KEY_NAME = "Name";
    public final static String KEY_RESP = "Response";
    public final static int MSG_WHAT_HELLO = 100;
    public final static int MSG_WHAT_RESP = 1001;
    /**
     * 一个用于跨进程的序列化对象,包裹着IMessenger AIDL接口
     */
    private static final Messenger messenger = new Messenger(new MessengerServerHandler());

    private static class MessengerServerHandler extends Handler 
        @Override
        public void handleMessage(Message msg) 
            doHandleMessage(msg);
        
    

    /**
     * 处理其他进程发过来的消息
     * @param msg
     */
    private static void doHandleMessage(Message msg) 
        if (msg != null) 
            String ret = "hello ";
            //接收客户端的消息并处理
            if (msg.what == MSG_WHAT_HELLO) 
                Log.e(TAG, "receive msg from client=" + msg.getData().getString(KEY_NAME));
                try 
                    Thread.sleep(1_000);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
                ret += msg.getData().getString(KEY_NAME);
                //把处理结果封装到Message返回给客户端
                Message reply = Message.obtain(null, MSG_WHAT_RESP);
                Bundle bundle = new Bundle();
                bundle.putString(KEY_RESP, ret);
                reply.setData(bundle);
                try 
                    //msg.replyTo @ android.os.Messenger类型,Messager.replyTo指向的客户端的Messenger,而Messenger又持有客户端的一个Binder对象(MessengerImpl)。服务端正是利用这个Binder对象做的与客户端的通信。
                    if (msg.replyTo != null) 
                        msg.replyTo.send(reply);
                    
                 catch (RemoteException e) 
                    e.printStackTrace();
                
            
         else 
            Log.e(TAG, "handle client empty msg");
        
    

    @Override
    public IBinder onBind(Intent intent) 
        //返回Messenger的IBinder对象,当bindService 执行时候就会触发该回调,就可以拿到服务端的IBinder对象
        return messenger.getBinder();
    

然后就在传入Handler#handleMessage 方法中实现处理消息的逻辑,至此一个远程Service实现完毕。

2、客户端使用Messenger

  • 定义一个Handler 用于发送Message
  • 初始化Messenger对象
package com.crazymo.messenger;

import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import com.crazymo.messenger.aidl.MyMessengerService;
import com.crazymo.messenger.rawmessenger.MessengerService;

public class MainActivity extends AppCompatActivity 
    private final static String TAG="MainActivity";
    /***********************1、Messenger 方式****************************/
    private Messenger mServer;
    private ServiceConnection connMessenger =new ServiceConnection() 
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) 
            mServer=new Messenger(service);//把返回的IBinder对象初始化Messenger
            Log.e(MessengerService.TAG, "MessengerService Connected!");
        

        @Override
        public void onServiceDisconnected(ComponentName name) 

        
    ;

    private final Handler handlerClient =new Handler()
        @SuppressLint("HandlerLeak")
        @Override
        public void handleMessage(Message msg) 
            if(msg!=null && msg.what== MessengerService.MSG_WHAT_RESP)
                String resp=msg.getData().getString(MessengerService.KEY_RESP);
                Log.e(MessengerService.TAG, "resp from server="+resp);
            
        
    ;

    //为了接收服务端的回复,客户端也需要准备一个接收消息的Messenger 和Handler
    private final Messenger clientMessenger=new Messenger(handlerClient);

    private void bindMessengerService() 
        Intent intent=new Intent(this,MessengerService.class);
        bindService(intent, connMessenger, Context.BIND_AUTO_CREATE);

    

    public void sendByMessenger(View view) 
        Message msg=Message.obtain(null,MessengerService.MSG_WHAT_HELLO);
        Bundle data=new Bundle();
        data.putString(MessengerService.KEY_NAME,"CrazyMo_");
        msg.setData(data);
        //Client 发信时指定希望回信人,把客户端进程的Messenger对象设置到Message中
        msg.replyTo=clientMessenger;
        try 
            mServer.send(msg);//跨进程传递
         catch (RemoteException e) 
            e.printStackTrace();
        
    

    /***********************2、MyMessenger AIDL方式****************************/
    private IMyMessenger myInterface;
    private ServiceConnection connAidl = new ServiceConnection() 
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) 
            myInterface = IMyMessenger.Stub.asInterface(service);
            Log.i(TAG, "MyMessenger 连接Service 成功");
        
        @Override
        public void onServiceDisconnected(ComponentName name) 
            Log.e(TAG, "连接Service失败");
        
    ;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindMessengerService();
        跨进程通信之messenger

...局,就可以轻松实现数据的跨进程传递了。Messenger是一种轻量级的IPC方案,其底层实现是AIDL。Messenger的使用方法很简单,它对AIDL进程了封装,并且由于它一次只处理一个请求,因此在服务端我们不需要考虑同步的问题。 2.... 查看详情

android史上最简单易懂的跨进程通讯(messenger)!

不需要AIDL也不需要复杂的ContentProvider,也不需要SharedPreferences或者共享存储文件!只需要简单易懂的Messenger,它也称为信使,通过它可以在不同进程中传递message对象,在message中放入我们需要传递的数据你就可以实现跨进程通讯... 查看详情

精品文章系列

文章目录Android☆跨进程通信☆☆Service☆Java☆多线程☆Flutter☆环境搭建☆☆语法基础☆☆实战进阶☆☆问题总结☆Dart☆环境搭建☆☆语法基础☆☆问题总结☆Android☆跨进程通信☆跨进程通讯的几种方式、作用Messenger实现跨进程... 查看详情

通过 IPC 跨进程传递对 COM 对象的引用?

】通过IPC跨进程传递对COM对象的引用?【英文标题】:PassingareferencetoaCOMobjectaccrossprocessesviaIPC?【发布时间】:2018-03-2008:52:05【问题描述】:我有一个对COM对象的引用,并且想将该引用传递给另一个进程。因此,我需要一些方法... 查看详情

androidaidlservice跨进程传递复杂数据(代码片段)

...黑色的眼睛,我却用它寻找光明~传值方式AIDL是允许跨进程传递值的,一般来说有三种方式:-广播;这种算是比较常见的一种方式了,传递小数据不错-文件;这个是保存到文件中,然后读取,传递... 查看详情

androidaidlservice跨进程传递复杂数据(代码片段)

...黑色的眼睛,我却用它寻找光明~传值方式AIDL是允许跨进程传递值的,一般来说有三种方式:-广播;这种算是比较常见的一种方式了,传递小数据不错-文件;这个是保存到文件中,然后读取,传递... 查看详情

如何在不传递引用的情况下在 Python 中使用 SyncManager 跨进程共享列表

】如何在不传递引用的情况下在Python中使用SyncManager跨进程共享列表【英文标题】:HowdoIsharealistacrossprocesseswithSyncManagerinPythonwithoutpassingreferences【发布时间】:2020-11-2401:08:24【问题描述】:在python中,多处理模块提供了可以在进... 查看详情

android跨进程传大图思考及实现——附上原理分析(代码片段)

1.抛一个问题这一天,法海想锻炼小青的定力,由于Bitmap也是一个Parcelable类型的数据,法海想通过Intent给小青传个特别大的图片intent.putExtra("myBitmap",fhBitmap)如果“法海”(Activity)使用Intent去传递一个大的Bitmap给“... 查看详情

跨进程共享状态变量

】跨进程共享状态变量【英文标题】:Sharingstatevariableacrossprocesses【发布时间】:2012-11-0915:55:24【问题描述】:我想跨进程提供某些信息(比如说一个状态变量-一个布尔值)。该变量的状态由服务维护,其他应用只能读取(不能... 查看详情

android中的跨进程是啥意思

参考技术Aandroid系统中应用程序之间不能共享内存。在不同应用程序之间进行的数据交换叫跨进程。在androidSDK中提供了4种用于跨进程通讯的方式。这4种方式正好对应于android系统中4种应用程序组件:Activity、ContentProvider、Broadcast... 查看详情

android文件存取可以跨进程吗

...指定一个Uri(通过Intent构造方法的第2个参数指定)。在android系统中有很多应用程序提供了可以跨进程访问的Activity,例如,下面的代码可以直接调用拨打电话的Activity。本回答被提问者采纳 查看详情

androidmessenger跨进程通信(代码片段)

1、androidmessager可以实现不同进程之间数据的传递。通过点击进去看messenger类可以看到,我们可以创建一个service用于处理和客户端的连接请求,然后通过handler中创建一个messener对象,实现对service数据传递。/***CreateanewM... 查看详情

如何在android中实现跨进程锁?

】如何在android中实现跨进程锁?【英文标题】:Howtoimplementcrossprocesslockinandroid?【发布时间】:2014-05-2715:31:58【问题描述】:我正在编写一个库项目供多个APP使用。而且由于某种原因,我必须为不同的APP做一个功能互斥,所以我... 查看详情

2路跨进程通信

】2路跨进程通信【英文标题】:2-wayCrossProcessCommunication【发布时间】:2011-10-0401:17:04【问题描述】:我正在做一个项目,我想要一个像System这样的插件沙箱,但是我在解决2-Way实时跨进程通信时遇到了问题。起初我想到了WCF,因... 查看详情

为啥 Android Bound Services 文档建议不能跨进程使用 IBinder?

】为啥AndroidBoundServices文档建议不能跨进程使用IBinder?【英文标题】:WhydoesAndroidBoundServicesdocumentationsuggestIBindercan\'tbeusedacrossprocesses?为什么AndroidBoundServices文档建议不能跨进程使用IBinder?【发布时间】:2017-07-1711:28:20【问题描... 查看详情

cudaSetDevice() 编号是不是跨进程一致?

】cudaSetDevice()编号是不是跨进程一致?【英文标题】:IscudaSetDevice()numberingconsistentacrossprocesses?cudaSetDevice()编号是否跨进程一致?【发布时间】:2019-12-2919:48:15【问题描述】:我想在父进程中调用cudaGetDeviceCount(&amp;N),然后创... 查看详情

处理 Android 自定义内容提供程序中的(跨进程)异常

】处理Android自定义内容提供程序中的(跨进程)异常【英文标题】:Dealingwith(cross-process)exceptionsinAndroidcustomcontentprovider【发布时间】:2012-08-1204:18:44【问题描述】:我的Android应用中有一个自定义内容提供程序,运行良好。我希... 查看详情

——parcelable接口的使用(跨进程,intent传输)

一、Parcelable类(Android独有的)简介:Parcelable是一个接口。作用:是Android提供的序列化接口,实现序列化和反序列化的操作。二、跨进程使用步骤一:创建Book类继承Parcelable接口publicclassBookimplementsParcelable{privateStringmBookName;privateintm... 查看详情