Android IPC 之 Messenger 笔记整理

知识前提:Binder 理论部分笔记整理,以及 AIDL 的相关知识。

参考文章:《Android 艺术探索》


1、概述

Android IPC 的途径之一就是使用 Messenger, 它可以在不同进程中传递 Message 对象,在 Message 中放入需要传递的数据,就可以实现在进程间传递。

Messenger 底层基于 AIDL 实现,是一个轻量级的 IPC 方案。

而且 Messenger 只适合传递消息,不能跨进程调用服务端的方法。

2、具体实现
2.1 实现步骤

假设有两个进程 A、B,如果要实现从 A 中发送 messge 到 B 中,则需要先在 B 中创建一个 Handler 对象,并使用这个对象来创建一个 Messenger 对象,然后调用该 Messenger 对象的 getBinder() 方法,将其内部持有 binder 对象的引用传递给进程 A。

// Messenger.java
public Messenger(Handler target) {
	// 得到 Handler 内部的 binder 对象的引用
    mTarget = target.getIMessenger();
}

// Handler.java
final IMessenger getIMessenger() {
    synchronized (mQueue) {
        if (mMessenger != null) {
            return mMessenger;
        }
        // MessengerImpl 时机上是基于 AIDL 的 binder 对象
        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);
    }
}

进程 A 得到上述 binder 对象的远程引用后,利用它在进程 A 中创建另外一个 Messenger 对象。

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

之后,就利用在 A 进程中的那个 Messenger 对象的 send() 方法发送 Message 到 B 进程中来。实际上是利用在 A 进程中的远程 biner 对象引用来发送 msg。

public void send(Message message) throws RemoteException {
    mTarget.send(message);
}

然后,就可以在 B 进程中的 handler.handleMessage() 中处理从 A 进程中发送来的消息。

如果要实现在 B 中返回消息给 A,则在 A 中也要单独维护另外的 Handler 与 Messenger 对象(与之前通过远程 binder 引用创建的 Messenger 不同)。然后在给 B 进程发送 Message 的时候,将维护的 Messenger 对象赋值给 Message#replyTo 成员变量。(因为 Messenger 也实现了 Parcelable,因此可以在进程中传递。)

而 B 进程在处理从 A 发送的消息的时候,就可以用从 Message 中取出从 A 中传递来的 Messenger 对象,利用该对象给 A 回复消息。

2.2 演示代码

可能上面文字说得比较绕,可以看看具体的代码,更加容易理解。

代码来自 《Android 艺术探索》,其中 B 进程就是 Service 所处进程,A 进程为 Activity 所处进程。

public class MessengerService extends Service {

    private static final String TAG = "MessengerService";

    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MyConstants.MSG_FROM_CLIENT:
            	// 处理从 A 进程中发送来的 message
                Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));

				// 获得 A 进程发送 msg 时附带的维护在 A 中的 Messenger 对象,
				// 该对象是基于序列化与反序列化,在 binder 底层实现传递的,
				// 因此其会持有 A 中维护的 Messenger 对象内部的 binder 本地对象的远程引用。
                Messenger client = msg.replyTo;
                Message relpyMessage = Message.obtain(null, MyConstants.MSG_FROM_SERVICE);
                Bundle bundle = new Bundle();
                bundle.putString("reply", "嗯,你的消息我已经收到,稍后会回复你。");
                relpyMessage.setData(bundle);
                try {
                	// 利用 client 给 A 进程发送信息
                    client.send(relpyMessage);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            default:
                super.handleMessage(msg);
            }
        }
    }
	
	// 根据 MessengerHandler 实例化对应的 Messenger,即维护一个 Messenger 对象
    private final Messenger mMessenger = new Messenger(new MessengerHandler());

    @Override
    public IBinder onBind(Intent intent) {
    	// 其他进程与该 Service 绑定的时候,就可以获得 B 进程的 Messenger 内部
    	// 持有的本地 binder 对象的远程引用
        return mMessenger.getBinder();
    }
}
public class MessengerActivity extends Activity {

