基础Android知识Android开发

IPC之Messenger

2017-05-18  本文已影响65人  juexingzhe

今天我们来聊一聊Android中另外一种IPC机制-Messenger,可以理解成信使,通过它可以在不同的进程中传递Message对象,其实它的底层实现还是AIDL,为什么这么说?先卖个关子,继续往后看就知道了。我们先看下Messenger怎么使用。还是通过一个栗子来说明,客户端向服务端发起请求,服务端返回一个百度的url,客户端再进行加载,就是这么简单。

1.png 2.png 3.png

当然在这之前有点AIDL的知识看起来会比较轻松,可以参考我前面的AIDL的博客:http://www.jianshu.com/p/4ebd4783d3d9

Messenger基本使用

当然还是分成服务端进程和服务端进程

服务端进程

首先创建一个Service来处理客户端的连接请求,创建一个Messenger,在onBind中返回这个Messenger对象底层的Binder给客户端即可。
通过Handler来创建Messenger,在收到客户端发过来的请求时会回送一个msg:clientMessenger.send(response);

private Messenger messenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            try {
                Thread.sleep(5 * 1000); //模拟耗时
            } catch (Exception e) {
                e.printStackTrace();
            }

            Message response = Message.obtain();
            Bundle bundle = new Bundle();
            bundle.putString("body", REMOTE_URL);
            response.setData(bundle);

            Messenger clientMessenger = msg.replyTo;
            try {
                clientMessenger.send(response);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    });

客户端需要在清单中进行注册,在另外一个juexingzhe进程

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

在客户端绑定服务的时候返回这个Messenger对象。

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

客户端进程

客户端首先发起绑定服务的请求,在服务绑定成功后,通过服务端返回的IBinder对象就可以创建一个Messenger mSender,通过它就可以向服务端发送信息了。

bindService(intent, serviceConnection, BIND_AUTO_CREATE);

ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mSender = new Messenger(iBinder);
            sendMessage();
        }

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

客户端还需要创建一个Handler,通过Handler来创建一个Messenger,用来接收服务端发送的消息:

private Messenger mReceiver = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.i(TAG, "msg is received from service");

            Bundle data = msg.getData();
            if (null != data) {
                mButton.setVisibility(View.GONE);
                String body = data.getString("body");
                mWebView.loadUrl(body);
            }
        }
    });

Messenger源码分析

使用还是比较简单的,基本和Handler的用法比较类似。我们下面来扒一扒源码。

1.构造

我们在上面的例子中用到了两个Messenger的构造函数:
一个是在服务绑定成功后mSender = new Messenger(iBinder);
另外一个是在创建的时候private Messenger mReceiver = new Messenger(new Handler())
我们先看下通过Binder的构造函数,可以看见浓浓的AIDL的味道吧。在夸进程通信的情况下,通过asInterface会将返回服务端Binder的代理Proxy。

    /**
     * Create a Messenger from a raw IBinder, which had previously been retrieved with {@link #getBinder}.
     * 
     * @param target The IBinder this Messenger should communicate with.
     */
    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

我们再看下通过Handler的构造函数,只有一行代码,就是获取mTarget对象。

    /**
     * Create a new Messenger pointing to the given Handler.  Any Message
     * objects sent through this Messenger will appear in the Handler as if
     * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
     * been called directly.
     * 
     * @param target The Handler that will receive sent messages.
     */
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }

上面两个构造函数无一例外都是获得mTarget对象,它是一个IMessenger对象
private final IMessenger mTarget;

IMessenger就是Android系统帮我们定义在android.os包下的一个AIDL接口,编译后会生成IMessenger.java文件。

package android.os;

import android.os.Message;

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

因此mTarget有两种情况,一种是通过Handler或的IMessenger:

    mTarget = Handler.getIMessenger();
    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);
        }
    }

一种是通过IBinder转化得到的IMessenger:

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

public static android.os.IMessenger asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin =obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof android.os.IMessenger))) {
            return ((android.os.IMessenger) iin);
        }
        return new android.os.IMessenger.Stub.Proxy(obj);
}

2.发送消息

Messenger发送消息的源码,都是通过mTarget进行发送。

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

发送消息要分成两种情况:

给服务端发送消息

一种是客户端给服务端发送消息(这种情况的Messenger是客户端通过服务端返回的IBinder进行构造),

    private void sendMessage() {
        if (null != mSender) {
            Message msg = Message.obtain();
            msg.replyTo = mReceiver;
            mSender.send(msg);
        }
    }

