Messenger source code analysis

Posted by rajsekar2u on Sat, 15 Jan 2022 09:39:09 +0100

When I first came into contact with messenger and Handler, I always felt that they were a little similar, because they both had a "send(Message message)" method and processed messages serially. Later, I learned from the source code that they were more than similar. Messenger is an enhanced version of Handler that supports cross process calls.

Basic usage of Messenger

Server: myservice kt

class MyService : Service() {
    val mServiceMessenger = Messenger(ServiceMessengerHandler(Looper.getMainLooper()))

    class ServiceMessengerHandler(looper: Looper) : Handler(looper) {
        override fun handleMessage(msg: Message) {
            val messageString = msg.data.getString("message")
            Log.d("mytestmessenger", "Receive Client message: $messageString")
            val message = Message.obtain()
            val data = Bundle()
            data.putString("message", "hello, I am Service!")
            message.data = data
            msg.replyTo.send(message)
        }
    }

    override fun onBind(intent: Intent?): IBinder? {
        return mServiceMessenger.binder
    }
}

Configuration file: androidmanifest xml
Claim to run in another process

<service android:name=".MyService"
            android:process=":myService"/> 

Client: mainactivity kt

var mClientMessenger = Messenger(ClientMessengerHandler(Looper.getMainLooper()))

class ClientMessengerHandler(looper: Looper) : Handler(looper) {
	override fun handleMessage(msg: Message) {
		val messageString = msg.data.getString("message")
		Log.d("mytestmessenger", "Receive Service message: $messageString")
	}
}

fun testMessenger() {
	val intent = Intent(this, MyService::class.java)
	val connection = object : ServiceConnection {
		override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
			val serviceMessenger = Messenger(service)
			val message = Message.obtain()
			val data = Bundle()
			data.putString("message", "hello, I am Client!")
			message.data = data
			message.replyTo = mClientMessenger
			serviceMessenger.send(message)
		}

		override fun onServiceDisconnected(name: ComponentName?) {
		}
	}
	bindService(intent, connection, Context.BIND_AUTO_CREATE)
}

Messenger source code analysis

Messenger. Overall structure of Java code

class Messenger implements Parcelable {
    private final IMessenger 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();
    }
	
	/**
     * 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);
    }
    
    /**
     * Send a Message to this Messenger's Handler.
     * 
     * @param message The Message to send.  Usually retrieved through
     * {@link Message#obtain() Message.obtain()}.
     * 
     * @throws RemoteException Throws DeadObjectException if the target
     * Handler no longer exists.
     */
    public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }
    /**
     * Retrieve the IBinder that this Messenger is using to communicate with
     * its associated Handler.
     * 
     * @return Returns the IBinder backing this Messenger.
     */
    public IBinder getBinder() {
        return mTarget.asBinder();
    }
	... ... 
}

From the code structure of Messenger:

  • It implements the Parcelable interface, so during cross process communication, the Messenger object of the Client can be passed to the server as the replyTo parameter of the Message
  • There are two construction methods: one is to pass in the Handler and the other is to pass in the IBinder. No matter which one is passed in, it is to get the [IMessenger] interface. What is the [IMessenger] interface?
  • Send (message): Send a message
  • getBinder(): convert IMessenger interface to IBinder

IMessenger interface

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

From "mTarget = IMessenger.Stub.asInterface(target);" From this code, we can see that [IMessenger] is an AIDL interface with cross process capability.
We know the basic usage of Messenger. The [IBinder] here is the IBinder returned when connecting to the server, combined with the following code:

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

We can know that the [IBinder] obtained by the Client is actually the [IMessenger] interface returned by the [Messenger] object of the server through the [Handler.getIMessenger()] method

Handler.java key source code:

class Handler{
	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);
        }
    }
}

You can see from the Handler source code:

  • The object returned by the [Handler.getIMessenger()] method is [MessengerImpl]
  • [MessengerImpl] inherits from [IMessen.Stub]
  • The [send(Message msg)] method implementation of [MessengerImpl] is to call the [sendMessage] method of the current [Handler]

Therefore, we can see that the ultimate implementer of the AIDL interface function of [IMessenger] is [Handler]. By providing the [getIMessenger] method externally, the [Handler] packages itself as a [IMessenger] AIDL interface with cross process capability, while the [Messenger] just packages the [IMessenger] interface again for ease of use.

Summary

Messenger is actually an enhanced version of the Handler that supports cross process calls. Calling the send method of messenger is essentially calling the sendMessage method of the remote Handler.

Topics: Android