Android基于Binder自行實現跨進程通信邏輯

leomos 8年前發布 | 9K 次閱讀 AIDL Android開發 移動開發

大家都知道在Android系統中跨進程通信是件很重要的事,app跟系統重要的系統服務進行交互都要進行跨進程通信,例如開啟一個新的Activity、獲取定位信息等。跨進程間通信方式有著不同實現方式,Android采用的是Binder方式進行處理的,為什么會選擇Binder以及Binder實現機制,網上已經有不少文章進行了深度分析,這里就不浪費筆墨了。除了與重要的系統服務需要進行跨進程通信。在我們的app里如果有remote service,app其他組件與這個service進行交互時,也是需要通過使用Binder進行消息通訊的。 一般情況我們用的是AIDL進行處理的。 不知道AIDL怎么使用的同學可以隨手Google下,這里也不在贅述了(不過大家如果想了解這塊內容也可以留言,有時間可以把AIDL相關的東西整理下給大家看一下)。現在換一種做法,直接使用Binder的接口來實現一下跨進程通訊。這里可能需要有一點的預備知識,就是了解一下這個Binder基本的接口是什么樣的,簡單來說是Client端調用一下transact函數,然后Service端會在onTransact函數中收到相關參數然后進行處理,處理完成之后將結果返回到Client端。

首先看下Service端代碼:

public class BinderService extends Service {
@Nullable 
@Override 
public IBinder onBind(Intent intent) {
return new TestBinder();
}
//實際執行的業務邏輯 
public String testStart(String tag, int data) {
Log.i("TestFlag", "--------Test testStart-------::" + tag + " " + data);
return "back from service is " + tag + ":" + data;
}
//Binder,大家可以對比思考想一下,使用AIDL時是怎樣處理的 
class TestBinder extends Binder {
/** * 接收客戶端的請求,解析然后進行處理 */ 
protected boolean onTransact(int code, Parcel data, Parcel reply,int flags) throws RemoteException {
//根據不同code,進行不同方法處理,例如一個Service可能實現不同功能,每個功能可以對應一個code 
switch (code)
 {
//其中一個分支 
case 1001: {
//按順序讀取,client發送過來的內容 
String testStr = data.readString();
int testInt = data.readInt();
String result = testStart(testStr, testInt);
//將返回的結果發送回去
reply.writeNoException();
reply.writeString(result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}
}

了解過Binder基本機制的同學都清楚,Binder會是一個client調用transact方法,服務端接收onTransact的過程,那看下客戶端的代碼:

private IBinder clientBinder;
//首先綁定Service,獲取Service Binder的BinderProxy
Intent intent = new Intent(AActivity.this, BinderService.class);
bindService(intent, new ServiceConnection() {
@Override 
public void onServiceConnected(ComponentName name, IBinder service) {
clientBinder = service;
}
@Override 
public void onServiceDisconnected(ComponentName name) {
}
}, Context.BIND_AUTO_CREATE);

現在我想調用BinderService的testStart功能,那客戶端的實現如此:

Parcel data = Parcel.obtain();
//按約定順序組織參數
data.writeString("testflag");
data.writeInt(10);
Parcel reply = Parcel.obtain();
try {
//testStart的code為1001,reply負責接收service返回的數據 
clientBinder.transact(1001, data, reply, 0);
reply.readException();
Log.i("TestFlag", reply.readString());
} 
catch (RemoteException e) {
e.printStackTrace();
}

這就完成了客戶端調用Service端的過程,大家有興趣看下AIDL生成的類,其實幫大家做了一些事情,把接口映射成不同的code,不同的接口參數按照順序進行組織,節省了大家的時間,并且減少了出錯的可能性。

這是基本的跨進程調用過程,我們利用接口自行搞了一個簡單的輪子處理了下相關功能。但是不知道大家有沒有這樣的需求或者疑問,上邊的實現方式本質為客戶端調用服務端,然后服務端接著返回結果到客戶端的模式。但是我們可能有這樣的需求,客戶端調用服務端一個需求后,服務端可能要執行一個耗時操作,再結束之后再將結果推送回客戶端的需求呢。當然實現這個一個方式,系統已經給我們提供了一個很好的實現,也就是Messenger,對于Messenger實現有疑問的同學搜索下吧。這里還是用自己使用Binder來造輪子,來理解系統的實現。

首先先想下客戶端向服務端發送消息時,我們在綁定Service的時候,能拿到Service在客戶端的BinderProxy,然后調用transact,即可以實現調用。而服務端向客戶端發消息的難點就在于服務端如何難道客戶端Binder的BinderProxy,所以問題的解決在于如何將Binder從客戶端傳遞到服務端。不廢話上代碼:

首先看Service端代碼,與上邊類似,多加了一個方法:

package launchmode.test.com.testlaunchmode;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
public class BinderService extends Service {
@Nullable 
@Override public IBinder onBind(Intent intent) {
return new TestBinder();}
//實際執行的業務邏輯 
public String testStart(String tag, int data) {
Log.i("TestFlag", "--------Test testStart-------::" + tag + " " + data);
return "back from service is " + tag + ":" + data;
}
public void runTask(final IBinder binder){
new Thread(){
public void run(){
//模擬耗時操作 
try {Thread.sleep(1000);} 
catch (InterruptedException e) 
{e.printStackTrace();}
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
binder.transact(1001,data,reply,0);
} 
catch (RemoteException e) {
e.printStackTrace();
}
}
}.start();
}
//Binder 
class TestBinder extends Binder {
/** * 接收客戶端的請求,解析然后進行處理 */ 
protected boolean onTransact(int code, Parcel data, Parcel reply,int flags) throws RemoteException {
//根據不同code,進行不同方法處理,例如一個Service可能實現不同功能,每個功能可以對應一個code 
switch (code) {
//其中一個分支 
case 1001: {
//按順序讀取,client發送過來的內容 
String testStr = data.readString();
int testInt = data.readInt();
String result = testStart(testStr, testInt);
//將返回的結果發送回去 
reply.writeNoException();
reply.writeString(result);
return true;
}
case 1002:
//獲取客戶端的Binder 
IBinder binder=data.readStrongBinder();
runTask(binder);
int result=1;
//將返回的結果發送回去 
reply.writeNoException();
reply.writeInt(result);
return true;
}
return super.onTransact(code, data, reply, flags);
}
}
}

客戶端代碼:

1:客戶端Binder

//Binder
static class ClientBinder extends Binder {
/** * 接收客戶端的請求,解析然后進行處理 */ 
protected boolean onTransact(int code, Parcel data, Parcel reply,int flags) throws RemoteException {
switch (code) {
case 1001: {
Log.i("TestFlag","-------Client Client------------");
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}

2:客戶端調用地方

Parcel data = Parcel.obtain();
data.writeStrongBinder(new ClientBinder());
Parcel reply = Parcel.obtain();
try { 
clientBinder.transact(1002, data, reply, 0);
reply.readException();
Parcel data = Parcel.obtain();
data.writeStrongBinder(new ClientBinder());
Parcel reply = Parcel.obtain();
try {
//testStart的code為1001,reply負責接收service返回的數據 
clientBinder.transact(1002, data, reply, 0);
reply.readException();
Log.i("TestFlag", "result is "+reply.readInt());
} 
catch (RemoteException e) {
e.printStackTrace();
}
} catch (RemoteException e) {
e.printStackTrace();
}

這就是基本的過程,在系統的源碼中,涉及到與ActivityMangerService打交道的地方會有相似的邏輯。

來自:http://www.jianshu.com/p/09f2ecb8237a

 

 本文由用戶 leomos 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!