Android的進階學習(五)--Messenger的使用和理解
有了上一篇《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);
//傳送資料給客戶端
Message msgTo = Message.obtain();
msgTo.arg1 = 0X0002;
Bundle bundle = new Bundle();
bundle.putString("content", "我是從伺服器來的字串");
msgTo.setData(bundle);
try {
//注意,這裡把資料從伺服器發出了
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`中我們接受到訊息後又給客戶端傳送了一條訊息`msg.replyTo(msgTo)`,恩,`Messnger`是可以相互傳送訊息的。當然,這也就要求在客戶端和服務端都有自己的`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`中,當然可能你已經發現這個就是`Service`中我們呼叫的`msg.replyTo`了。
message.replyTo = aMessenger;
Bundle bundle = new Bundle();
bundle.putString("content", "我就是Activity傳過來的字串");
message.setData(bundle);
try {
//訊息從客戶端發出
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);
,就這樣,我們的訊息就傳送出了,和同一執行緒裡面的訊息傳送幾乎一樣,只是這裡的handmessage(Message message)
是在不同的程式。
當然,為了接受從服務端發來的訊息,我們在Activity
中定義了一個Messenger
,並且在客戶端發給服務端的訊息(Message)中把客戶端的Messenger
給賦值進Message
中。
就這樣,最簡單的Messenger
就完成了。
Demo執行結果
客戶端:
服務端:
可見,當我們的服務端接收到訊息後,就向客戶端發出了一條訊息。這種情況在我們的專案中也是經常可見的,例如,你下載一個很大的東西時,就直接開一個程式去下,讓後下載完成後再將檔案路徑給傳送給客戶端......當然,這個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
的建構函式中發現了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);
}
}
在getIMessenger()
中就簡單的判斷一下mMessenger
是否存,不存在的話就new
一個返回,存在的話就直接返回,還是很簡單的。接著我們就看看這個MessengerImpl
到底是個什麼東西?
由原始碼我們發現MessengerImpl
就是Hnadler
的一個內部類,然後繼承於IMessenger.Stub
。看到這裡,或許就已經發現了,這和aidl
中生成的類不是一樣的嗎?的確,這裡就是實現的一個aidl
檔案。在這個類中,實現了以一個send
方法,這個方法中呼叫了Handler.sendMessage()
方法,這也就是為什麼,我們的訊息來得時候會出現在handlerMessage()
中。這裡,我們在理一下,客戶端在呼叫send
方法的時候,由於在不同的程式,傳送的資料先回被序列化,然後進行跨程式傳送,最後到了服務端進行解析,對應呼叫send
方法執行。
不知道是否還記得,我們在用aidl
通訊的時候,我們在Service
中首先要生成一個XXXX.Stub
的實現類,然後再在onBind()
中返回一個XXXX.Stub
的引用。
現在我們在回首看一下,我們在Service
中返回的mTarget.asBinder()
其實就是MessengerImpl
,當然asBinder()
返回的就是自己本身。
當然,還有個問題我們得注意,就是服務端給客戶端傳送的訊息。從demo中我們知道,我們在Serivice
中使用msg.replyTo.send(msgTo);
進行向客戶端傳送訊息。而msg
的replyTo
這個屬性在客戶端中就是由客戶端生成的Messenger
。其實這裡也是好理解的,首先,我們這裡的跨程式主要就是通過Binder
的,即我們在客戶端和服務端傳遞Binder
來進行通訊。而這裡的Messnger
也就是一個包裝了的Binder
。
圖畫得很簡單,但是也足以說明問題了。這裡我們有兩個程式,一個程式A,一個程式B,其中每個程式中我們都生成了一個相應的程式。接著就該注意一下了,當我們從A到B傳送訊息時,我們使用的是MessengerB.send(Message)
,B到A的時候是MessengerA.send(Message)
,這時候你可能就納悶了,為什麼不是用相應的傳送?程式A中的MessengerB
又是怎樣來的?
聯絡上面的demo這就很說明問題了,我們假設程式A是客戶端,B是服務端。那麼程式A(Activity)中通過ServiceConnection
就得到了程式B(Service)中Messenger
中包裝的Binder
,在自己包裝一下,就是MessengerB
了。這也和上一篇中的在客戶端呼叫aidl
檔案介面方法聯絡起來。
然後,我們需要知道的就是程式B(Service)中是怎樣有程式A(Activity)的MessengerA
的了?其實我想你已經明白了,就是在程式A給程式B傳送的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
傳送訊息即可。
基本思路就是這樣,當然肯定沒有谷歌的Messenger
完美,但是可以加強我們對aidl
和Messenger
的理解了。
總結
搞什麼嘛,最後還不就是aidl
,就是封裝了一下而已。這也就是文章開頭所說的不顯式使用aidl
。
最後
我一個苦逼的大學生,而且專業還不對口,希望寫寫東西,加強理解與記憶,同時也希望自己的理解能幫到更多的人。所以,有什麼錯誤的,寫得不好的,希望指出,共同進步。
還有,這些是我參考《Android開發藝術探索》的,對,就是任大大的。
相關文章
- create table進階學習系列(五)
- Blazor和Vue對比學習(進階2.1.1):生命週期,基本理解和使用BlazorVue
- Android UI學習 - Tab的學習和使用AndroidUI
- HBase學習的第五天--HBase進階結尾和phoenix開頭
- Go語言學習的五個進化階段(帶例子)Go
- [Android進階]Binder學習(初始篇)Android
- Android 多程式之Messenger的使用AndroidMessenger
- Android多程式之Messenger的使用AndroidMessenger
- Linux學習的五個臺階Linux
- 學習python的進階之路Python
- Android IPC程式間通訊之AIDL和Messenger的使用AndroidC程式AIMessenger
- Android進階(五)View繪製流程AndroidView
- Node進階學習
- Android學習之路五:Dialog和ToastAndroidAST
- Hive學習之五 《Hive進階—UDF操作案例》 詳解Hive
- Blazor和Vue對比學習(進階.路由導航一):基本使用BlazorVue路由
- 一個極好的學習,進階方法
- Java進階容器學習Java
- JS學習理解之閉包和高階函式JS函式
- RxJS進階——關於流的理解和應用JS
- 學習SVM(五)理解線性SVM的鬆弛因子
- 高階前端進階(五)前端
- Java進階學習之Java架構師的學習路線Java架構
- android學習筆記五Android筆記
- Android的IPC機制(四)—— Messenger的使用及原始碼分析AndroidMessenger原始碼
- Android 你應該知道的學習資源 進階之路貴在堅持Android
- Android 進階17:Fragment FragmentManager FragmentTransaction 深入理解AndroidFragment
- 學習java的路線圖-五個必經階段Java
- Go 進階學習筆記Go筆記
- Java學習路線·進階Java
- c++學習進階之路
- Swift進階學習筆記Swift筆記
- Git進階學習筆記Git筆記
- JavaScript學習8:DOM進階JavaScript
- create table進階學習(一)
- create table進階學習(三)
- create table進階學習(四)
- Android學習進階路線導航線路(Android原始碼分享)Android原始碼