Android 基于Message的進程間通信 Messenger完全解析

jopen 9年前發布 | 125K 次閱讀 Android Android開發 移動開發

http://blog.csdn.net/lmj623565791/article/details/47017485; 
本文出自:【張鴻洋的博客】

一、概述

說到Android進程間通信,大家肯定能想到的是編寫aidl文件,然后通過aapt生成的類方便的完成服務端,以及客戶端代碼的編寫。如果你對這個過程不熟悉,可以查看Android aidl Binder框架淺析

當然今天要說的通信方式肯定不是通過編寫aidl文件的方式,那么有請今天的主角:Messenger。ok,這是什么樣的一個類呢?我們看下注釋

This allows for the implementation of message-based communication across processes

允許實現基于消息的進程間通信的方式。

那么,什么叫基于消息的進程間通信方式呢?看個圖理解下:

20150723095636567.png

可以看到,我們可以在客戶端發送一個Message給服務端,在服務端的handler中會接收到客戶端的消息,然后進行對應的處理,處理完成后,再將結果等數據封裝成Message,發送給客戶端,客戶端的handler中會接收到處理的結果。

這樣的進程間通信是不是很爽呢?

  • 基于Message,相信大家都很熟悉

  • 支持回調的方式,也就是服務端處理完成長任務可以和客戶端交互

  • 不需要編寫aidl文件

此外,還支持,記錄客戶端對象的Messenger,然后可以實現一對多的通信;甚至作為一個轉接處,任意兩個進程都能通過服務端進行通信,這個后面再說。

看到這,有沒有一些的小激動,我們可以不寫aidl文件,方便的實現進程間通信了,是不是又可以裝一下了。哈,下面看個簡單的例子。


二、通信實例

這個例子,通過兩個apk演示,一個apk是Server端,一個是Client端;

(1) Server端

代碼

package com.imooc.messenger_server;

import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException;

public class MessengerService extends Service {

    private static final int MSG_SUM = 0x110;

    //最好換成HandlerThread的形式     private Messenger mMessenger = new Messenger(new Handler()     {         @Override         public void handleMessage(Message msgfromClient)         {             Message msgToClient = Message.obtain(msgfromClient);//返回給客戶端的消息             switch (msgfromClient.what)             {                 //msg 客戶端傳來的消息                 case MSG_SUM:                     msgToClient.what = MSG_SUM;                     try                     {                         //模擬耗時                         Thread.sleep(2000);                         msgToClient.arg2 = msgfromClient.arg1 + msgfromClient.arg2;                         msgfromClient.replyTo.send(msgToClient);                     } catch (InterruptedException e)                     {                         e.printStackTrace();                     } catch (RemoteException e)                     {                         e.printStackTrace();                     }                     break;             }

            super.handleMessage(msgfromClient);         }     });

    @Override     public IBinder onBind(Intent intent)     {         return mMessenger.getBinder();     } }</pre>

服務端就一個Service,可以看到代碼相當的簡單,只需要去聲明一個Messenger對象,然后onBind方法返回mMessenger.getBinder();

然后坐等客戶端將消息發送到handleMessage想法,根據message.what去判斷進行什么操作,然后做對應的操作,最終將結果通過 msgfromClient.replyTo.send(msgToClient);返回。

可以看到我們這里主要是取出客戶端傳來的兩個數字,然后求和返回,這里我有意添加了sleep(2000)模擬耗時,注意在實際使用過程中,可以換成在獨立開辟的線程中完成耗時操作,比如和HandlerThread結合使用。

注冊文件

<service
            android:name=".MessengerService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.zhy.aidl.calc"></action>
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>

別忘了注冊service,寫完以后直接安裝。

(二)客戶端

Activity

package com.imooc.messenger_client;

import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView;

public class MainActivity extends AppCompatActivity {     private static final String TAG = "MainActivity";     private static final int MSG_SUM = 0x110;

    private Button mBtnAdd;     private LinearLayout mLyContainer;     //顯示連接狀態     private TextView mTvState;

    private Messenger mService;     private boolean isConn;

    private Messenger mMessenger = new Messenger(new Handler()     {         @Override         public void handleMessage(Message msgFromServer)         {             switch (msgFromServer.what)             {                 case MSG_SUM:                     TextView tv = (TextView) mLyContainer.findViewById(msgFromServer.arg1);                     tv.setText(tv.getText() + "=>" + msgFromServer.arg2);                     break;             }             super.handleMessage(msgFromServer);         }     });

