十一、 Android 中的 IPC 方式(3) --- 使用 Messenger

    Messenger 是 Android 中的一种轻量级的 IPC 方案。它可以翻译为信使,是 Android 中的一个类,通过它可以在不同进程间传递 Message 对象,在 Message 中放入我们需要传递的数据,就可以轻松地实现数据的进程间传递了。下面看一下 Messenger 类的源码:

/*
* 可以看到 Messenger 实现了 Parcelable 接口,所以它可以在进程间进行传递。
*/
public final class Messenger implements Parcelable {
    private final IMessenger mTarget;

    /**
     * Messenger 的构造方法,创建一个与目标 Handler 关联的 Messenger 对象。
     */
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }

    /**
     * 发送一个 Message 对象给这个 Messenger 的 Handler,然后由该 Handler 来处理消息。
     */
    public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }

    /**
     * 返回与该 Messenger 相关的 IBinder 对象。
     */
    public IBinder getBinder() {
        return mTarget.asBinder();
    }

    /**
     * 通过参数中的 IBinder 对象创建一个与之相关联的 Messenger 对象。
     */
    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }
}

    通过源码中的 IMessenger 对象以及 IMessenger.asBinder()、IMessenger.Stub.asInterface() 方法可以看到,其实 Messenger 底层实现就是 AIDL,它只是对 AIDL 进行了封装,使得我们使用起来非常简单。同时,由于它一次只处理一个请求,所以服务端不存在并发执行的情形,因此在服务端我们不用考虑线程同步的问题。

    下面看一下它的使用:

eg1:客户端向服务端发送两个 int 型整数,并请求服务端计算两个整型数的结果,但是服务端不返回结果给客户端,只执行计算逻辑。

远程服务端 MessengerService.java:

package com.cfm.messengertest;

/**
* 步骤1. 在服务端创建一个 Service 来处理客户端的连接请求
*/
public class MessengerService extends Service {
    private static final String TAG = "cfmtest";

    /**
     * 步骤2. 创建一个执行客户端的请求的 Handler 类
     */
    private static class MessengerHandler extends Handler {

        // 执行客户端请求的回调方法
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case ClientActivity.MSG_FROM_CLIENT:
                    Log.d(TAG, "接收到了客户端发起的请求!");
                    int result = msg.getData().getInt("a") + msg.getData().getInt("b");
                    Log.d(TAG, "服务端计算两个数相加的结果为:" + result);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * 步骤3: 通过步骤 2 创建一个 Handler 对象,然后创建一个与之相关联的 Messenger
     */
    private final Messenger mMessenger = new Messenger(new MessengerHandler());

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "远程服务绑定成功!");

        /**
         *  步骤4: 获取该 Messenger 对象的底层 Binder,然后通过 Binder 完成进程间通信。
         */
        return mMessenger.getBinder();
    }
}

客户端 ClientActivity.java:

package com.cfm.messengertest;

/**
*  客户端需要绑定远程服务,然后根据服务端返回的 IBinder 对象创建 Messenger 对象并使用此对象向服务端发送消息即可完成进程间通信。
*/
public class ClientActivity extends AppCompatActivity {
    
    private static final String TAG = "cfmtest";
    public static final int MSG_FROM_CLIENT = 1;
    private Messenger mMessenger;
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 根据服务端返回的 IBinder ,创建一个 Messenger 对象
            mMessenger = new Messenger(service);

            // 通过该 Messenger 对象,发送 Message 消息给服务端,服务端即可根据 Message 的信息作相应的处理
            Message msg = Message.obtain();
            msg.what = MSG_FROM_CLIENT;
            Bundle bundle = new Bundle();
            bundle.putInt("a", 333);
            bundle.putInt("b", 555);
            msg.setData(bundle);

            Log.d(TAG, "客户端请求服务端计算 333 和 555 相加的结果!");
            try {
                mMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client);

        // 绑定远程服务
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}

AndroidManifest.xml:

...
<service
    android:name=".MessengerService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote">
</service>
...

Log 打印信息:

客户端:

com.cfm.messengertest D/cfmtest: 客户端请求服务端计算 333 和 555 相加的结果!

服务端:

com.cfm.messengertest:remote D/cfmtest: 远程服务绑定成功!
com.cfm.messengertest:remote D/cfmtest: 接收到了客户端发起的请求!
com.cfm.messengertest:remote D/cfmtest: 服务端计算两个数相加的结果为:888

 

    通过上面的例子可以得出:

    1. 在 Messenger 中进行数据传输必须将数据放入 Message 中,由于 Messenger 和 Message 都实现了 Parcelable 接口,所以它们都可以进行跨进程传输。故 Message 所支持的数据类型就是 Messenger 中所支持的数据传输类型。

    2. Message 中能使用的载体包含 what、arg1、agr2、Bundle 和 replyTo。

    3. Message 中的另一个字段 object 在同一个进程中很实用,但是在进程间通信的时候,在 Android 2.2 以前 object 字段不支持跨进程传输,在 2.2 之后仅支持系统提供的并实现了 Parcelable 接口的对象才能通过它来传输。所以我们自定义的实现了 Parcelable 接口的对象无法通过 object 字段来传输,但是我们可以通过 Bundle 对象来实现。

 

