Binder是Android一個十分重要進程間通訊機制,Android系統的不少核心服務AMS,PMS,WMS的使用都是創建在Binder之上的。在對Activity啓動流程,App安裝流程源碼梳理過程當中,Binder也是咱們常常碰到的。所以,在咱們閱讀這些源碼以前,要弄清Binder是如何使用的。java
咱們知道,Binder是基於C/S結構的,就像http接口請求調用。 這裏咱們想一想調用接口時,客戶端和服務端作了什麼:android
Binder和以上流程相似,咱們使用AS分別建立Client,Server兩個項目。咱們模擬一個考試成績查詢場景,即經過學生名稱在服務端查詢該學生的成績。 那麼在客戶端,就有了以下實現:git
//學生
public class Student implements Parcelable {
public String name;
...
}
複製代碼
public class ScoreProxy {
private IBinder mRemote;
private static final int TRANSACTION_query = 1;
public ScoreProxy(IBinder mRemote) {//經過IBinder對象想服務端發送數據
this.mRemote = mRemote;
}
public int query(Student student) {
Parcel _data = android.os.Parcel.obtain();
Parcel _reply = android.os.Parcel.obtain();
int result = -1;
try {
_data.writeInterfaceToken("ScoreQuery");
_data.writeParcelable(student, 0);
mRemote.transact(TRANSACTION_query, _data, _reply, 0);
result = _reply.readInt();
} catch (Exception e) {
e.printStackTrace();
} finally {
_reply.recycle();
_data.recycle();
}
return result;
}
}
複製代碼
在query
方法中,咱們經過·_data·傳入Student參數,經過_reply
接收查詢結果,經過IBiner
對象發送數據。github
在服務端,一樣建立Student
類(包名相同),而後新建一個ScoreQueryService
服務,並經過ScoreStub
Binder類用於接收處理客戶端傳遞端數據。數據庫
public class ScoreQueryService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new ScoreStub();
}
private static class ScoreStub extends Binder {
private static final int TRANSACTION_query = 1;
private Map<String, Integer> scoreMap = new HashMap<>();//模擬數據查詢
public ScoreStub() {
scoreMap.put("張三", 100);
scoreMap.put("李四", 89);
scoreMap.put("王五", 60);
}
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
if (code == TRANSACTION_query) {
data.enforceInterface("ScoreQuery");
Student student = data.readParcelable(Student.class.getClassLoader());
int score = query(student);
Log.e("Server","query:"+student+",result:"+score);
reply.writeInt(score);
return true;
}
return super.onTransact(code, data, reply, flags);
}
private int query(Student s) {
Integer score = scoreMap.get(s.getName());
return score != null ? score : -1;
}
}
}
複製代碼
在清單文件中註冊這個服務bash
<service android:name="com.iamyours.service.ScoreQueryService" android:enabled="true" android:exported="true">
<intent-filter>
<action android:name="com.iamyours.score" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
複製代碼
在安裝完Server
apk後,在Client端調用成績查詢服務,以下app
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
query();
}
});
bindServices();
}
private ScoreProxy scoreProxy;
private void bindServices() {
Intent intent = new Intent();
intent.setAction("com.iamyours.score");
intent.setPackage("com.iamyours.server");//Server端applicationId
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("Client", "onServiceConnected");
scoreProxy = new ScoreProxy(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("Client", "onServiceDisconnected:");
}
}, BIND_AUTO_CREATE);
}
private void query() {
Student s = new Student("張三");
int result = scoreProxy.query(s);
Log.e("client", "result:" + result);
s = new Student("李四");
result = scoreProxy.query(s);
Log.e("client", "result:" + result);
s = new Student("馬雲");
result = scoreProxy.query(s);
Log.e("client", "result:" + result);
}
}
複製代碼
最終調用結果以下: 服務端ide
com.iamyours.server E/Server: query:Student{name='張三'},result:100
com.iamyours.server E/Server: query:Student{name='李四'},result:89
com.iamyours.server E/Server: query:Student{name='馬雲'},result:-1
複製代碼
客戶端ui
com.iamyours.client E/client: result:100
com.iamyours.client E/client: result:89
com.iamyours.client E/client: result:-1
複製代碼
至此咱們簡單經過Binder實現一個成績查詢服務。this
會看上面的代碼,咱們發現不少代碼是耦合在一塊兒的,ScoreProxy
與ScoreStub
有許多和業務無關的代碼,若是一個功能,數據的發送接收處理會產生相似的模版代碼。實際業務開發場景下,在客戶端咱們只須要定義接口方法,參數(就像Retrofit),並使用它。在服務端,咱們應該根據接口實現對應的業務邏輯。在它們中間數據如何傳輸,如何處理卻不是咱們關心的。 所以,在使用時,只須要定義好接口,而且在服務端實現它便可。而中間的數據傳輸相關的代碼是通用相似的,咱們能夠經過APT
生成。 好比咱們定義了一個ISayHello
的接口以下,並用自定義註解@AIDL
聲明它:
@AIDL
public interface ISayHello {
void sayHello();
int sayHelloTo(String name);
int query(Student s);
}
複製代碼
最終咱們但願自動生成在客戶端的ISayHelloProxy
代理類以及服務端的實現類ISayHelloStub
實現類,大概是這樣的:
public abstract class ISayHelloStub extends Binder implements ISayHello {
private static final String DESCRIPTOR = "com.iamyours.interfaces.ISayHello";
private static final int TRANSACTION_sayHello = android.os.IBinder.FIRST_CALL_TRANSACTION + 0;
private static final int TRANSACTION_sayHelloTo = android.os.IBinder.FIRST_CALL_TRANSACTION + 1;
private static final int TRANSACTION_query = android.os.IBinder.FIRST_CALL_TRANSACTION + 2;
public static ISayHello asInterface(IBinder iBinder) {
return new Proxy(iBinder);
}
@Override
public abstract void sayHello();
@Override
public abstract int sayHelloTo(String var0);
@Override
public abstract int query(Student var0);
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
String descriptor = DESCRIPTOR;
switch(code){
case TRANSACTION_sayHello:{
data.enforceInterface(descriptor);
this.sayHello();
reply.writeNoException();
return true;
}
case TRANSACTION_sayHelloTo:{
data.enforceInterface(descriptor);
String _arg0 = data.readString();
int _result = this.sayHelloTo(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_query:{
data.enforceInterface(descriptor);
com.iamyours.bean.Student _arg0 = data.readParcelable(com.iamyours.bean.Student.class.getClassLoader());
int _result = this.query(_arg0);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements ISayHello {
private IBinder mRemote;
Proxy(IBinder mRemote) {
this.mRemote = mRemote;
}
@Override
public void sayHello() {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try{
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(TRANSACTION_sayHello, _data, _reply, 0);
_reply.readException();
}catch(Exception e){e.printStackTrace();}finally{
_reply.recycle();
_data.recycle();
}
}
@Override
public int sayHelloTo(String var0) {
int _result = 0;
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try{
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(var0);
mRemote.transact(TRANSACTION_sayHelloTo, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();}catch(Exception e){e.printStackTrace();}finally{
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public int query(Student var0) {
int _result = 0;
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try{
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeParcelable(var0,0);
mRemote.transact(TRANSACTION_query, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();}catch(Exception e){e.printStackTrace();}finally{
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}
複製代碼
能夠看到有很大部分是數據傳輸相關的,咱們只需經過APT
生成便可,而Stub
中的sayHello
等方法經過抽象交給要實現最終業務的子類,從而實現代碼解耦。 咱們可使用javapoet
庫生成代碼,在AbstractProcessor
子類中的process
方法遍歷找到AIDL
註解,獲取接口中的方法列表:
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Map<Element, List<AidlMethod>> sources = new HashMap<>();
for (Element e : roundEnvironment.getElementsAnnotatedWith(AIDL.class)) {
List<AidlMethod> methods = new ArrayList<>();
sources.put(e, methods);
List<? extends Element> list = elementUtils.getAllMembers((TypeElement) e);
for (Element ee : list) {
boolean isAbstract = ee.getModifiers().contains(Modifier.ABSTRACT);
if (isAbstract) {
methods.add(createAidlMethod(ee));
}
}
}
generateAIDL(sources);
return true;
}
複製代碼
其中AidlMethod
包含了方法名,返回類型,參數列表
public class AidlMethod {
public Class retCls;//PrimitiveType,基本類型,int,double等
public ClassName retClsName;
public List<ParamData> params;
public int code;
public String name;
}
複製代碼
而後經過createAidlMethod
方法獲取接口方法的數據
private AidlMethod createAidlMethod(Element e) {
AidlMethod aMethod = new AidlMethod();
Type.MethodType mt = (Type.MethodType) e.asType();
Type retType = mt.getReturnType();
aMethod.name = e.getSimpleName() + "";
if (retType instanceof Type.JCPrimitiveType) {
aMethod.retCls = getPrimitiveType(retType);
} else {
if (!"void".equals(retType + "")) {
aMethod.retClsName = ClassName.bestGuess(retType + "");
}
}
List<Type> types = mt.getParameterTypes();
List<ParamData> params = new ArrayList<>();
for (Type t : types) {
ParamData p = new ParamData();
if (t instanceof Type.JCPrimitiveType) {
p.cls = getPrimitiveType(t);
} else if (t instanceof Type.ClassType) {
Type.ClassType ct = (Type.ClassType) t;
String cname = ct + "";
if ("java.lang.String".equals(cname) || isParcelable(ct)) {
p.clsName = ClassName.bestGuess(cname);
} else {
throw new RuntimeException("--unSupport param:" + t + ",in method:" + mt + " source:" + e);
}
} else {
throw new RuntimeException("unSupport param:" + t + ",in method:" + mt + " source:" + e);
}
params.add(p);
}
aMethod.params = params;
System.out.println(aMethod);
return aMethod;
}
複製代碼
最後在generateAIDL
方法中生成Stub
和Proxy
類代碼(詳細代碼能夠看這裏)。