Android 中的进程通信:Messenger

AIDL 不同的是,AIDL 必须同时应对若干个请求,这就必然会发生线程安全问题,Android 还为我们提供了另外一种无须考虑线程安全问题的 IPC 方法:Messenger

和 AIDL 同时应对若干进程的请求不同的是,Messenger 会将所有的请求排入队列当中,所以不会存在并发的情况,自然也就无须考虑线程安全问题了。

使用 Messenger 也很简单:

接收端:

  • 服务创建一个 Handler,由这个 Handler 来接受来自发送端的信息
  • 创建一个 Messenger 对象(Messenger 引用 Handler 对象)
  • Messenger 创建 Binder 对象
  • 服务的 onBind 方法将 Binder 对象返回到发送端

发送端:

  • 发送端使用 IBinderMessenger 实例化,然后调用 Messenger 对象的 send 方法将 Message 对象发送给接收端

example:

接收端:

public class MyService extends Service {

    /**
     * 第一步:创建一个 Handler
     */
    private static class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.d("TTT", "发送端传递过来的消息是:" + msg.getData().getString("Message"));
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    /**
     * 第二步:创建一个 Messenger,该 Messenger 包含对 Handler 的引用
     */
    private Messenger messenger = new Messenger(new MyHandler());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //第三步:messenger 创建 IBinder 对象
        //第四步:在 onBind 方法中奖 IBinder 对象返回
        return messenger.getBinder();
    }
}

发送端:

public class MainActivity extends Activity {

    private static final int CLIENT_TO_SERVER = 100;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button bind = findViewById(R.id.get);
        bind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setComponent(new ComponentName("com.lixyz.messengerserver", "com.lixyz.messengerserver.MyService"));
                bindService(intent, conn, BIND_AUTO_CREATE);
            }
        });
    }

    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Messenger messenger = new Messenger(service);
            Bundle bundle = new Bundle();
            bundle.putString("Message", "这是给接收端的一封信");
            Message message = Message.obtain();
            message.what = CLIENT_TO_SERVER;
            message.setData(bundle);
            try {
                messenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
}

当然,不要忘记将接收端的 Service 暴露出去:

        <service
            android:name=".MyService"
            android:exported="true" />

这样,一个简单的单项跨进程通信就完成了。

如果想要双向通信,也就是说,如果发送端也想要接受接收端发来的消息的话,可以设置 Message 对象的 replyTo 参数,将发送端 Messenger 传递过去,这样一来,接收端也就可以调用 Messenger 的 send 方法向发送端发送消息了。同样的,发送端也需要一个 Handler 对象来接收接收端回复过来的消息。

简单修改一下代码:

接收端:

    private static class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.d("TTT", "发送端传递过来的消息是:" + msg.getData().getString("Message"));
            Messenger messenger = msg.replyTo;
            Bundle bundle = new Bundle();
            bundle.putString("Message","这是接收端给发送端的回函");
            Message message = Message.obtain();
            message.setData(bundle);
            try {
                messenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

发送端:

    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Messenger messenger = new Messenger(service);
            Bundle bundle = new Bundle();
            bundle.putString("Message", "这是给接收端的一封信");
            Message message = Message.obtain();
            message.what = CLIENT_TO_SERVER;
            message.setData(bundle);
            //添加这一句,将 messenger 传递给 接收端
            message.replyTo = messenger;
            try {
                messenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    
    //再创建一个 Handler 来接收接收端回传过来的信息
    private static class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.d("TTT", "接收端给发送端的回函是:" + msg.getData().getString("Message"));
        }
    }

至于如何区别信息,给 Message 设置 what 参数就可以啦

代码解析

先从第一步 Messenger 的创建看起来:

    /**
     * 创建一个指定 Handler 的 Messenger。
     * 通过这个 Messenger 发送的 Message 对象,都将在  Handler 呈现
     * 就像直接调用 Handler 的 sendMessage 方法一样
     */
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }

Messenger 的构造方法保存了 Handler 中 getIMessenger 方法的返回值,看一下这个方法:

    final IMessenger getIMessenger() {
        synchronized (mQueue) {
            if (mMessenger != null) {
                return mMessenger;
            }
            mMessenger = new MessengerImpl();
            return mMessenger;
        }
    }

这个方法的逻辑也很简单,判断 mMessenger 是否为 null,如果为 null,则创建一个再返回,可以看到,Messenger 的构造方法中,mTarget 实际上保存的是一个 MessengerImpl 对象,看一下这个 MessengerImpl

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

看到这里似乎有点儿熟悉,MessengerImpl 继承自 IMessenger.Stub,还记不记得 AIDL 中的相关套路?没错儿,和 AIDL 一模一样,源码当中也存在一个 IMessenger.aidl 接口,位于:platform_frameworks_base\core\java\android\os,其内容为:

package android.os;

import android.os.Message;

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

也就是说,我们创建 Messenger 对象,实际上是获取了一个 IBinder 对象,而这个对象中实现了 send 方法。至此,接收端的第一步和第二步工作内容明确了,接着看第三步和第四步,更简单了:

    /**
     * 检索与该 Messenger 关联的 Handler 通信的 IBinder
     */
    public IBinder getBinder() {
        return mTarget.asBinder();
    }

还记得 AIDL 文件由系统自动生成的同名 Java 文件不?里面的 asBinder 方法,实际上就是将自身返回了。

接收端的代码也就是这样了。

再看发送端这边,参数为 IBinder 的 Messenger 构造方法:

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

是不是 AIDL 一样?接下来就是调用 send 方法了:

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

还是和 AIDL 一样,其实就是调用了刚刚代理类中的的 send 方法把 Message 传进去。这里面的逻辑其实就是通过代理类中的 IBinder 对象来远程调用接收端中已经实现的 send 方法。

如果要双向通信,发送端通过 replyTo 参数将 Messenger 传递过去,这样接收端同样也可以利用这个 Messenger 来传递消息,接收端通过 Handler 来接收接收端回传的消息了。

Messenger 和 AIDL 的异同

首先说相同点:

  • 二者都可以是用于跨进程通信(不跨进程也可以使用,但是属于脱裤子放屁,直接绑定本地服务更简单直接)
  • Messenger 本质上也是 AIDL,只不过对 AIDL 实行了封装,只不过是由 Handler 接收处理消息
  • 二者使用时,都需要考虑执行时间,最好将之放入子线程,以免造成 ANR

不同点:

  • 使用 AIDL 需要考虑线程安全问题,而 Messenger 由 Handler 进行处理,Handler 内部维护着一个 quene,消息处理按照 quene 中的顺序进行,无须考虑线程安全问题。