preface

IPC series of articles: Recommended reading in order.

Android IPC Service Android IPC AIDL Service Android IPC AIDL Android IPC server (IBinder) Android IPC server (IBinder)

After analyzing AIDL in detail from the source code +Demo point of view, this article will analyze the principle and application of AIDL simplified Messenger. Through this article, you will learn:

Messenger client sends a Message to the server. The server sends a Message to the client

1. The Messenger client sends a message to the server

Like AIDL, server-side and client-side logic is still written separately.

Write server

public class MyService extends Service { private String TAG = "ipc"; // create Handler, Private Handler Handler = new Handler(looper.getMainLooper ()) {@override public void handleMessage(@NonNull Message msg) { Bundle bundle = msg.getData(); int id = bundle.getInt("id"); Log.d(TAG, "receive id from client, id:" + id); }}; @override public IBinder onBind(Intent Intent) {return new Messenger(handler).getbinder (); }}Copy the code

Writing a client

ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName Name, IBinder service) {// Use the IBinder reference to construct Messenger Messenger = new Messenger(service); // construct Message Message Message = message.obtain (); Bundle Bundle = new Bundle(); bundle.putInt("id", 100); message.setData(bundle); Try {// Send a message messenger.send(message); } catch (Exception e) { } } @Override public void onServiceDisconnected(ComponentName name) { } }; private void bindService() { Intent intent = new Intent(MainActivity.this, MyService.class); bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); }Copy the code

The process is as follows:

1, The server constructs a Handler to receive messages. 2, the client constructs a Messenger to send messages

The server receives the message and prints the following:

You can see how convenient it is to communicate between processes without writing any AIDL files.

2. The Messenger server sends messages to the client

In the Demo above, the client sends a message to the server. What if the server wants to send a reply message to the client? For example, after receiving the ID from the client, the server finds out the name and age of the student corresponding to the ID and sends it to the client.

Write server

Modify the server code:

// create Handler, Private Handler Handler = new Handler(looper.getMainLooper ()) {@override public void handleMessage(@NonNull Message msg) { Bundle bundle = msg.getData(); int id = bundle.getInt("id"); Log.d(TAG, "receive id from client, id:" + id); Msg. replyTo = msg.replyTo; msg.replyTo = msg.replyTo; if (replyMessenger ! = null) {Message = message.obtain (); Bundle replyBundle = new Bundle(); replyBundle.putString("name", "xiaoming"); replyBundle.putInt("age", 18); message.setData(replyBundle); try { replyMessenger.send(message); } catch (Exception e) { } } } };Copy the code

Just modify the handleMessage(xx), when receiving the message from the client immediately return the name and age of the query sent to the client.

Writing a client

The client code also requires only minor changes:

Private Handler Handler = new Handler(looper.getMainLooper ()) {@override public void handleMessage(@nonnull) Message msg) { Bundle bundle = msg.getData(); if (bundle ! String name = bundle.getString("name"); int age = bundle.getInt("age"); Log.d("ipc", "receive name, age from server, name:" + name + "age:" + age); }}}; ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName Name, IBinder service) {// Use the IBinder reference to construct Messenger Messenger = new Messenger(service); // construct Message Message Message = message.obtain (); Bundle Bundle = new Bundle(); bundle.putInt("id", 100); message.setData(bundle); ReplyTo = new Messenger(handler); Try {// Send a message messenger.send(message); } catch (Exception e) { } } @Override public void onServiceDisconnected(ComponentName name) { } };Copy the code

When a client sends a message to a server, it carries its own Messenger so that the server can retrieve the Messenger to send messages to the client. At the same time, the handleMessage(xx) of the Handler needs to be overridden to receive information from the server. This is consistent with the server-side implementation, which is equivalent to having Messenger on both sides.

At this point, with the help of Messenger, client/server communication is easily accomplished.

3. Basic principles of Messenger

Start by sending a message

Find out why Messenger can do IPC so easily, starting with sending messages.

#Messenger.java
    public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }
Copy the code

The mTarget is of type IMessenger and is initialized in the Messenger constructor:

#Messenger.java
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
Copy the code

If target is of type Handler, go to Handler.

#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(); // call Handler to sendMessage handler.this. sendMessage(MSG); }}Copy the code

To see if this is similar, MessengerImpl inherits from IMessenger.Stub and implements a single method: send(Message MSG), which takes Message. From this, we can easily draw the conclusion that:

