天天看點

Android Messenger原理

前言

Messenger中文意思是送信者,通過Messenger我們可以在不同程序之間傳遞Message對象,其底層也是通過上文講到的AIDL實作的,先來看看基本用法

一、基本用法

以用戶端發送消息給服務端,服務端回複一個消息給用戶端為例。

// 運作在其它程序
class MessengerService : Service() {
    class MessengerHandler : Handler() {
        val TAG = "MessengerHandler"
        override fun handleMessage(msg: Message?) {
            when (msg?.what) {
                ADD_BOOK -> {
                    // 必須設定classLoader不然會抛出異常
                    msg.data.classLoader = Thread.currentThread().contextClassLoader
                    Log.d(TAG, "do add book ${msg.data.get("book")}")
                    val replyMessage = Message.obtain(null, REPLY_ADD_BOOK)
                    val bundle = Bundle()
                    bundle.putString("reply", "我收到了響應")
                    replyMessage.data = bundle
                    msg.replyTo.send(replyMessage)
                }
                ALL_BOOKS -> {
                    Log.d(TAG, "do all books")
                }
            }
        }
    }
    override fun onBind(intent: Intent?): IBinder? {
        val messenger = Messenger(MessengerHandler())
        return messenger.binder
    }
}
複制代碼
           

接着在MainActivity中綁定服務,代碼如下

class MainActivity : AppCompatActivity() {

    private lateinit var connection: ServiceConnection
    private lateinit var replyMessenger: Messenger
    private var messenger: Messenger? = null
    
    class GetReplyHandler : Handler() {
        private var TAG = "MainActivity"
        override fun handleMessage(msg: Message?) {
            when (msg?.what) {
                REPLY_ADD_BOOK -> {
                    Log.d(TAG, msg.data.getString("reply"))
                }
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        connection = object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                messenger = Messenger(service)
            }
            override fun onServiceDisconnected(name: ComponentName?) {
            }
        }
        replyMessenger = Messenger(GetReplyHandler())
        val intent = Intent(this, MessengerService::class.java)
        bindService(intent, connection, Context.BIND_AUTO_CREATE)
    }

    fun send(v: View) {
        val msg = Message.obtain(null, ADD_BOOK)
    //    msg.obj = Book(0, "第0本書") 不能使用obj跨程序傳遞自定義的Parcelable對象,使用Bundle因為其可以設定classLoader
        val msg = Message.obtain(null, ADD_BOOK)
        val bundle = Bundle()
        bundle.putParcelable("book", Book(, "第0本書"))
        msg.data = bundle
        msg.replyTo = replyMessenger
        messenger?.send(msg)
    }
    
    override fun destroy() {
        unbindService(connection)
    }
}
複制代碼
           

注意send方法内部發送的Parcelable類(這裡是Book)在服務端必須也要存在,這樣當調用MainActivity的send方法時就會服務程序就會列印出do add book,下面就來看看該過程的源碼

二、源碼分析

  • 用戶端發送消息給服務端

首先我們在MessengerService的onBind中建立了一個Messenger執行個體,是以我們就從Messenger構造器開始說起

public Messenger(Handler target) {
    mTarget = target.getIMessenger();
}
複制代碼
           

繼續看看Handler的getIMessager

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

剛開始mMessenger肯定為null,繼續看看MessengerImpl

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

這裡居然有個Stub!瞬間想起了AIDL,于是就去找了找有沒有IMessenger.aidl這個檔案,最終找到了該檔案,檔案内容如下

oneway interface IMessenger {
    void send(in Message msg);
}
複制代碼
           

這裡的oneway表示調用send方法并不會挂起目前線程等待服務端執行,而是會立即傳回,send方法實作為将收到的消息發送給建立Messenger時的入參,至此服務端的Messenger使用分析完畢接着看看用戶端中Messenger的使用,在onServiceConnected中通過傳入的Binder對象構造了Messenger對象。

public Messenger(IBinder target) {
    mTarget = IMessenger.Stub.asInterface(target);
}
複制代碼
           

調用asInterface(上篇文章有講到)拿到IMessenger.Stub.Proxy執行個體指派給mTarget,最後用戶端通過調用Messengr.send發送消息

public void send(Message message) throws RemoteException {
    mTarget.send(message);
}
複制代碼
           

不管這裡send是個直接調用還是IPC調用都會調用到以下MessengerImpl的send方法,該方法又把消息發送到了對應的Handler,是以服務端的Handler就能收到消息了

  • 服務端收到消息回複用戶端

注意用戶端将replyMessenger設定給了Message.replyTo然後發送消息,這個過程中會調用Message.writeToParcel

public void writeToParcel(Parcel dest, int flags) {
    if (callback != null) {
        throw new RuntimeException(
            "Can't marshal callbacks across processes.");
    }
    dest.writeInt(what);
    dest.writeInt(arg1);
    dest.writeInt(arg2);
    if (obj != null) {
        try {
            Parcelable p = (Parcelable)obj;
            dest.writeInt();
            dest.writeParcelable(p, flags);
        } catch (ClassCastException e) {
            throw new RuntimeException(
                "Can't marshal non-Parcelable objects across processes.");
        }
    } else {
        dest.writeInt();
    }
    dest.writeLong(when);
    dest.writeBundle(data);
    Messenger.writeMessengerOrNullToParcel(replyTo, dest);
    dest.writeInt(sendingUid);
}
複制代碼
           

又會調用到Messenger.writeMessengerOrNullToParcel

public static void writeMessengerOrNullToParcel(Messenger messenger,
        Parcel out) {
    out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder()
            : null);
}
複制代碼
           

向Parcel中寫入了一個MessengerImpl執行個體(Binder),然後在IPC結束後會調用Message.readFromParcel

private void readFromParcel(Parcel source) {
    what = source.readInt();
    arg1 = source.readInt();
    arg2 = source.readInt();
    if (source.readInt() != ) {
        obj = source.readParcelable(getClass().getClassLoader());
    }
    when = source.readLong();
    data = source.readBundle();
    replyTo = Messenger.readMessengerOrNullFromParcel(source);
    sendingUid = source.readInt();
}
複制代碼
           

又調用到了Messenger.readMessengerOrNullFromParcel

public static Messenger readMessengerOrNullFromParcel(Parcel in) {
    IBinder b = in.readStrongBinder();
    return b != null ? new Messenger(b) : null;
}
複制代碼
           

如果是跨程序傳遞那麼讀取的會是一個BinderProxy對象,通過該BinderProxy構造Messenger對象其内部的mTarget就會是一個IMessenger.Stub.Proxy執行個體,是以服務端就可以調用用戶端的對應方法了

轉載于:https://juejin.im/post/5cb59f24f265da03914d573a