    private static final String TAG = "MessengerActivity";

    private Messenger mService;

	// 也想 B 进程一样维护一个 Messenger 对象
    private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
    
    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MyConstants.MSG_FROM_SERVICE:
                Log.i(TAG, "receive msg from Service:" + msg.getData().getString("reply"));
                break;
            default:
                super.handleMessage(msg);
            }
        }
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
        	// 基于 B 进程的维护的 Messenger 对象的内部的 binder 对象的远程引用实例化
        	// 对应的 Messenger,该 Messenger 对象与在 A 中维护的 Messenger 对象是两回事
            mService = new Messenger(service);
            Log.d(TAG, "bind service");
            Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
            Bundle data = new Bundle();
            data.putString("msg", "hello, this is client.");
            msg.setData(data);
           	// 将 A 中维护的 Messenger 对象附属在 msg.replyTo 上
            msg.replyTo = mGetReplyMessenger;
            try {
            	// 给 B 进程发送消息
                mService.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        public void onServiceDisconnected(ComponentName className) {}
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }
    
    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }
}

需要注意,Service 在 AndroidMenifest.xml 中注册的时候,使用 process:name 设置其处于与 Activity 不同的进程。


3、补充内容
  1. 因为是使用 Handler 来处理消息,因此一次只能处理一个消息请求,不能支持并发处理的情形。

  2. 支持一对多串行通信,只要能获取到目标进程维护的对应的 Messenger 内部持有的 binder 对象的引用即可。

  3. 使用 Messenger 的场景并不局限于 Activity 与 Service 的场景,关键在于能够获取到目标进程维护的对应的 Messenger 内部持有的 binder 对象的引用,通过该 binder 对象的引用,就实现了与目标进程的关联。只不过刚好 Service 在系统层面上实现了该 binder 引用的传递,即可以通过绑定 Service 来实现获取 binder 引用,因此方便了我们的使用,而不用我们再单独实现 binder 引用传递这部分的逻辑,而且传递的逻辑暂时没有头绪。

  4. 使用 Messenger 是单向通信的,即如果要从 A 往 B 发送消息,则 B 中要维护一个本地的 Messenger 对象,然后在 A 中还要基于进程 B 维护的对应的 Messenger 内部持有的 binder 对象的引用再实例化一个 Messenger 对象,这样,总共有两个 Messenger 对象,然后只能从 A 往 B 发送消息。

    而如果还要实现从 B 往 A 发送消息,则在 A 中也要维护一个 Messenger 对象,当然,此时无需再将 A 中维护的 Messenger 内部持有的 binder 对象的引用传递给 B,而是直接将 A 中维护的 Messenger 对象附属到发送到 B 的 Message 上即可。这样,实现从 B 反过来往 A 通信,又多了两个 Messenger 对象。注意,这里是多了两个,A 进程维护的一个,传递到 B 中反序列化的又是一个,别把这个反序列化与 A 维护的给看作同一引用,这里是跨进程的,反序列化出来的是一个新的处于 B 进程的对象。

    因此,总共维护了两组,4 个 Messenger 对象。

  5. 数据通过 Message 进行传输,因此只能传输 Bundle 支持的数据类型。

    Message 中能够使用的载体只有 what(int)、arg1(int)、arg2(int)、data(Bundle 类型) 以及 replyTo(Messenger 类型),另一个字段 object 在同一进程中实用,但是在 Android 2.2 之前无法跨进程传输,在 2.2 以后,也只支持 系统提供的且实现了 Parcelable 的对象 才支持跨进程传输,而自定义的实现了 Parcelable 的对象没有支持。

    总共 5 个载体,支持的范围即 Bundle 支持的数据类型,且因为 Bundle 支持 Serializable 的(包括自自定义的),因此 Messenger 在通过 data 字段传输数据的时候是支持 Serializable 的(包括自自定义的)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值