Android 中的 IPC 方式(二) Messenger

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_29874521/article/details/82627395

Messenger 可以翻译为信使,顾名思义,通过它可以在不同进程中传递 Message 对象,在 Message 中加入我们需要传递的数据,就可以轻松地实现数据的进程间传递了。Messenger 是一种清零的 IPC 方案,它的底层实现是 AIDL,下面是 Messenger 的两个构造,从构造方法的实现上我们可以明显看出 AIDL 的痕迹,不管是 Messenger 还是 Stub.asInterface,这种使用方法都表明它的底层是 AIDL。

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

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

Messenger 的使用方法很简单,它对 AIDL 做了封装,是的我们可以更简单的进行进程间通信。同时,由于它一次处理一个请求,因此在服务端我们不用考虑线程同步的问题,这是因为服务端中不存在并发执行的情形。实现一个 Messenger 有如下几个步骤,分为服务端和客户端:

(1)服务端进程

首先,我们需要在服务端创建一个 Service,来处理客户端的连接请求,同时创建一个 Handler 并通过它来创建一个 Messenger 对象,然后在 Service 的 onBind 中返回这个 Messenger 对象底层的 Binder 即可。

(2)客户端进程

客户端进程中,首先要绑定服务端的 Service,绑定成功后用服务端返回的 IBinder 对象创建一个 Messenger,通过 Messenger 就可以向服务端发送消息了,发消息类型为 Message 对象。如果需要服务端能回应客户端,就和服务端一样,我们还需要创建一个 IBinder 并创建一个新的 Messenger,并把这个 Messenger 对象通过 Message 的 replyTo 参数传递给服务端,服务端通过 replyTo 参数就可以回应客户端。首先我们看一个简单的例子,在这个例子中服务端无法回应客户端。

首先看服务端的代码,可以看到 MessengerHandler 用来处理客户端发送的消息,并从消息中取出客户端发来的文本信息。而 mMessenger 是一个 Messenger 对象,它和 MessengerHandler 相关联,并在 onBind 中返回它的 Binder 对象,可以看出,这里 Messenger 的作用是将客户端发送来的消息传递给 MessengerHandler 处理。

public class MessengerService extends Service {

    public final String TAG = this.getClass().getName();

    private class MessengerHandler extends Handler {
       @Override
       public void handleMessage(Message msg) {
           if(msg.what == 1) {
                Log.i(TAG, "receive msg from client:" + msg.getData().getString("msg"));
           }
       }
   }

   private Messenger messenger = new Messenger(new MessengerHandler());

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }

}

当然,不要忘记在 AndroidManifest 中注册:

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

接下来我们看客户端的代码,客户端的实现也比较简单,肯定需要绑定远程服务的 MessengerService,半丁成功后,根据服务端发送的 Binder 对象创建 Messenger 对象并根据这个对象向服务端发送消息,下面代码正在 Bundle 中向服务端发送了一句话,在上面的服务端会打印这句话。

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            messenger = new Messenger(service);
            Message msg = new Message();
            Bundle bundle = new Bundle();
            bundle.putString("msg", "Hello,this is client.");
            msg.setData(bundle);
            msg.what = 1;
            try {
                messenger.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_main);
        bindService(new Intent(this, MessengerService.class), connection, Context.BIND_AUTO_CREATE);
    }

运行之后,我们来看下 log 日志:

09-12 14:33:33.771 23040-23040/com.demo.text.demotext:remote I/com.demo.text.demotext.service.MessengerService: receive msg from client:Hello,this is client.

通过上面的例子可以看出,在 Messenger 中进行数据传递必须将数据放入 Message 中, 而 Messenger 和 Message 都实现了 Parcelable 接口,因此可以跨进程传输。简单来说,Message 所支持的数据类型就是 Messenger 所支持的传输类型。实际上,通过 Messenger 来传输 Message,Message 中使用的载体只有 what、arg1、arg2、Bundle 以及 replyTo。Message 中的另一个字段 object 在同一个进程中是很实用的,但是在进程间通信的时候,在 Android 2.2以前 object 字段不支持进程间通信,即使 2.2 以后,也仅仅是系统提供的实现了 Parcelable 接口的对象才能通过它来传输,这就意味着我们自定义的 Parcelable 对象是无法通过 object 字段来传输的。

上面的例子演示了如何在服务端接收客户端发来的信息,但是有时候我们还需要能够回应客户端,下面介绍如何实现这种效果,还是采用上面的例子,稍微做一下修改,每当客户端发来一条信息,服务端就会自动恢复一套。

首先看服务端的修改,服务端只需要修改 MessengerHandler,当收到消息后,会立即回复一条给客户端:

    private class MessengerHandler extends Handler {
       @Override
       public void handleMessage(Message msg) {
           if(msg.what == 1) {
                Log.i(TAG, "receive msg from client:" + msg.getData().getString("msg"));
                Messenger client = msg.replyTo;
                Message replyMsg = new Message();
                replyMsg.what = 1;
               Bundle bundle = new Bundle();
               bundle.putString("reply", "嗯,你的消息我已经收到,马上回复你。");
               replyMsg.setData(bundle);
               try {
                   client.send(replyMsg);
               } catch (RemoteException e) {
                   e.printStackTrace();
               }
           }
       }
   }

接着再看客户端的修改,为了接收服务端的回复,客户端也需要准备一个接收消息的 Messenger 的 Handler,如下所示

 private Messenger replyMessenger = new Messenger(new MessaengerHandler());

    private class MessaengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            if(msg.what == 1) {
                Log.i(MainActivity.this.getClass().getName(), "receiver msg from service:" + msg.getData().getString("reply"));
            }
        }
    }

除了上述修改,还有关键的一点,当客户端发送消息的时候,需要把接收服务端回复的 Messenger 通过 Message 的 replyTo 参数传递给服务器,如下所示:

      Messenger messenger = new Messenger(service);
            Message msg = new Message();
            Bundle bundle = new Bundle();
            bundle.putString("msg", "Hello,this is client.");
            msg.setData(bundle);
            msg.what = 1;
            try {
                msg.replyTo = replyMessenger;
                messenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }

通过上述修改,我们在运行程序,查看 log 日志:

/com.demo.text.demotext:remote I/com.demo.text.demotext.service.MessengerService: receive msg from client:Hello,this is client.
/com.demo.text.demotext I/com.demo.text.demotext.MainActivity: receiver msg from service:嗯,你的消息我已经收到,马上回复你。

到这里,我们已经把采用 Messenger 实现进程间通信的方式都介绍完了,下面给出一张 Messenger 的工作原理图,方便更好地理解 Messenger,如下所示:

 

猜你喜欢

转载自blog.csdn.net/sinat_29874521/article/details/82627395