有了上一篇《Android的進階學習(四)--AIDL的使用與理解》的知識后叔营,現在我們看Messenger
就會更加容易了妓忍。所以味咳,如果你還沒有看《Android的進階學習(四)--AIDL的使用與理解》孤页,推薦看后再看這篇。
Messenger
Messenger
就是基于Message
的進程間通信,也就是我們可以向在線程間利用Handler.send(Message)
一樣天通,所以用起來是非常簡單的。
由上篇文章儡蔓,我們知道我們可以編寫aidl
文件來進行進程間的通訊,而現在疼邀,我們用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ā)送數據給客戶端
Message msgTo = Message.obtain();
msgTo.arg1 = 0X0002;
Bundle bundle = new Bundle();
bundle.putString("content", "我是從服務器來的字符串");
msgTo.setData(bundle);
try {
//注意尝哆,這里把數據從服務器發(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`的構造函數中出入了一個`Handler`對象较解。然后在`onBind`方法中返回了一個`messenger.getBinder()`,思路也是相當的清晰赴邻。我們注意到在`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`中,當然可能你已經發(fā)現這個就是`Service`中我們調用的`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
轉換成的是Messnger
而已,接著就調用而messenger.send(message);
,就這樣之剧,我們的消息就發(fā)送出了郭卫,和同一線程里面的消息發(fā)送幾乎一樣,只是這里的handmessage(Message message)
是在不同的進程背稼。
當然贰军,為了接受從服務端發(fā)來的消息,我們在Activity
中定義了一個Messenger
,并且在客戶端發(fā)給服務端的消息(Message)中把客戶端的Messenger
給賦值進Message
中蟹肘。
就這樣词疼,最簡單的Messenger
就完成了。
Demo運行結果
客戶端:
服務端:
可見疆前,當我們的服務端接收到消息后寒跳,就向客戶端發(fā)出了一條消息。這種情況在我們的項目中也是經持窠罚可見的童太,例如,你下載一個很大的東西時胸完,就直接開一個進程去下书释,讓后下載完成后再將文件路徑給發(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
的構造函數中發(fā)現了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ā)現MessengerImpl
就是Hnadler
的一個內部類鳄袍,然后繼承于IMessenger.Stub
±艄В看到這里拗小,或許就已經發(fā)現了,這和aidl
中生成的類不是一樣的嗎砸泛?的確十籍,這里就是實現的一個aidl
文件。在這個類中唇礁,實現了以一個send
方法勾栗,這個方法中調用了Handler.sendMessage()
方法,這也就是為什么盏筐,我們的消息來得時候會出現在handlerMessage()
中围俘。這里,我們在理一下琢融,客戶端在調用send
方法的時候界牡,由于在不同的進程,傳送的數據先回被序列化漾抬,然后進行跨進程傳送宿亡,最后到了服務端進行解析,對應調用send
方法執(zhí)行纳令。
不知道是否還記得挽荠,我們在用aidl
通信的時候,我們在Service
中首先要生成一個XXXX.Stub
的實現類平绩,然后再在onBind()
中返回一個XXXX.Stub
的引用圈匆。
現在我們在回首看一下,我們在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
又是怎樣來的婿脸?
聯系上面的demo這就很說明問題了粱胜,我們假設進程A是客戶端,B是服務端狐树。那么進程A(Activity)中通過ServiceConnection
就得到了進程B(Service)中Messenger
中包裝的Binder
,在自己包裝一下焙压,就是MessengerB
了。這也和上一篇中的在客戶端調用aidl
文件接口方法聯系起來抑钟。
然后涯曲,我們需要知道的就是進程B(Service)中是怎樣有進程A(Activity)的MessengerA
的了?其實我想你已經明白了在塔,就是在進程A給進程B發(fā)送的Message
中幻件,我們把MessengerA
給傳了過去』桌#回想上面的代碼绰沥,我們在客戶端進行的message.replyTo = aMessenger;
和在服務端進行的msg.replyTo.send(msgTo);
就是把MessengerA
給進行寫入和讀取。現在是不是有一種大徹大悟的感覺城榛?如果沒有揪利,那就慢慢再理一下吧!
aidl實現雙向通信
看了Messenger
的實現方式狠持,我們想一下用aidl
來實現客戶端與服務端的雙向通信也是很容易的,那就在上一篇文章的基礎上理一下思路吧疟位。
1.我們在服務端aidl
定義一個方法,接受一個Binder喘垂。
2.在客戶端中我們也通過aidl
生成一個Binder
,然后當我們通過ServicConnection
獲取到服務端的Binder
后甜刻,再調用接受Bindler
的那個方法绍撞,并把客戶端中生成的Binder
給傳進去。
3.服務端相應客戶端請求的方法時得院,在調用客戶端Binder
發(fā)送消息即可傻铣。
基本思路就是這樣,當然肯定沒有谷歌的Messenger
完美祥绞,但是可以加強我們對aidl
和Messenger
的理解了非洲。
總結
搞什么嘛,最后還不就是aidl
蜕径,就是封裝了一下而已两踏。這也就是文章開頭所說的不顯式使用aidl
。
最后
還有兜喻,這些是我參考《Android開發(fā)藝術探索》的梦染,對,就是任大大的朴皆。