程序間通信
說到Android程序間通信,大家肯定能想到的是編寫aidl檔案,然後通過aapt生成的類友善的完成服務端,以及用戶端代碼的編寫。
為什麼要寫這個架構
曾經聽過一個人說過,程式中盡量使用通用元件或者架構,這樣可以盡量的少寫代碼,這樣有兩個好處,一是少寫代碼就意味着少犯錯誤,二是可以多出喝咖啡的時間 ;
使用
手寫Binder方式有兩種方式
1.使用通用的IPCService服務庫,包括與Activity通信
服務端
服務端可以通過Activity 啟動也可以不啟動,若不啟動可以通過繼承BaseIPCService的方式,具體實作檢視SimpleService
public class MainActivity extends AppCompatActivity {
private ServiceUtils mServiceUtils;
private static final String TAG = "jcy_service";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mServiceUtils = new ServiceUtils(this);
}
@Override
protected void onResume() {
super.onResume();
//調用預設IPC 服務
mServiceUtils.setConnectListener(mConnectListener);
mServiceUtils.bindService();
}
@Override
protected void onStop() {
super.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
mServiceUtils.unBindService();
}
ConnectListener mConnectListener = new ConnectListener() {
@Override
public void onBind() {
}
@Override
public void onDisconnected() {
}
@Override
public void onConnected(boolean success) {
mServiceUtils.setReceiverListener(mReceiverListener);
}
@Override
public void onUnbind() {
mServiceUtils.setReceiverListener(null);
}
;
};
private ReceiverListener mReceiverListener = new ReceiverListener() {
@Override
public Bundle receiveInfo(int code, Bundle msg) {
String msgStr = msg.getString("msgStr");
int msgInt = msg.getInt("msgInt");
Log.d(TAG, "receiveInfo: msgStr " + msgStr + " ,msgInt : " + msgInt);
Bundle ret = new Bundle();
ret.putString("return", "服務端傳回 code " + code + " ,msgStr " + msgStr + " ,msgInt : " + msgInt);
return ret;
}
};
}
用戶端
public class MainActivity extends AppCompatActivity {
private static final String TAG = "jcy_client_one";
private TextView mTextView;
private TextView tv_state;
private ClientUtils mClientUtils;
private Button btnGet, btn_bind, btn_unbind;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.tv_service);
tv_state = (TextView) findViewById(R.id.tv_state);
btnGet = (Button) findViewById(R.id.btn_getService);
btn_bind = (Button) findViewById(R.id.btn_bind);
btn_unbind = (Button) findViewById(R.id.btn_unbind);
btnGet.setEnabled(false);
tv_state.setText(" 服務未綁定 ");
mClientUtils = new ClientUtils(this);
mClientUtils.setConnectListener(mConnectListener);
btn_bind.setEnabled(true);
btn_unbind.setEnabled(false);
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
if(mClientUtils!=null){
mClientUtils.unBindService();
}
}
public void onGetServie(View v) {
if (mClientUtils.isConnect()) {
Bundle send = new Bundle();
send.putString("msgStr", "ClientOneApp發送消息");
send.putInt("msgInt", 110);
try {
Bundle ret = mClientUtils.send(110, send);
if (ret == null) {
Log.e(TAG, "onGetServie: ret==null");
} else {
String retStr = ret.getString("return");
mTextView.append("\n"+retStr);
}
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "onGetServie:Exception " + e.toString());
}
} else {
Toast.makeText(this, "未連接配接服務,無法擷取消息", Toast.LENGTH_SHORT).show();
}
}
public void onBindServie(View v) {
mClientUtils.bindService();
}
public void onUnBindServie(View v) {
mClientUtils.unBindService();
}
private ConnectListener mConnectListener = new ConnectListener() {
@Override
public void onBind() {
tv_state.setText(" 服務正在綁定中…… ");
}
@Override
public void onDisconnected() {
btnGet.setEnabled(false);
tv_state.setText(" 服務斷開連接配接 ");
btn_bind.setEnabled(true);
btn_unbind.setEnabled(false);
}
@Override
public void onConnected(boolean success) {
if (success) {
tv_state.setText(" 服務綁定成功 ");
btn_bind.setEnabled(false);
btn_unbind.setEnabled(true);
btnGet.setEnabled(true);
} else {
btnGet.setEnabled(false);
tv_state.setText(" 服務綁定失敗 ");
mClientUtils.unBindService();
btn_bind.setEnabled(true);
btn_unbind.setEnabled(false);
}
}
@Override
public void onUnbind() {
btn_bind.setEnabled(true);
btn_unbind.setEnabled(false);
tv_state.setText(" 服務已解除綁定 ");
mTextView.setText("");
}
};
}
另外還支援自定義Service 的方式 具體檢視SimpleService類
Message的使用方式與自定義Binder的形式類似,大部分使用方式幾乎相同
分析
手寫Binder
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI5YjN5E2N3MzM1cTNvwFcvwVbvNmL1h2cuFWaq5yd3d3Lc9CX6MHc0RHaiojIsJye.jpg)
Binder.png
這個圖是盜用别人的嘿嘿;
首先Activity通過調用bindService去綁定一個遠端的服務(Service),綁定成功後傳回一個IBinder對象。這時候雙方就算是建立了連接配接了。
建立連接配接之後,雙方就可以通過持有的IBinder進行通信。Activity使用IBinder的transact方法去給底層的Binder Driver(Linux層)發送消息間接調用底層的IBinder的execTransact方法。
而execTransact導緻的結果就是調用onTransact方法。那麼這時候事件的處理就可以在該環節進行了。
上層主要使用 這個方法進行通信 處理消息;
public boolean onTransact(int code, android.os.Parcel data,
android.os.Parcel reply, int flags)
這個方法 一般情況下 都是傳回true的,也隻有傳回true的時候才有意義,如果傳回false了 就代表這個方法執行失敗,onTransact 這個方法 就是運作在Binder線程池中的,一般就是用戶端發起請求,然後android底層代碼把這個用戶端發起的,請求 封裝成3個參數 來調用這個onTransact方法,第一個參數code 就代表通信的标志位,data就是方法參數(用戶端傳遞過來的資料),reply就是方法傳回值(通過這個方法可以向用戶端中傳回資料)。
自定義Binder為
public class IPCBinder extends Binder {
@Override
protected boolean onTransact(final int code, final Parcel data, final Parcel reply, final int flags) throws RemoteException {
data.enforceInterface(Contant.DESCRIPTOR);
Bundle bundle = dealMessage(code,data.readBundle());
reply.writeNoException();
reply.writeBundle(bundle);
return true;
}
public BaseIPCService getService(){
return BaseIPCService.this;
}
}
暫時還未找到如何實作異步通信
用戶端發送消息
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
Log.d(TAG, "--- onServiceDisconnected --- ");
connect = false;
if (mConnectListener != null) {
mConnectListener.onDisconnected();
}
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
if (service == null) {
connect = false;
Log.d(TAG, "--- onServiceConnected failed service == null ---");
if (mConnectListener != null) {
mConnectListener.onConnected(false);
}
} else {
Log.d(TAG, "--- onServiceConnected sucessed ---");
connect = true;
if (mConnectListener != null) {
mConnectListener.onConnected(true);
}
mIBinder = service;
}
}
};
連接配接成功後通過可以擷取到Binder對象
發送消息使用 mIBinder.transact(code, _data, _reply, 0);
public Bundle send(int code, Bundle bundle) throws Exception {
if (mIBinder == null) {
throw new NullPointerException("IBinder is Null");
}
Bundle result = null;
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeBundle(bundle);
mIBinder.transact(code,
_data, _reply, 0);
_reply.readException();
result = _reply.readBundle();
} catch (Exception e) {
e.printStackTrace();
throw new Exception(e);
} finally {
_reply.recycle();
_data.recycle();
return result;
}
}
Message通信
服務端的onBind是這麼寫的:
public IBinder onBind(Intent intent)
{
return mMessenger.getBinder();
}
那麼點進去:
public IBinder getBinder() {
return mTarget.asBinder();
}
可以看到傳回的是mTarget.asBinder();
mTarget是哪來的呢?
HandlerThread mHandlerThread = new HandlerThread("BaseMsgIPCService");
mHandlerThread.start();
Handler mHandler= new Handler(mHandlerThread.getLooper()){
@Override
public void handleMessage(Message msgfromClient)
{
super.handleMessage(msgfromClient);
}
};
mMessenger= new Messenger(mHandler);
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);
}
}
mTarget是一個MessengerImpl對象,那麼asBinder實際上是傳回this,也就是MessengerImpl對象;
用戶端
用戶端首先通過onServiceConnected拿到sevice(Ibinder)對象,這裡沒什麼特殊的,我們平時的寫法也是這樣的,隻不過我們平時會這麼寫:
IMessenger.Stub.asInterface(service)拿到接口對象進行調用;
而,我們的代碼中是
mService = new Messenger(service);
跟進去,你會發現:
public Messenger(IBinder target) { mTarget = IMessenger.Stub.asInterface(target); }
和我們平時的寫法一模一樣!
到這裡就可以明白,用戶端與服務端通信,實際上和我們平時的寫法沒有任何差別,通過編寫aidl檔案,服務端onBind利用Stub編寫接口實作傳回;用戶端利用回調得到的IBinder對象,使用IMessenger.Stub.asInterface(target)拿到接口執行個體進行調用。
(2)服務端與用戶端通信
那麼,用戶端與服務端通信的确沒什麼特殊的地方,我們完全也可以編寫個類似的aidl檔案實作;那麼服務端是如何與用戶端通信的呢?
還記得,用戶端send方法發送的是一個Message,這個Message.replyTo指向的是一個mMessenger,我們在Activity中初始化的。
那麼将消息發送到服務端,肯定是通過序列化與反序列化拿到Message對象,我們看下Message的反序列化的代碼:
# Message
private void readFromParcel(Parcel source) {
what = source.readInt();
arg1 = source.readInt();
arg2 = source.readInt();
if (source.readInt() != 0) {
obj = source.readParcelable(getClass().getClassLoader());
}
when = source.readLong();
data = source.readBundle();
replyTo = Messenger.readMessengerOrNullFromParcel(source);
sendingUid = source.readInt();
}
主要看replyTo,調用的是Messenger.readMessengerOrNullFromParcel
public static Messenger readMessengerOrNullFromParcel(Parcel in) {
IBinder b = in.readStrongBinder();
return b != null ? new Messenger(b) : null;
}
public static void writeMessengerOrNullToParcel(Messenger messenger,
Parcel out) {
out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder()
: null);
}
通過上面的writeMessengerOrNullToParcel可以看到,它将用戶端的messenger.mTarget.asBinder()對象進行了恢複,用戶端的message.mTarget.asBinder()是什麼?
用戶端也是通過Handler建立的Messenger,于是asBinder傳回的是:
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
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);
}
}
public IBinder getBinder() {
return mTarget.asBinder();
}
那麼asBinder,實際上就是MessengerImpl extends IMessenger.Stub
中的asBinder了。
#IMessenger.Stub
@Override
public android.os.IBinder asBinder()
{
return this;
}
那麼其實傳回的就是MessengerImpl對象自己。到這裡可以看到message.mTarget.asBinder()其實傳回的是用戶端的MessengerImpl對象。
最終,發送給用戶端的代碼是這麼寫的:
msgfromClient.replyTo.send(msgToClient);
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
這個mTarget實際上就是對用戶端的MessengerImpl對象的封裝,那麼send(message)(屏蔽了transact/onTransact的細節),這個message最終肯定傳到用戶端的handler的handleMessage方法中。
Message源碼分析部分摘抄自鴻洋_的部落格
歡迎大家follow,star,如果發現bug,希望您能夠及時聯系我