1. The server exposes the IMessenger interface, which has a unique method: Send (Message MSG). 2. The server implements the send(Message MSG) method, which sends messages using the Handler.

Find the AIDL file for IMessenger:

package android.os;

import android.os.Message;

/** @hide */
oneway interface IMessenger {
    void send(in Message msg);
}
Copy the code

Note: this document in the framework/core/Java/android/OS/IMessenger aidl aidl is said before, the Message itself to support serialization, add “in” tag data can only flow from the client to the server.

The interface definition has an additional identifier: “oneway”. This field will eventually affect the last parameter in ibinder.transact (xx) :

boolean _status = mRemote.transact(Stub.TRANSACTION_send, 
_data, null, android.os.IBinder.FLAG_ONEWAY);
Copy the code

FLAG_ONEWAY indicates that transact(xx) is not a blocking call, meaning that the client calls the method immediately and returns without waiting. If you think about it, you don’t need to wait, because send(xx) returns no value and the in modifier parameter, and data flows can’t flow from the server to the client.

Why can a server send messages to a client

From the IMessenger. Aidl definition, the send(xx) method can only send messages from the client to the server, and no value is returned. Therefore, it is not practical to carry server data through method parameters or return values. The biggest proof that a client can send data to a server is that the client can access the IBinder interface of the server. So how about the other way around? Let’s look at the fields in message.java:

#Message.java /** * Optional Messenger where replies to this message can be sent. The * semantics of exactly how this is  used are up to the sender and * receiver. */ public Messenger replyTo;Copy the code

Message can hold a Messenger reference, and we know that once Messenger is constructed, we can get the associated IBinder reference through getBinder(xx).

#Messenger.java
    public IBinder getBinder() {
        return mTarget.asBinder();
    }
Copy the code

The IBinder has it and all that’s left is to figure out how to pass it to the server.

Client delivery of IBinder to server Message is cross-process, so its member variables, including Messenger replyTo, need to be serialized and deserialized.

#Message.java public void writeToParcel(Parcel dest, int flags) { ... Messenger.writeMessengerOrNullToParcel(replyTo, dest); . }Copy the code

Static method called in Messenger:

#Messenger.java public static void writeMessengerOrNullToParcel(Messenger messenger, Parcel out) {// Get the Binder associated with Messenger and write it to the serialized object out.writeStrongBinder(Messenger! = null ? messenger.mTarget.asBinder() : null); }Copy the code

At this point, the client IBinder reference can be passed to the server.

After receiving the Message, the server deserializes member variables such as Messenger replyTo.

#Message.java private void readFromParcel(Parcel source) { ... replyTo = Messenger.readMessengerOrNullFromParcel(source); . }Copy the code

Similar calls to Messenger static methods:

# Messenger. Java public static Messenger readMessengerOrNullFromParcel (Parcel in) {/ / deserialize the IBinder IBinder = b in.readStrongBinder(); Messenger return b! = null ? new Messenger(b) : null; }Copy the code

Eventually, the server receives the IBinder from the client and constructs Messenger, which of course can send messages to the client.

4. Message, AIDL, Messenger

Message is used to pass data between threads. Used in conjunction with handlers, it supports serialization itself and can pass Message objects across processes. AIDL is used to simplify client-side and server-side code writing for interprocess communication. On the basis of AIDL, Messenger further encapsulates the interface exposed by the server and sends the Message received by the server to the target thread through the handler.

Use AIDL to communicate with Messenger.

1, can flexibly write server-side interface, and can customize method parameter type, data flow, method return value. 2, server-side method implementation can open multi-threaded data processing.

Disadvantages of using AIDL:

1. You need to write an AIDL file to define the server interface. 2, if it is a custom data type, also need to write the corresponding AIDL file.

Advantages of using Messenger:

1. Construct Message without defining AIDL file. 2. Fast bidirectional communication (technically AIDL can do this, but Messenger encapsulates IBinder delivery)

Disadvantages of using Messenger:

See above, AIDL’s strengths are Messenger’s weaknesses.

Applicable occasions

Messenger is recommended for simple communications where the server processes messages from the client in a single-threaded order.

This article is based on Android 10.0.

If you like, please like, pay attention to your encouragement is my motivation to move forward

Learn Android step by step with me as we continue to update