eg2:客户端向服务端发送两个 int 型整数,并请求服务端计算两个整型数的结果,然后服务端将计算的结果返回给客户端。

远程服务端 MessengerService.java:

package com.cfm.messengertest;

/**
* 步骤1. 在服务端创建一个 Service 来处理客户端的连接请求
*/
public class MessengerService extends Service {
    private static final String TAG = "cfmtest";
    public static final int MSG_FROM_SERVER = 2;

    /**
     * 步骤2. 创建一个执行客户端的请求的 Handler 类
     */
    private static class MessengerHandler extends Handler {

        // 执行客户端请求的回调方法
        @Override
        public void handleMessage(Message msg) {

            switch (msg.what) {
                case ClientActivity.MSG_FROM_CLIENT:
                    Log.d(TAG, "接收到了客户端发起的请求!");

                    // 获取客户端发过来的 Messenger 对象,然后通过该对象向客户端回复消息
                    // 此时与这个 messenger 相关联的 handler 就是客户端的 handler
                    Messenger messenger = msg.replyTo;
                    int result = msg.getData().getInt("a") + msg.getData().getInt("b");
                    Bundle bundle = new Bundle();
                    bundle.putInt("result", result);
                    Message replyMsg = Message.obtain(null, MSG_FROM_SERVER);
                    replyMsg.setData(bundle);

                    try {
                        messenger.send(replyMsg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * 步骤3: 通过步骤 2 创建一个 Handler 对象,然后创建一个与之相关联的 Messenger
     */
    private final Messenger mMessenger = new Messenger(new MessengerHandler());

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "远程服务绑定成功!");

        /**
         *  步骤4: 获取该 Messenger 对象的底层 Binder,然后通过 Binder 完成进程间通信。
         */
        return mMessenger.getBinder();
    }
}

客户端 ClientActivity.java:

package com.cfm.messengertest;

/**
* 客户端需要绑定远程服务,然后根据服务端返回的 IBinder 对象创建 Messenger 对象并使用此对象向服务端发送消息即可完成进程间通信。
*/
public class ClientActivity extends AppCompatActivity {

    private static final String TAG = "cfmtest";
    public static final int MSG_FROM_CLIENT = 1;

    private Messenger mMessenger;

    /**
     * 创建一个处理服务端回复的 Handler,在客户端作相应的逻辑处理。
     */
    private static class MessengerHandlerForClient extends Handler {

        @Override
        public void handleMessage(Message msg) {

            switch (msg.what) {
                case MessengerService.MSG_FROM_SERVER:
                    // 处理服务端回复的请求
                    int result = msg.getData().getInt("result");
                    Log.d("cfmtest", "客户端接收到服务端返回的计算结果: " + result);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     *  创建一个与客户端 Handler 相关联的 Messenger 对象
     */
    private Messenger mClientReplyMessenger = new Messenger(new MessengerHandlerForClient());

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            // 根据服务端返回的 IBinder ,创建一个 Messenger 对象
            mMessenger = new Messenger(service);

            // 通过该 Messenger 对象,发送 Message 消息给服务端,服务端即可根据 Message 的信息作相应的处理
            Message msg = Message.obtain();
            msg.what = MSG_FROM_CLIENT;
            Bundle bundle = new Bundle();
            bundle.putInt("a", 333);
            bundle.putInt("b", 555);
            msg.setData(bundle);
            Log.d(TAG, "客户端请求服务端计算 333 和 555 相加的结果!");
            // 服务端可以获取到该 Messenger 对象,然后通过该对象向客户端发送消息
            msg.replyTo = mClientReplyMessenger;
            try {
                mMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client);

        // 绑定远程服务
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}

AndroidManifest.xml:

...
<service
    android:name=".MessengerService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote">
</service>
...

Log 打印信息:

客户端:

com.cfm.messengertest D/cfmtest: 客户端请求服务端计算 333 和 555 相加的结果!
com.cfm.messengertest D/cfmtest: 客户端接收到服务端返回的计算结果: 888

服务端:

com.cfm.messengertest:remote D/cfmtest: 远程服务绑定成功!
com.cfm.messengertest:remote D/cfmtest: 接收到了客户端发起的请求!

最后来一张刚哥的总结图:Messenger 的工作原理图

    综上:

    可以看到通过 Messenger 实现的跨进程通信功能一般,但是支持一对多串行通信(因为它一次只处理一个消息),并且支持实时通信。但是缺点是它不能很好的处理高并发的情形,不支持 RPC(Remote Procedure Call 远程过程调用),因为数据通过 Message 进行传输,因此只能传输 Bundle 支持的数据类型。所以这种 IPC 方式适用的场景为低并发的一对多即时通信,无 RPC 需求或者不需要返回结果的 RPC 需求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值