Android IPC之Messenger淺談

來源:互聯網
上載者:User

標籤:component   不同   ide   nbsp   簡單   others   undle   cts   cti   

      之前寫過一篇有關 IPC之AIDL淺談的文章,詳情請看Android IPC之AIDL淺談。今天再來介紹另一種 IPC-Messenger。

一、概述。

首先看Messenger介紹,

 Reference to a Handler, which others can use to send messages to it.This allows for the implementation of message-based communication across  processes, by creating a Messenger pointing to a Handler in one process, and handing that Messenger to another process.
大概意思是說,通過Handler來發送訊息。允許在進程中基於訊息通訊實現,在一個進程中建立一個信使指向一個Handler,並將該信使傳遞給另外一個進程。

換句話說,就是Messenger通過Handler來發送訊息,可以在不同的處理序間通訊。

下面通過一張圖來說明,


該圖基本說明了Messenger的上層工作流程。小結如下:

1.Messenger的使用需要有服務端和用戶端;

2.用戶端需要綁定遠端連線,綁定成功後,就能擷取遠端IBinder對象,通過該IBinder就可以得到遠端Messenger對象;

3.向服務端發送訊息時,需要使用服務端的Messenger對象,並且可以把用戶端的Messenger對象賦值給訊息的‘replyTo’屬性,一併傳遞到服務端;

4.向用戶端發送訊息,需要使用用戶端的Messenger對象,該對象是從用戶端發送的訊息的‘replyTo’屬性中擷取的。

下面依舊通過一個執行個體來示範Messenger的整個流程。

二、執行個體。

建立兩個應用,一個是服務端,一個用戶端。

1.服務端實現。

建立應用後,建立一個Service,

