有了上一篇《Android的進階學習(四)--AIDL的使用與理解》的知識后,現(xiàn)在我們看Messenger就會更加容易了。所以,如果你還沒有看《Android的進階學習(四)--AIDL的使用與理解》,推薦看后再看這篇。
Messenger
Messenger就是基于Message的進程間通信,也就是我們可以向在線程間利用Handler.send(Message)一樣,所以用起來是非常簡單的。
由上篇文章,我們知道我們可以編寫aidl文件來進行進程間的通訊,而現(xiàn)在,我們用Messnger就不需要顯式使用aidl文件了,為什么說不是顯式呢?看完你就懂了。
Messenger的使用
明白一個類的原理前,首先就因該學會使用,畢竟我們的最終目的是搞明白原理,讓我們能夠更好的使用。
這里我們還是選擇一個遠程的Service和Activity之間的通信吧。
Service服務端
首先,我們寫一個服務端Service:
public class MyService extends Service {
public final static String TAG = "MyService";
public final static int SERVICEID = 0x0001;
private Messenger messenger = new Messenger(new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.arg1 == SERVICEID) {
//接受從客戶端傳來的消息
Log.d(TAG, "客服端傳來的消息===>>>>>>");
String str = (String) msg.getData().get("content");
Log.d(TAG, str);
//發(fā)送數(shù)據(jù)給客戶端
Message msgTo = Message.obtain();
msgTo.arg1 = 0X0002;
Bundle bundle = new Bundle();
bundle.putString("content", "我是從服務器來的字符串");
msgTo.setData(bundle);
try {
//注意,這里把數(shù)據(jù)從服務器發(fā)出了
msg.replyTo.send(msgTo);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreat");
}
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}```
看看我們的`Service`服務端,可謂是真的很簡單,我們首先生成的一個`messenger`并個這個`messenger`的構造函數(shù)中出入了一個`Handler`對象。然后在`onBind`方法中返回了一個`messenger.getBinder()`,思路也是相當?shù)那逦?。我們注意到在`Handler`中我們接受到消息后又給客戶端發(fā)送了一條消息`msg.replyTo(msgTo)`,恩,`Messnger`是可以相互發(fā)送消息的。當然,這也就要求在客戶端和服務端都有自己的`Messnger`。
最后,我們要么就直接安裝運行`Service`,要么就注冊`Service`的時候加上`Process`屬性,因為我們要測試的是跨進程通信了。
######Activity客戶端
我們的`Activity`也是相當簡單的,所以還是先上代碼:
public class MainActivity extends AppCompatActivity {
public final static String TAG = "MainActivity";
public final static int ACTIVITYID = 0X0002;
//客戶端的Messnger
private Messenger aMessenger = new Messenger(new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.arg1 == ACTIVITYID) {
//客戶端接受服務端傳來的消息
Log.d(TAG, "服務端傳來了消息=====>>>>>>>");
String str = (String) msg.getData().get("content");
Log.d(TAG, str);
}
}
});
//服務端傳來的Messenger
Messenger sMessenger;
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
sMessenger = new Messenger(service);
Message message = Message.obtain();
message.arg1 = 0x0001;
//注意這里,把`Activity`的`Messenger`賦值給了`message`中,當然可能你已經(jīng)發(fā)現(xiàn)這個就是`Service`中我們調(diào)用的`msg.replyTo`了。
message.replyTo = aMessenger;
Bundle bundle = new Bundle();
bundle.putString("content", "我就是Activity傳過來的字符串");
message.setData(bundle);
try {
//消息從客戶端發(fā)出
sMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "連接Service失敗");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startAndBindService();
}
private void startAndBindService() {
Intent service = new Intent(MainActivity.this, MyService.class);
startService(service);
bindService(service, serviceConnection, Context.BIND_AUTO_CREATE);
}
} ```
這里和上一篇的代碼布局都幾乎是一樣的,只是這里我們把serviceConnection成功返回的IBinder轉(zhuǎn)換成的是Messnger而已,接著就調(diào)用而messenger.send(message);,就這樣,我們的消息就發(fā)送出了,和同一線程里面的消息發(fā)送幾乎一樣,只是這里的handmessage(Message message)是在不同的進程。
當然,為了接受從服務端發(fā)來的消息,我們在Activity中定義了一個Messenger,并且在客戶端發(fā)給服務端的消息(Message)中把客戶端的Messenger給賦值進Message中。
就這樣,最簡單的Messenger就完成了。
Demo運行結果
客戶端:

服務端:

可見,當我們的服務端接收到消息后,就向客戶端發(fā)出了一條消息。這種情況在我們的項目中也是經(jīng)??梢姷模?,你下載一個很大的東西時,就直接開一個進程去下,讓后下載完成后再將文件路徑給發(fā)送給客戶端......當然,這個demo中我們什么都沒有去處理就直接返回了,在服務端是會有邏輯的,這里就沒有過多的演示了。
Messenger的理解
知其所以然,我們就接著看看Messenger到底是何方神圣?
首先,我們還是從Service中看起,還記得我們在Service中new了一個Messenger并且傳入了一個Handler吧,然后在onBind()方法中就返回了一個messenger.getBinder():
public IBinder getBinder() {
return mTarget.asBinder();
}
一看,里面返回的就是mTarget.asBinder(),然后我們就找找mTarget:
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
結果在Messenger的構造函數(shù)中發(fā)現(xiàn)了target.getIMessenger(),繼續(xù),我們看看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);
}
}
在getIMessenger()中就簡單的判斷一下mMessenger是否存,不存在的話就new一個返回,存在的話就直接返回,還是很簡單的。接著我們就看看這個MessengerImpl到底是個什么東西?
由源碼我們發(fā)現(xiàn)MessengerImpl就是Hnadler的一個內(nèi)部類,然后繼承于IMessenger.Stub??吹竭@里,或許就已經(jīng)發(fā)現(xiàn)了,這和aidl中生成的類不是一樣的嗎?的確,這里就是實現(xiàn)的一個aidl文件。在這個類中,實現(xiàn)了以一個send方法,這個方法中調(diào)用了Handler.sendMessage()方法,這也就是為什么,我們的消息來得時候會出現(xiàn)在handlerMessage()中。這里,我們在理一下,客戶端在調(diào)用send方法的時候,由于在不同的進程,傳送的數(shù)據(jù)先回被序列化,然后進行跨進程傳送,最后到了服務端進行解析,對應調(diào)用send方法執(zhí)行。
不知道是否還記得,我們在用aidl通信的時候,我們在Service中首先要生成一個XXXX.Stub的實現(xiàn)類,然后再在onBind()中返回一個XXXX.Stub的引用。
現(xiàn)在我們在回首看一下,我們在Service中返回的mTarget.asBinder()其實就是MessengerImpl,當然asBinder()返回的就是自己本身。
當然,還有個問題我們得注意,就是服務端給客戶端發(fā)送的消息。從demo中我們知道,我們在Serivice中使用msg.replyTo.send(msgTo);進行向客戶端發(fā)送消息。而msg的replyTo這個屬性在客戶端中就是由客戶端生成的Messenger。其實這里也是好理解的,首先,我們這里的跨進程主要就是通過Binder的,即我們在客戶端和服務端傳遞Binder來進行通信。而這里的Messnger也就是一個包裝了的Binder。

