1. The concept
Messenger, a Messenger for interprocess communication. It is message-based interprocess communication, which we can use as handler.send (Message) between threads.
Messenger is a lightweight IPC solution whose underlying implementation is AIDL. When using Messenger for cross-process communication,Messenger queues all service calls and then processes them one at a time on the server side, without simultaneous calls. AIDL, on the other hand, may have multiple calls executed at the same time and must deal with multithreading.
For most applications, cross-process communication does not require one-to-many, which means that you do not need to perform multithreading, and Messenger is more appropriate.
2. Use
2.1 General Process
- The server implements a Handler that receives callbacks from each call made by the client
- The server uses a Handler to create Messenger objects
- Messenger creates an IBinder, which the server returns to the client via onBind()
- The client instantiates Messenger using IBinder and then sends the Message object to the server
- The server receives each Message in its Handler#handleMessage()
2.2 case
2.2.1 the service side
You first need to create a Handler on the server side to receive messages, then pass this Handler to Messenger and return the Messenger’s underlying binder in onBind.
Android :process=":other"
class MessengerService : Service() {
private lateinit var mMessenger: Messenger
override fun onBind(intent: Intent): IBinder {
log(TAG, "onBind~")
// Pass Handler to instantiate Messenger
mMessenger = Messenger(IncomingHandler(this))
// Return binder in Messenger to the client so it can be called remotely
return mMessenger.binder
}
// Process the Message from the client and decide what to do next
internal class IncomingHandler(
context: Context,
private val applicationContext: Context = context.applicationContext
) : Handler(
Looper.getMainLooper()
) {
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_SAY_HELLO -> {
Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show()
log(TAG, "hello!")}else -> super.handleMessage(msg)
}
}
}
}
Copy the code
2.2.2 the client
On the client side, you first need to bind the remote Service. Once the connection is complete, you retrieve the IBinder object returned by the remote Service from onServiceConnected() and use the IBinder object to instantiate Messenger on the client side. With this Messenger, you can send messages to the server through this Messenger. Example code is as follows:
class MessengerActivity : TitleBarActivity() {
/** Messenger to communicate with the server */
private var mService: Messenger? = null
/** Whether bindService */ is enabled
private var bound: Boolean = false
private val mServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName? , service:IBinder?). {
mService = Messenger(service)
bound = true
}
override fun onServiceDisconnected(name: ComponentName?). {
mService = null
bound = false}}override fun getThisTitle(a): CharSequence {
return "Messenger"
}
override fun onCreate(savedInstanceState: Bundle?). {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_messenger)
btnConnect.setOnClickListener {
connectService()
}
btnSayHello.setOnClickListener {
sayHello()
}
}
private fun sayHello(a) {
if(! bound) {return
}
// create and send a message to the server with what specified as MSG_SAY_HELLO
val message = Message.obtain(null, MSG_SAY_HELLO, 0.0)
try{ mService? .send(message) }catch (e: RemoteException) {
e.printStackTrace()
}
}
private fun connectService(a) {
Intent().apply {
action = "com.xfhy.messenger.Server.Action"
setPackage("com.xfhy.allinone")
}.also { intent ->
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)
}
}
override fun onStop(a) {
super.onStop()
if (bound) {
unbindService(mServiceConnection)
bound = false}}}Copy the code
As shown in the sample code, when a client communicates with a server through Messenger, it must put data into Message. Both Messenger and Message implement the Parcelable interface, so they can be transferred across processes. Messages can only be delivered via WHAT, arg1, arg2,Bundle, and replyTo. Serializable or Parcelable objects can be delivered in bundles. Bundles also support a wide range of other data classes Type.
2.2.3 The server Sends Messages to the Client
Sometimes we need the client to be able to respond to messages sent by the server, so we can simply modify the example above.
Each time the server receives a message, it replies a message to the client for testing
internal class IncomingHandler : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_SAY_HELLO -> {
log(TAG, "hello!")
// The client's Messenger is in the replyTo of the Message
replyToClient(msg, "I have received your message and will reply to you later")
}
MSG_TRANSFER_SERIALIZABLE -> log(TAG, "Object passed:${msg.data? .get("person")}")
else -> super.handleMessage(msg)
}
}
private fun replyToClient(msg: Message, replyText: String) {
val clientMessenger = msg.replyTo
val replyMessage = Message.obtain(null, MSG_FROM_SERVICE)
replyMessage.data = Bundle().apply {
putString("reply", replyText)
}
try{ clientMessenger? .send(replyMessage) }catch (e: RemoteException) {
e.printStackTrace()
}
}
}
Copy the code
To respond, create a Messenger on the client side and create a Handler for it to receive messages from the server side. When the client sends a message, you need to set Message#replyTo to the client’s Messenger. The server needs this Messanger to reply to the message.
/** Messenger */ on the client side
private var mClientMessenger = Messenger(IncomingHandler())
class IncomingHandler : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_FROM_SERVICE -> {
log(TAG, "Received from service: ${msg.data? .getString("reply")}")}else -> super.handleMessage(msg)
}
}
}
private fun sayHello(a) {
if(! bound) {return
}
// create and send a message to the server with what specified as MSG_SAY_HELLO
val message = Message.obtain(null, MSG_SAY_HELLO, 0.0)
// Note that this is new
message.replyTo = mClientMessenger
message.data = Bundle().apply {
putSerializable("person", SerializablePerson("Zhang"))}try{ mService? .send(message) }catch (e: RemoteException) {
e.printStackTrace()
}
}
Copy the code
SayHello () is called by the server.
Xfhy. allinone D/xfhy_messenger: Hello! Xfhy. allinone D/xfhy_messenger: Received from service: I have received your message and will reply to you laterCopy the code
There are obviously two processes in the log, so it is now two-way communication. That’s about it. Here’s how It works
// How Todo xFHY Illustration Messenger works (P93)
Principle 3.
3.1 Client -> Server Communication
The service side
Let’s take a look at how this works when client-to-server communication is one-way. The first is that the server side returns Messenger’s binder object in the onBind method
override fun onBind(intent: Intent): IBinder {
// Pass Handler to instantiate Messenger
mMessenger = Messenger(IncomingHandler())
// Return binder in Messenger to the client so it can be called remotely
return mMessenger.binder
}
Copy the code
Binder in Messenger
private final IMessenger mTarget;
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
public IBinder getBinder(a) {
return mTarget.asBinder();
}
Copy the code
From Messenger constructor (IMessenger. Stub. AsInterface ()) as you can see it at the bottom should be made use AIDL. GetBinder () is actually will invoke the mTarget. AsBinder (), and incoming Ha mTarget is us Handler. GetIMessenger () :
final IMessenger getIMessenger(a) {
synchronized (mQueue) {
if(mMessenger ! =null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
returnmMessenger; }}private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg); }}Copy the code
The IMessenger is the Handler’s internal class MessengerImpl, which has only one Send method. MessengerImpl sends a Message to Handler#sendMessage(), and sends it to Handler#sendMessage() The Message is received inside andler#handleMessage().
MessengerImpl is inherited from IMessenger.Stub, which looks like an AIDL file automatically generated,easy. Corresponding aidl document should be bold guess IMessenger. Aidl, let’s go to the source code inside find IMessenger. Aidl, indeed as expected at frameworks/base/core/Java/android/OS/IMessenger aidl this position is found It. As follows:
package android.os;
import android.os.Message;
/ * *@hide* /
oneway interface IMessenger {
void send(in Message msg);
}
Copy the code
According to the AIDL file, its automatically generated IMessenger. Java should look like this:
package android.os;
public interface IMessenger extends android.os.IInterface {
/** * Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements IMessenger {
private static final java.lang.String DESCRIPTOR = "android.os.IMessenger";
/** * Construct the stub at attach it to the interface. */
public Stub(a) {
this.attachInterface(this, DESCRIPTOR);
}
/** * Cast an IBinder object into an android.os.IMessenger interface, * generating a proxy if needed. */
public static IMessenger asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if(((iin ! =null) && (iin instanceof IMessenger))) {
return ((IMessenger) iin);
}
return new IMessenger.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder(a) {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
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;
}
default: {
return super.onTransact(code, data, reply, flags); }}}private static class Proxy implements IMessenger {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder(a) {
return mRemote;
}
public java.lang.String getInterfaceDescriptor(a) {
return DESCRIPTOR;
}
@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;
}
Copy the code
That’s easy to do, and it’s clear that Messenger’s underlying implementation is based on AIDL. This line on the server side: Service#onBind()->mMessenger. GetBinder ()->Handler#getIMessenger()->MessengerImpl(IMessenger.Stub Tub subclasses go back through onBind to get binder objects when clients bind. When receiving a Message from a client, the MessengerImpl forwards the Message to a Handler. The Handler defined on the server side processes the Message in handleMessage() to understand what service the client wants to call, and then performs the appropriate logic Series.
The client
On the client side, onServiceConnected() puts the IBinder object returned by the server into Messenger.
//MessengerActivity.kt
private val mServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName? , service:IBinder?). {
mService = Messenger(service)
bound = true
}
override fun onServiceDisconnected(name: ComponentName?). {
mService = null
bound = false}}//Messenger.java
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
public Messenger(IBinder target) {
// The asInterface object is IMessenger.Stub.Proxy
mTarget = IMessenger.Stub.asInterface(target);
}
Copy the code
IBinder object into the Messenger was familiar with the operation of the IMessenger. The Stub. AsInterface (), simple. Then, when the client sends a message to the server, it calls the send method in Messenger. The internal send method in Messenger is actually called IMessenger.Stub.Proxy(cross-process). After the call, the server side receives the Message in the Handler’s handleMessage, enabling cross-process communication.
3.2 Server -> Client Communication
Client-server communication is almost identical to the way we do it with AIDL, and we can do it ourselves. Messenger just wraps it up for us. Let’s look at the communication between the server and the client.
If the server needs to communicate with the client, the client needs to store the client Messenger in the replyTo of the send message.
private fun sayHello(a) {
val message = Message.obtain(null, MSG_SAY_HELLO, 0.0)
// Put the client's Messenger into replyTomessage.replyTo = mClientMessenger mService? .send(message) }Copy the code
When sending messages to the server, serialization and deserialization messages are definitely needed because they are cross-process. Look at the deserialization code for Message:
private void readFromParcel(Parcel source) {
what = source.readInt();
arg1 = source.readInt();
arg2 = source.readInt();
if(source.readInt() ! =0) {
obj = source.readParcelable(getClass().getClassLoader());
}
when = source.readLong();
data = source.readBundle();
replyTo = Messenger.readMessengerOrNullFromParcel(source);
sendingUid = source.readInt();
workSourceUid = source.readInt();
}
Copy the code
Mainly see how replyTo deserialization, it calls the Messenger readMessengerOrNullFromParcel method:
public static void writeMessengerOrNullToParcel(Messenger messenger, Parcel out) { out.writeStrongBinder(messenger ! =null ? messenger.mTarget.asBinder()
: null);
}
public static Messenger readMessengerOrNullFromParcel(Parcel in) {
IBinder b = in.readStrongBinder();
returnb ! =null ? new Messenger(b) : null;
}
Copy the code
WriteMessengerOrNullToParcel in the client’s messenger. MTarget. AsBinder write (), then the recovery, when readMessengerOrNullFromParcel and messenger. MT Arget is a MessengerImpl,asBinder() is a method in its parent IMessenger.Stub:
@Override
public android.os.IBinder asBinder(a) {
return this;
}
Copy the code
It’s going to take itself back out. That is, the replyTo deserialized on the server corresponds to the IBinder in Messenger, which is the MessengerImpl object on the client. The server takes the Messenger and sends the message. The client receives the message through cross-process communication with the IBinder object.
4. Summary
Messenger is more commonly used than AIDL when communicating across processes (when the conditions are met) because it is easier to use and officially recommended. While using Messenger, we need to understand how it works:
- One-way communication between the client and server uses the principles of the AIDL interface, just as we usually write
- When the server communicates with the client, Message objects need to be serialized and deserialized when the client sends messages. Binder objects on the client side are encapsulated in replyTo fields, which are then taken out and assembled into Messenger when the server side deserializes them. With binder objects on the client side, of course, cross-process communication with the client is also possible.
data
- Binding Service Overview
- Android Message-based Interprocess Communication Messenger is fully resolved