    private ServiceConnection mConn = new ServiceConnection()     {         @Override         public void onServiceConnected(ComponentName name, IBinder service)         {             mService = new Messenger(service);             isConn = true;             mTvState.setText("connected!");         }

        @Override         public void onServiceDisconnected(ComponentName name)         {             mService = null;             isConn = false;             mTvState.setText("disconnected!");         }     };

    private int mA;

    @Override     protected void onCreate(Bundle savedInstanceState)     {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);

        //開始綁定服務         bindServiceInvoked();

        mTvState = (TextView) findViewById(R.id.id_tv_callback);         mBtnAdd = (Button) findViewById(R.id.id_btn_add);         mLyContainer = (LinearLayout) findViewById(R.id.id_ll_container);

        mBtnAdd.setOnClickListener(new View.OnClickListener()         {             @Override             public void onClick(View v)             {                 try                 {                     int a = mA++;                     int b = (int) (Math.random() * 100);

                    //創建一個tv,添加到LinearLayout中                     TextView tv = new TextView(MainActivity.this);                     tv.setText(a + " + " + b + " = caculating ...");                     tv.setId(a);                     mLyContainer.addView(tv);

                    Message msgFromClient = Message.obtain(null, MSG_SUM, a, b);                     msgFromClient.replyTo = mMessenger;                     if (isConn)                     {                         //往服務端發送消息                         mService.send(msgFromClient);                     }                 } catch (RemoteException e)                 {                     e.printStackTrace();                 }             }         });

    }

    private void bindServiceInvoked()     {         Intent intent = new Intent();         intent.setAction("com.zhy.aidl.calc");         bindService(intent, mConn, Context.BIND_AUTO_CREATE);         Log.e(TAG, "bindService invoked !");     }

    @Override     protected void onDestroy()     {         super.onDestroy();         unbindService(mConn);     }

}</pre>

代碼也不復雜,首先bindService,然后在onServiceConnected中拿到回調的service(IBinder)對象,通過service對象去構造一個mService =new Messenger(service);然后就可以使用mService.send(msg)給服務端了。

我們消息的發送在Btn.onclick里面:

Message msgFromClient = Message.obtain(null, MSG_SUM, a, b);
msgFromClient.replyTo = mMessenger;
if (isConn)
{
    //往服務端發送消息
    mService.send(msgFromClient);
}

那么服務端會收到消息,處理完成會將結果返回,傳到Client端的mMessenger中的Handler的handleMessage方法中。

布局文件

<LinearLayout android:id="@+id/id_ll_container"
              xmlns:android="

    <TextView         android:id="@+id/id_tv_callback"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:text="Messenger Test!"/>

    <Button android:id="@+id/id_btn_add"             android:layout_width="wrap_content"             android:layout_height="wrap_content"             android:text="add"/>

</LinearLayout></pre>

效果圖

20150723095211830.gif

可以看到,我們每點擊一次按鈕,就往服務器發送個消息,服務器拿到消息執行完成后,將結果返回。

整個通信的代碼看起來還是相當的清爽的,那么大家有沒有對其內部的原理有一絲的好奇呢?下面我們就來看下其內部是如何實現的。

對了,源碼分析前,這里插一句,大家通過代碼可以看到服務端往客戶端傳遞數據是通過msg.replyTo這個對象的。那么服務端完全可以做到,使用一個List甚至Map去存儲所有綁定的客戶端的msg.replyTo對象,然后想給誰發消息都可以。甚至可以把A進程發來的消息,通過B進程的msg.replyTo發到B進程那里去。相關代碼呢,可以參考官方的文檔:service,注意下拉找:Remote Messenger Service Sample。


三、源碼分析

其實Messenger的內部實現的,實際上也是依賴于aidl文件實現的。

(一)首先我們看客戶端向服務端通信

服務端

服務端的onBind是這么寫的:

public IBinder onBind(Intent intent)
{
   return mMessenger.getBinder();
}

那么點進去:

public IBinder getBinder() {
   return mTarget.asBinder();
}

可以看到返回的是mTarget.asBinder();

mTarget是哪來的呢?

別忘了我們前面去構造mMessenger對象的代碼:new Messenger(new Handler());

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

原來是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);         }     }</pre>

mTarget是一個MessengerImpl對象,那么asBinder實際上是返回this,也就是MessengerImpl對象;

這是個內部類,可以看到繼承自IMessenger.Stub,然后實現了一個send方法,該方法就是將接收到的消息通過 Handler.this.sendMessage(msg);發送到handleMessage方法。

看到這,大家有沒有想到什么,難道不覺得extends IMessenger.Stub這種寫法異常的熟悉么?

我們傳統寫aidl文件,aapt給我們生成什么,生成IXXX.Stub類,然后我們服務端繼承IXXX.Stub實現接口中的方法。

沒錯,其實這里內部其實也是依賴一個aidl生成的類,這個aidl位于:frameworks/base/core/java/android/os/IMessenger.aidl.

package android.os;  

import android.os.Message;  

/* @hide /   oneway interface IMessenger {       void send(in Message msg);   }</pre>

看到這,你應該明白了,Messenger并沒有什么神奇之處,實際上,就是依賴該aidl文件生成的類,繼承了IMessenger.Stub類,實現了send方法,send方法中參數會通過客戶端傳遞過來,最終發送給handler進行處理。這里不理解,請詳細看下Android aidl Binder框架淺析

客戶端

客戶端首先通過onServiceConnected拿到sevice(Ibinder)對象,這里沒什么特殊的,我們平時的寫法也是這樣的,只不過我們平時會這么寫:

IMessenger.Stub.asInterface(service)拿到接口對象進行調用;

而,我們的代碼中是

mService = new Messenger(service);

跟進去,你會發現:

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

soga,和我們平時的寫法一模一樣!

到這里就可以明白,客戶端與服務端通信,實際上和我們平時的寫法沒有任何區別,通過編寫aidl文件,服務端onBind利用Stub編寫接口實現返回;客戶端利用回調得到的IBinder對象,使用IMessenger.Stub.asInterface(target)拿到接口實例進行調用(內部實現,參考Android aidl Binder框架淺析)。

(2)服務端與客戶端通信

那么,客戶端與服務端通信的確沒什么特殊的地方,我們完全也可以編寫個類似的aidl文件實現;那么服務端是如何與客戶端通信的呢?

還記得,客戶端send方法發送的是一個Message,這個Message.replyTo指向的是一個mMessenger,我們在Activity中初始化的。

那么將消息發送到服務端,肯定是通過序列化與反序列化拿到Message對象,我們看下Message的反序列化的代碼:

# 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();     }</pre>

主要看replyTo,調用的是Messenger.readMessengerOrNullFromParcel

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

    public static void writeMessengerOrNullToParcel(Messenger messenger,             Parcel out) {         out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder()                 : null);     }</pre>

通過上面的writeMessengerOrNullToParcel可以看到,它將客戶端的messenger.mTarget.asBinder()對象進行了恢復,客戶端的message.mTarget.asBinder()是什么?

客戶端也是通過Handler創建的Messenger,于是asBinder返回的是:

public Messenger(Handler target) {
        mTarget = target.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);         }     }

   public IBinder getBinder() {         return mTarget.asBinder();     }</pre>

那么asBinder,實際上就是MessengerImpl extends IMessenger.Stub中的asBinder了。

#IMessenger.Stub

@Override  public android.os.IBinder asBinder() {     return this; }</pre>

那么其實返回的就是MessengerImpl對象自己。到這里可以看到message.mTarget.asBinder()其實返回的是客戶端的MessengerImpl對象。

最終,發送給客戶端的代碼是這么寫的:

msgfromClient.replyTo.send(msgToClient);

public void send(Message message) throws RemoteException {         mTarget.send(message);     }</pre>

這個mTarget實際上就是對客戶端的MessengerImpl對象的封裝,那么send(message)(屏蔽了transact/onTransact的細節),這個message最終肯定傳到客戶端的handler的handleMessage方法中。

好了,到此我們的源碼分析就結束了~~

總結下:

  • 客戶端與服務端通信,利用的aidl文件,沒什么特殊的

  • 服務端與客戶端通信,主要是在傳輸的消息上做了處理,讓Messager.replyTo指向的客戶端的Messenger,而Messenger又持有客戶端的一個Binder對象(MessengerImpl)。服務端正是利用這個Binder對象做的與客戶端的通信。

可以考慮自己編寫aidl文件,實現下服務端對客戶端的回調。


 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!