圖畫得很簡單,但是也足以說明問題了。這里我們有兩個進程,一個進程A,一個進程B,其中每個進程中我們都生成了一個相應的進程。接著就該注意一下了,當我們從A到B發(fā)送消息時,我們使用的是MessengerB.send(Message),B到A的時候是MessengerA.send(Message),這時候你可能就納悶了,為什么不是用相應的發(fā)送?進程A中的MessengerB又是怎樣來的?
聯(lián)系上面的demo這就很說明問題了,我們假設進程A是客戶端,B是服務端。那么進程A(Activity)中通過ServiceConnection就得到了進程B(Service)中Messenger中包裝的Binder,在自己包裝一下,就是MessengerB了。這也和上一篇中的在客戶端調(diào)用aidl文件接口方法聯(lián)系起來。
然后,我們需要知道的就是進程B(Service)中是怎樣有進程A(Activity)的MessengerA的了?其實我想你已經(jīng)明白了,就是在進程A給進程B發(fā)送的Message中,我們把MessengerA給傳了過去?;叵肷厦娴拇a,我們在客戶端進行的message.replyTo = aMessenger;和在服務端進行的msg.replyTo.send(msgTo);就是把MessengerA給進行寫入和讀取。現(xiàn)在是不是有一種大徹大悟的感覺?如果沒有,那就慢慢再理一下吧!
aidl實現(xiàn)雙向通信
看了Messenger的實現(xiàn)方式,我們想一下用aidl來實現(xiàn)客戶端與服務端的雙向通信也是很容易的,那就在上一篇文章的基礎上理一下思路吧。
1.我們在服務端aidl定義一個方法,接受一個Binder。
2.在客戶端中我們也通過aidl生成一個Binder,然后當我們通過ServicConnection獲取到服務端的Binder后,再調(diào)用接受Bindler的那個方法,并把客戶端中生成的Binder給傳進去。
3.服務端相應客戶端請求的方法時,在調(diào)用客戶端Binder發(fā)送消息即可。
基本思路就是這樣,當然肯定沒有谷歌的Messenger完美,但是可以加強我們對aidl和Messenger的理解了。
總結
搞什么嘛,最后還不就是aidl,就是封裝了一下而已。這也就是文章開頭所說的不顯式使用aidl。
最后
還有,這些是我參考《Android開發(fā)藝術探索》的,對,就是任大大的。