/** * 服務端 */public class MessengerService extends Service {    private static final String TAG = "MessengerService";    private static final int WHAT = 0x101;    Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            switch (msg.what) {                case WHAT:                    //接受從用戶端傳來的訊息                    Bundle bundle= (Bundle) msg.obj;                    String str = (String) bundle.get("str");                    Log.e(TAG, "服務端已接受到用戶端的訊息");                    Log.e(TAG, "訊息是---->" + str);                    //發送資料給用戶端                    Message message = Message.obtain();                    Bundle bundle1= new Bundle();                    bundle1.putString("str","我已經知道了,加油,看好你!");                    message.obj = bundle1;                    message.what=0x102;                    try {                        //向用戶端發送訊息                        msg.replyTo.send(message);//首先擷取從用戶端傳遞的Messenger對象,然後調用該Messenger對象的send()方法                    } catch (RemoteException e) {                        e.printStackTrace();                    }                    break;            }        }    };    private Messenger messenger = new Messenger(mHandler);    @Override    public void onCreate() {        super.onCreate();        Log.d(TAG, "onCreate: ");    }    @Nullable    @Override    public IBinder onBind(Intent intent) {        return messenger.getBinder();    }}

實現該Service的onBind()方法,調用‘messenger.getBinder()’返回一個IBinder,返回的IBinder我們將會在用戶端接收並使用。建立一個Handler,用於接收用戶端傳遞的訊息,當接收到訊息後,然後向用戶端發送了一個訊息。服務端就是這些內容!

      一定要記得,發送訊息傳遞資料時不能把資料直接扔給訊息,否則會報錯,如下所示:

java.lang.RuntimeException: Can‘t marshal non-Parcelable objects across processes.
因為Binder事務傳遞的資料被稱為包裹(Parcel),必須實現Parcelable介面,否則無法在兩個應用之間進行通訊。所以,如果需要在訊息中攜帶資料,請使用Bundle對象,因為給類以及實現了Parcelable介面。
      還要記得註冊該服務。

...  <service            android:name=".MessengerService"            android:exported="true">            <intent-filter>                <action android:name="cn.xinxing.messengerservice"></action>                <category android:name="android.intent.category.DEFAULT" />            </intent-filter>        </service>...
其中android:exported的作用是“是否支援其它應用調用當前組件”,true允許被啟動;false不允許被啟動。並且設定了“action”屬性,便於我們隱式調用該服務。

一切Ok的話,就可以運行該程式了!

PS:

      IBinder是一個介面。 IBinder是遠程對象的基本介面,是為高效能而設計的輕量級遠程調用機制的核心部分。但它不僅用於遠程調用,也用於進程內調用。這個介面定義了與遠程對象互動的協議。不要直接實現這個介面,而應該從Binder派生。

      IBinder的主要API是transact(),與它對應另一方法是Binder.onTransact()。第一個方法使你可以向遠端的IBinder對象發送發出調用,第二個方法使你自己的遠程對象能夠響應接收到的調用。IBinder的API都是同步執行的,比如transact()直到對方的Binder.onTransact()方法調用完成後才返回。調用發生在進程內時無疑是這樣的,而在進程間時,在IPC的協助下,也是同樣的效果。

2.用戶端實現。

public class MainActivity extends AppCompatActivity {    private static final String TAG = "MainActivity";    private static final int WHAT = 0x102;    private boolean isConn;    Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            switch (msg.what) {                case WHAT:                    //接受從服務端發送的訊息                    Bundle bundle= (Bundle) msg.obj;                    String str = (String) bundle.get("str");                    Log.e(TAG, "用戶端服已接受到務端的訊息");                    Log.e(TAG, "訊息是---->" + str);                    break;            }        }    };    private Messenger mClientMessenger = new Messenger(mHandler);    private Messenger mServiceMessenger;        ServiceConnection serviceConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            Log.e(TAG, "串連Service成功---->");            mServiceMessenger = new Messenger(service);            isConn = true;            Message message = Message.obtain();            Bundle bundle=new Bundle();            bundle.putString("str","你好!我是程式猿!");            message.obj=bundle;            message.what = 0x101;            message.replyTo = mClientMessenger;            try {                mServiceMessenger.send(message);            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {            Log.e(TAG, "串連Service---->disconnected");            mServiceMessenger = null;            isConn = false;        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        findViewById(R.id.btn_start_service).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                bindService();            }        });    }    /**     * 綁定遠端連線服務     */    private void bindService() {        Intent intent = new Intent();        intent.setAction("cn.xinxing.messengerservice");        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);    }    @Override    protected void onDestroy() {        super.onDestroy();        if (isConn) {            unbindService(serviceConnection);        }    }}

螢幕中有一個按鈕,點擊按鈕時,綁定遠端連線服務;綁定成功後,發送訊息;Handler用於接收訊息;當Activity銷毀時,解除綁定。

整個實現還是比較簡單的。

運行用戶端後,點擊按鈕,看Log輸出,


遠端連線服務綁定成功,接著服務端輸出Log,


最後用戶端Log輸出,


小結:整個流程是,點擊按鈕時,綁定遠端連線服務,綁定成功後,並且會拿到遠程服務的Messenger對象,然後使用Messenger對象發送訊息,並且將用戶端的Messenger對象也發送到了服務端,當服務端收到訊息後,會輸出用戶端發送的訊息內容,並且拿到用戶端的Messenger對象,然後使用該Messenger對象向用戶端發送訊息,用戶端收到訊息後,會輸出Log。

整體上,使用Messenger實現IPC,相比AIDL來說,還是比較簡單的。下面我們通過Messenger的源碼來分析,它的內部實現。

三、源碼解析。

先看Messenger的構造方法,

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

構造方法傳遞一個Handler,然後將HandlerIMessenger對象傳遞給mTarget對象(MessengerImpl)。下面是Handler的getIMessenger()具體實現,

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

執行個體化一個MessengerImpl對象,然後返回了一個IMessenger對象。其中MessengerImpl的源碼如下,

    private final class MessengerImpl extends IMessenger.Stub {        public void send(Message msg) {            msg.sendingUid = Binder.getCallingUid();            Handler.this.sendMessage(msg);        }    }
看到這裡 IMessenger.Stub,貌似很熟悉啊!這不就是AIDL的實現嗎!bingo!是的! Messenger的底層其實就AIDL!只不過, Messenger已經為我們封裝了好了,不需要我們自己關心AIDL的具體細節。

IMessenger的源碼位於‘ \base\core\java\android\os\IMessenger.aidl’,可以看到它是一個aidl檔案,下面是它的源碼,

package android.os;import android.os.Message;/** @hide */oneway interface IMessenger {    void send(in Message msg);}
內部只有一個方法,用於發送訊息。這裡更加證實了, Messenger的底層其實就AIDL!有關如何?一個AIDL,可以參考 Android IPC之AIDL淺談。

當發送訊息時,調用Messengersend()方法,

    public void send(Message message) throws RemoteException {        mTarget.send(message);    }
調用了mTarget的 send()方法,而mTarget是MessengerImpl。通過上文可以得知 MessengerImpl類實現了send()方法,該方法是發送一個訊息,可以看到它其實還是通過 Handler發送訊息。有關Handler發送訊息,可以參考 Android 源碼解析Handler處理機制(二)。

服務端有一個onBind()方法,會返回一個IBinder對象,該對象來自於Messenger,

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

繼續跟下去,

    public IBinder getBinder() {        return mTarget.asBinder();    }    
返回mTarget對象的asBinder(),而“mTarget.asBinder()”到底是什麼呢?
我們先看看使用AIDL時,AAPT自動產生的AIDL類中的asBinder()方法,
@Override public android.os.IBinder asBinder(){return this;}
      該方法返回的是自己,即xxxx.Stub類。那麼我們此時再看看mTarget對象,mTarget是MessengerImpl,而MessengerImpl是繼承自IMessenger.Stub,那麼mTarget.asBinder()返回的就是Messenger.Stub.asBinder(),而Messenger.Stub.asBinder()返回的又是Messenger.Stub,而MessengerImpl是繼承自IMessenger.Stub,所以繞了一圈, “mTarget.asBinder()”返回的就是MessengerImpl對象自己。也就是說 “mTarget.asBinder()”返回的是當前Messenger中的MessengerImpl對象。

      當在服務端時,“mTarget.asBinder()”返回的是服務端的MessengerImpl對象,使用它來向服務端發送訊息;當在用戶端時,“mTarget.asBinder()”返回的是用戶端的MessengerImpl對象,使用它來向用戶端發送訊息。

用戶端綁定遠端連線成功後,會擷取到一個遠程IBinder對象,通過該IBinder對象,就可以擷取到服務端的Messenger對象,即調用下面的方法,

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

這不是和AIDL的寫法一模一樣嗎!下面是AIDL的代碼,

 //遠端連線    private ServiceConnection conn = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            calculateAidl = CalculateAidl.Stub.asInterface(service);            isBindSuccess=true;        }        @Override        public void onServiceDisconnected(ComponentName name) {            calculateAidl = null;            isBindSuccess=false;        }    };

沒啥好多的!有關AIDL,詳情請看Android IPC之AIDL淺談。

好了,有關Messenger的源碼分析就這麼多!

四、小結。

1.Messenger的底層還是使用的AIDL;

2.服務端和用戶端通訊,向服務端發現訊息,使用的是服務端的Messenger(該對象來自於遠端連線成功通過IBinder擷取),向用戶端發送訊息,使用的是用戶端的Messenger(該對象是向服務端發送的訊息中攜帶的-replyTo屬性攜帶的參數);

3.在IPC中,要傳遞參數,需要實現Parcelable介面,建議使用Bundle封裝參數。

4.Messenger會將所有Service請求入隊列,所以它不支援多線程通訊。如果你要支援多線程,那麼請使用AIDL。

Android IPC之Messenger淺談

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.