http://blog.csdn.net/lmj623565791/article/details/47017485
一.概述
我們可以在客戶端發送一個Message給服務端,在服務端的handler中會接收到客戶端的消息,然后進行對應的處理,處理完成后,再將結果等數據封裝成Message,發送給客戶端,客戶端的handler中會接收到處理的結果。
有這么幾個特點:
-
基於Message,相信大家都很熟悉
-
支持回調的方式,也就是服務端處理完成長任務可以和客戶端交互
-
不需要編寫aidl文件
此外,還支持,記錄客戶端對象的Messenger,然后可以實現一對多的通信;甚至作為一個轉接處,任意兩個進程都能通過服務端進行通信,這個后面再說。
二.應用
(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(); } }
服務端就一個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>
(二)客戶端
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); } }
代碼也不復雜,首先bindService,然后在onServiceConnected中拿到回調的service(IBinder)對象,通過service對象去構造一個mService =new Messenger(service);然后就可以使用mService.send(msg)給服務端了。
這里插一句,大家通過代碼可以看到服務端往客戶端傳遞數據是通過msg.replyTo這個對象的。那么服務端完全可以做到,使用一個List甚至Map去存儲所有綁定的客戶端的msg.replyTo對象,然后想給誰發消息都可以。甚至可以把A進程發來的消息,通過B進程的msg.replyTo發到B進程那里去。相關代碼呢,可以參考官方的文檔:service,注意下拉找:Remote Messenger Service Sample。
三.原理
源碼解析那篇文章里有非常詳細的分析,這里給出我的個人理解:
先簡單總結一下應用流程:
- 服務器端new Messenger(new Handler()),然后在Handler里定義handleMessage()接收和處理客戶端傳來的message,最后通過message.replyto.send()把結果發送回去
- 客戶端new Messenger(new Handler()),然后在Handler里定義handleMessage()接收和處理服務器端傳來的message。實現ServiceConnection類,並在onServiceConnected里用mService = new Messenger(service)得到遠程接口,bindService連接后用mService.send發送數據給服務器端
(一)客戶端向服務端通信
我們直接看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); } }
其實看到這就知道了,Messenger內部也是用AIDL實現的
下面就直接用AIDL來理解Messenger:
- 首先是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); }
- 然后要在stub中實現這個方法,在onbind中返回stub
private final class MessengerImpl extends IMessenger.Stub { public void send(Message msg) { msg.sendingUid = Binder.getCallingUid(); Handler.this.sendMessage(msg); } }
和AIDL一樣,服務端stub的方法是給客戶端調用的,客戶端利用AIDL把msg傳來,再調用send方法交給服務器端的handleMessage處理
客戶端
得到服務器端的IBinder對象: mService = new Messenger(service);
public Messenger(IBinder target) { mTarget = IMessenger.Stub.asInterface(target); }
(二)服務端與客戶端通信
客戶端向服務器端發送消息:
Message msgFromClient = Message.obtain(null, MSG_SUM, a, b); msgFromClient.replyTo = mMessenger; if (isConn) { //往服務端發送消息 mService.send(msgFromClient); }
客戶端利用AIDL把msg傳過去,再調用send方法交給服務器端的handleMessage處理
服務器接收並處理消息:
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); }
取出客戶端發過來的message,處理后用message.replyTo.send()就可以直接把結果發回去
這里的replyTo是客戶端指定的
msgFromClient.replyTo = mMessenger;
mMessenger就是客戶端的Messenger對象。服務器端在反序列化客戶端傳來的message時,會通過這個replyTo得到客戶端的MessengerImpl對象。
換句話說,服務器端通過客戶端的stub里的send方法,把message結果發送回去,最后在客戶端的handlerMessage里處理
總結下:
-
客戶端與服務端通信,利用的aidl文件,沒什么特殊的
-
服務端與客戶端通信,主要是在傳輸的消息上做了處理,讓Messager.replyTo指向的客戶端的Messenger,而Messenger又持有客戶端的一個Binder對象(MessengerImpl)。服務端正是利用這個Binder對象做的與客戶端的通信。
上面提到過通過Meaager跨進程不適合並發量大的情況,那么如果並發量大的話,我們用什么來處理呢?那就可以通過AIDL來進行,這里是Google的描述
Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want
to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you
should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle
multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before
implementing an AIDL.
主要意思就是你可以用Messager處理簡單的跨進程通信,但是高並發量的要用AIDL