这种情况下会的调用流程会是这样的(注意前方高能,可能会引起轻微不适):
Proxy.send()--->mRemote.transact(Stub.TRANSACTION_send, _data, null, android.os.IBinder.FLAG_ONEWAY)--->Stub.onTransact--->this.send(_arg0)(这里面的arg0就是msg)--->Handler中MessengerImpl.send()--->handler.sendMessage--->服务端handleMessage
上面这个过程会比较绕:
1.因为和服务端通信是跨进程,所以首先会在Proxy中序列化msg参数(包括bundle和Messenger),然后会调用到服务端进程的onTransact;

2.在服务端进程中会调用this.send,那这边的this指的是哪个对象?还记得服务端Messenger是怎么创建的吗?也是通过Handler创建的,因此this.send就会调用服务端Messenger的Handler中内部类MessengerImpl中的send方法;

3.MessengerImpl中的send方法会调用Handler的sendMessage方法

Handler.this.sendMessage(msg);

明显这里的Handler就是服务端构建Messenger我们传进去的handler,因此msg也就被handleMessage处理。

给客户端发送消息

这种情况下会拿到客户端发送过来的Messenger,那么为什么服务端可以直接拿到Messenger就给客户端发送消息,不是应该先拿到一个客户端的Binder,然后再转化balabalabalabala吗?

Messenger clientMessenger = msg.replyTo;
clientMessenger.send(response);

其实Android也是这样做的,我们去一探究竟(注:下方源码是我经过删减的):
在客户端send消息给服务端时会,在Proxy的send方法中会将msg和clientMessenger一起会调用Message msg.writeToParcel(_data, 0)写到data中;

public void send(android.os.Message msg) throws android.os.RemoteException {
     android.os.Parcel _data = android.os.Parcel.obtain();
     _data.writeInterfaceToken(DESCRIPTOR);
     if ((msg != null)) {
               _data.writeInt(1);
               msg.writeToParcel(_data, 0);
     }
}

在Message的writeToParcel方法中会调用writeMessengerOrNullToParcel,然后在方法中会在data中writeStrongBinder,会将clientMessenger的Binder存储起来,是不是恍然大悟???

Messenger.writeMessengerOrNullToParcel(replyTo, dest);
dest.writeInt(sendingUid);
public static void writeMessengerOrNullToParcel(Messenger messenger, Parcel out) {
        out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder(): null);
}

接下来就顺利成章了,服务端会在onTransact中调用CREATOR;

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

public static final Parcelable.Creator<Message> CREATOR
            = new Parcelable.Creator<Message>() {
        public Message createFromParcel(Parcel source) {
            Message msg = Message.obtain();
            msg.readFromParcel(source);
            return msg;
        }
        
        public Message[] newArray(int size) {
            return new Message[size];
        }
};

然后会调用Message的readFromParcel,接着就是readMessengerOrNullFromParcel,在该方法中会得到clientMessenger

private void readFromParcel(Parcel source) {
        replyTo = Messenger.readMessengerOrNullFromParcel(source);
        sendingUid = source.readInt();
}
public static Messenger readMessengerOrNullFromParcel(Parcel in) {
        IBinder b = in.readStrongBinder();
        return b != null ? new Messenger(b) : null;
}

可以看到服务端发送消息也是通过Binder,之后过程就与客户端发送消息服务端一样了,就不做分析了。

其他

因为Messenger在进程内是通过Handler进行消息发送和处理的,所以只能串行发送消息和处理消息;
Messenger跨进程调用方法是非阻塞的,这个在我们栗子中可以体现,客户端发送消息后会马上打印后面的log,一段时间后服务端才返回消息。还记得IMessenger.aidl这个文件?有oneway这个关键字,这个就是非阻塞的原因,具体可以看后面参考链接中的博客。

private void sendMessage() {
            mSender.send(msg);
            Log.i(TAG, "msg is right now send");
}

private Messenger mReceiver = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.i(TAG, "msg is received from service");
        }
});
4.png

Messenger其实就是Android帮我们封装好的AIDL,用法和Handler类似,比较方便,文中的代码可以在Github中看到:https://github.com/juexingzhe/MessengerSample

好了,我们今天的Messenger之旅就到此结束了。谢谢!

参考链接:
http://www.jianshu.com/p/c6a73b9a14ce
http://www.jianshu.com/p/4ebd4783d3d9

欢迎关注公众号:JueCode

上一篇 下一篇

猜你喜欢

热点阅读