安卓开发学习之Messenger实现IPC

时间:2021-05-04 16:44:13

背景

这几天在学习安卓的进程间通信方式,方学AIDL,又看Messenger。

Messenger可以和Handler结合,从而使IPC就像线程间通信一样

而Messenger的实现原理,其实也是用的AIDL,如果对AIDL不甚熟悉,可以参见我的文章安卓开发学习之AIDL的使用


使用步骤

1、在服务端建立Service

内部构造一个messenger,做为接收客户端来信的信使,代码如下

public class MessengerService extends Service {
    private Messenger messenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0:
                    try {
                        Bundle content = (Bundle) msg.obj;
                        String info = content.getString("info", "nothing");
                        System.out.println(info);
                        Message replyMesg = Message.obtain();
                        replyMesg.what = 0;
                        content.clear();
                        content.putString("info", "Got it,service pid:" + Process.myPid());
                        replyMesg.obj = content;
                        msg.replyTo.send(replyMesg); // replyTo也是一个Messenger
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }
    });

    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder(); // 一会儿将看到,getBinder()返回的是一个Stub对象
    }
}

在清单文件中注册,把name填成:remote,让service做为新的进程,代码如下

        <service android:name=".MessengerService"
            android:exported="true"
            android:process=":remote">
            <intent-filter>
                <action android:name="com.example.songzeceng.Messenger"></action>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>


2、客户端建立连接

建立两个Messenger,ServiceMessenger用来向服务端发送数据,ClientMessenger用来从服务端接收数据,代码如下

public class MainActivity extends Activity {
    public static final String TAG = "MainActivity";

    private boolean isConnected = false;
    private Messenger serviceMessenger;
    private Messenger clientMessenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0:
                    Bundle content = (Bundle) msg.obj;
                    String info = content.getString("info", "nothing");
                    logger(info);
                    break;
            }
        }
    });

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            try {
                System.out.println(service.getClass().getCanonicalName());
                serviceMessenger = new Messenger(service); // 此时ServiceMessenger内部负责传递的则是Proxy类
                Message msg = Message.obtain();
                msg.what = 0;
                // msg.obj = "客户端来信,pid:" + Process.myPid(); // IPC时传递的数据类型必须实现Parcelable接口,然而String没有实现
                Bundle bundle = new Bundle();
                bundle.putString("info", "客户端来信,pid:" + Process.myPid());
                msg.obj = bundle;
                msg.replyTo = clientMessenger;
                serviceMessenger.send(msg);

                isConnected = true;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

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

    private void logger(String s) {
        Log.i(TAG, s);
    }

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

    @Override
    protected void onStart() {
        super.onStart();
        if (!isConnected) {
            Intent intent = new Intent(this, MessengerService.class);
            intent.setAction("com.example.songzeceng.Messenger");
            bindService(intent, connection, BIND_AUTO_CREATE);
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (isConnected) {
            unbindService(connection);
            isConnected = false;
        }
    }
}


运行结果

服务端截图

安卓开发学习之Messenger实现IPC

客户端截图

安卓开发学习之Messenger实现IPC

可见,实现了进程间的通信


源码分析

1、服务端

从服务端开始,进行源码分析

我们先是构造了一个Messenger对象,给构造方法传入一个Handler对象,Messenger的这个构造方法代码如下

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

mTarget是IMessenger类型的,这个IMessenger就是用aidl生成的接口类,而后面调用了Handler的getIMessenger()方法,这个方法源码如下

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

返回的是一个MessengerImpl对象,MessengerImpl是Handler的内部类,源码如下

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

相当于IMessenger.Stub对象,这似乎也是Stub的唯一子类,所以我也认为Stub的send()方法就是最终调用了handler的sendMessage()方法


好,返回Messenger的构造方法,服务端的Messenger里的mTarget就是一个MessengerImpl对象,也是一个Stub对象

之后客户端连接service,服务端调用onBind(),代码如下

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

调用了messenger.getBinder(),此方法代码如下

    public IBinder getBinder() {
        return mTarget.asBinder();
    }

调用的是mTarget.asBinder(),我方才说过,mTarget是一个IMessenger.Stub,这个类的源码如下

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


        /**
         * Cast an IBinder object into an android.os.IMessenger interface,
         * generating a proxy if needed.
         */
        public static android.os.IMessenger asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = (android.os.IInterface) obj
                    .queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof android.os.IMessenger))) {
                return ((android.os.IMessenger) iin);
            }
            return new android.os.IMessenger.Stub.Proxy(obj);
        }


        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 INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                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);
        }

        static final int TRANSACTION_send = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }


    public void send(android.os.Message msg) throws android.os.RemoteException;
}

asBinder()方法返回的是Stub自己,所以messenger.getBinder()方法,返回的是handler里面的IMessengerImpl对象,也就是Stub对象


2、客户端

客户端直接看onServiceConnected()里的内容,上来根据服务端onBind()方法返回的IBinder对象,构造serviceMessenger,这个构造方法代码如下

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

翻翻上面的Stub的asInterface()方法,会发现如果客户端本地没有IMessenger接口对象,就返回Proxy对象,而Proxy对象里的mRemote,就是服务端返回的IBinder,也就是一个Stub;但如果已经有了IMessenger实现类对象,就返回那个对象

现在,虽然之前在客户端我们实例化了一个ClientMessenger,但它里面的handler的IMessengerImpl和服务端返回的IMessengerImpl是两个对象,所以asInterface()还是返回的是Proxy对象

通过反射获取serviceMessenger.mTarget类型的结果如下

安卓开发学习之Messenger实现IPC

所以当我们通过serviceMessenger发送消息时,调用的是Proxy的send()方法,通过clientMessenger接收消息时,调用的是Stub的send()方法

Proxy的源码如下

        private static class Proxy implements android.os.IMessenger {
            private android.os.IBinder mRemote;


            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }


            public android.os.IBinder asBinder() {
                return mRemote;
            }


            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }


            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); // Message实现了Parcelable接口
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_send, _data, null,
                            android.os.IBinder.FLAG_ONEWAY);
                } finally {
                    _data.recycle();
                }
            }
        }
所以,综上所述,serviceMessenger.send()调用的是Proxy.send(),然后经过onTransact调用的是Service端的messenger.handler.IMessengerImpl.send()方法;服务端的message.reply.send()则是调用了客户端clientMessenger.handler.IMessengerImpl.send()方法。本质都是Stub在发送,都是AIDL的封装