美文网首页
Android IPC之Messenger详解

Android IPC之Messenger详解

作者: 雷涛赛文 | 来源:发表于2020-08-31 17:33 被阅读0次

    一.Messenger简介

           与AIDL进程间通信作用一样,Messenger是基于Message对象进行跨进程通信的,类似于Handler发送消息实现子线程和UI线程进行通信,此外,还支持记录客户端对象的Messenger,然后可以实现一对多的通信;甚至作为一个转接处,任意两个进程都能通过服务端进行通信。
           普通的进程间通信,需要服务端自己提供AIDL接口文件,然后本地实现Stub内方法,客户端需要引入方可进行通信;
           Messenger是系统提供统一的AIDL接口文件,并且由Handler来实现Stub内方法,服务端在Handler内处理即可,客户端不需要引入,直接可以通信;

    二.Messenger实现流程

    a.服务端实现

           1.创建一个Handler对象,并实现handleMessage方法,用于接收来自客户端的消息并作处理;
           2.创建一个Messenger(送信人),封装Handler;
           3.用Messenger的getBinder()方法获取一个IBinder对象,通过onBind返回给客户端;
           代码如下:

    public class MyService extends Service {
        private static final String TAG = MyService.class.getSimpleName();
        public MyService() {
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return mMessenger.getBinder();
        }
    
        private MessengerHandler mHandler = new MessengerHandler();
        private Messenger mMessenger = new Messenger(mHandler);
        private static class MessengerHandler extends Handler{
    
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //取出客户端的消息内容
                Bundle bundle = msg.getData();
                String clientMsg = bundle.getString("client");
                Log.i(TAG,"来自客户端的消息:"+clientMsg);
                //新建一个Message对象,作为回复客户端的对象
                Message message = Message.obtain();
                Bundle bundle1 = new Bundle();
                bundle1.putString("server","已收到消息");
                message.setData(bundle1);
                try {
                    msg.replyTo.send(message);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

           服务端接受到客户端的消息,其实与线程间通信一样,也是在handleMessage方法中进行处理;
           如果服务端需要回复客户端,则需要获取到客户端携带过来的Messenger对象(即msg.replyTo),通过msg.replyTo.send()给客户端发送信息。

    b.客户端实现

           1.绑定服务,创建ServiceConnection并在其中使用IBinder将Messenger实例化;
           2.使用Messenger向服务端发送消息;
           3.接收服务端发送过来的message;
           4.解绑服务;
           实现如下:

    public class MyActivity extends Activity implements View.OnClickListener {
        private static final String TAG = MyActivity.class.getSimpleName();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Button bindService = findViewById(R.id.bindService);
            Button unBindService = findViewById(R.id.unBindService);
            bindService.setOnClickListener(this);
            unBindService.setOnClickListener(this);
        }
    
        public void onClick(View view) {
            switch (view.getId()){
                case R.id.bindService:
                    Intent intent = new Intent();
                    intent.setAction("com.hly.learn.server.action");
                    bindService(intent,mConnection,BIND_AUTO_CREATE);
                    break;
                case R.id.unBindService:
                    unbindService(mConnection);
                    break;
               default:
                    break;
            }
        }
    
        private ServiceConnection mConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                //获取服务端关联的Messenger对象
                Messenger mService = new Messenger(service);
                //创建Message对象
                Message message = Message.obtain();
                Bundle bundle = new Bundle();
                bundle.putString("client","hello");
                message.setData(bundle);
                try {
                    mService.send(message);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
            }
        };
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
        }
    }
    

           客户端在绑定服务之后,在ServiceConnection中通过IBinder得到Messenger对象(mService),然后用mService的send方法把Message作为形参发送给服务端。

    双向通信实现

           如果服务端收到消息需要回复客户端,应该怎么实现呢?
           客户端也需要创建一个Messenger对象,接着创建一个对应的处理Handler对象,最后在onServiceConnected方法中,把Messenger对象赋值给message.replyTo,通过mService.send(message)方法发送给服务端,服务端可以通过replyTo的messenger对象给客户端发消息,从而完成双向通信,实现如下:

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //获取服务端关联的Messenger对象
            Messenger mService=new Messenger(service);
            //创建Message对象
            Message message = Message.obtain();
            Bundle bundle = new Bundle();
            bundle.putString("client","hello");
            message.setData(bundle);
            //在message中添加一个回复mReplyMessenger对象,server端可通过此来给client发消息
            message.replyTo = mReplyMessenger;
            try {
                mService.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };
    
    private ReplyHandler mReplyHandler = new ReplyHandler();
    private Messenger mReplyMessenger = new Messenger(mGetReplyHandler);
    public static class ReplyHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Bundle bundle = msg.getData();
            String serviceMsg = bundle.getString("server");
            Log.i(TAG, "来自服务端的回复:"+serviceMsg);
        }
    }
    

           服务端在AndroidManifest.xml中对service进行配置:

    <service android:name=".model.MyService"
         android:exported="true"
         <intent-filter>
              <action android:name="com.hly.learn.server.action" />
         </intent-filter>
    </service>
    

           其实Messenger底层也是AIDL。客户端和服务端通讯,就是普通的AIDL,客户端实例化Stub之后,通过Stub的send方法把消息发到服务端。服务端和客户端通讯:服务端通过解析message的replyto,获得客户端的Stub,然后通过send方法发送到客户端。

    三.源码分析

           从上面实例使用可以看到,在Service的onBind()方法中通过mMessenger.getBinder()来返回对应的IBinder,先看一下Messenger的源码实现:

    a.Messenger.java

    public final class Messenger implements Parcelable {
        private final IMessenger mTarget;
    
        public Messenger(Handler target) {
            mTarget = target.getIMessenger();
        }
    
       public void send(Message message) throws RemoteException {
            mTarget.send(message);
        }
    
       public IBinder getBinder() {
            return mTarget.asBinder();
        }
        ......
        ......
        public Messenger(IBinder target) {
            mTarget = IMessenger.Stub.asInterface(target);
        }
    }
    

           从Messenger的源码可以看到,主要有四个方法:
           1.提供给服务端的构造方法,需要传入Handler实例,在方法内部通过Handler返回IMessenger实例;
           2.提供给客户端的send()方法,发送message到服务端;
           3.提供给服务端在onBind()返回Binder的方法;
           4.提供给客户端在onServiceConnected()内部获取Messenger实例的方法;
           Messenger内部同时提供了服务端和客户端对应的方法,getBinder()是通过mTarget来返回的,mTarget是通过Handler的getIMessenger()来返回的,具体实现是在Handler内部,一起看一下Handler的内部对应的实现:

    b.Handler.java

        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);
            }
        }
    

    c.IMessenger.aidl

    oneway interface IMessenger {
        void send(in Message msg);
    }
    

    d.IMessenger.java

    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";
            /** Construct the stub at attach it to the interface. */
            public Stub() {
                this.attachInterface(this, DESCRIPTOR);
            }
            ........
            ........
            @Override 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 {
                switch (code)
                    ..........
                    case TRANSACTION_send: {
                        data.enforceInterface(DESCRIPTOR);
                        android.os.Message _arg0;
                        if ((0!=data.readInt())) {
                            _arg0 = android.os.Message.CREATOR.createFromParcel(data);
                        } else {
                            _arg0 = null;
                        }
                        this.send(_arg0);
                        return true;
                    }
             }
             return super.onTransact(code, data, reply, flags);
        }
    
        private static class Proxy implements android.os.IMessenger {
            private android.os.IBinder mRemote;
            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }
            @Override public android.os.IBinder asBinder() {
                return mRemote;
            }
            .........
           @Override public void send(android.os.Message msg) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                   if ((msg!=null)) {
                       _data.writeInt(1);
                       msg.writeToParcel(_data, 0);
                   } else {
                       _data.writeInt(0);
                   }
                    mRemote.transact(Stub.TRANSACTION_send, _data, null, android.os.IBinder.FLAG_ONEWAY);
                 } finally {
                     _data.recycle();
                 }
            }
        }
            static final int TRANSACTION_send = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        }
        public void send(android.os.Message msg) throws android.os.RemoteException;
    }
    

           从上面可以看到,getIMessenger()返回的是MessengerImpl实例,而MessengerImpl是继承了IMessenger.Stub,实现了send()方法来接收消息,从这里可以看到,用的是跟AIDL相同的处理;

    总结一下

           服务端:service的onBind()方法调用Messenger.getBinder(),最终是通过IMessenger.Stub的asBinder()返回的IBinder;
           客户端:onServiceConnected()里面调用Messenger mService=new Messenger(service)来返回Messenger对象,在构造方法内部会先通过IMessenger.Stub.asInterface(target)来创建IMessenger实例mTarget;
           交互:客户端在通过send()发送消息时,会通过mTarget的send()方法,经过binder驱动处理,会调用到IMessenger.Stub的onTransact()方法,最终会调用服务端的MessengerImpl的send()方法,继而通过Handler来sendMessage(),最后服务端的Handler来handleMessage();

    四.与 AIDL 比较:

           执行 IPC 时,使用 Messenger 要比使用 AIDL 实现更加简单,因为 Messenger 会将所有服务调用排入队列,而纯粹的 AIDL 接口会同时向服务发送多个请求,服务随后必须应对多线程处理。
           对于大多数应用,服务不需要执行多线程处理,因此使用 Messenger 可让服务一次处理一个调用。如果服务必须执行多线程处理,则应使用 AIDL 来定义接口。

    相关文章

      网友评论

          本文标题:Android IPC之Messenger详解

          本文链接:https://www.haomeiwen.com/subject/batpsktx.html