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
类代码(详细代码能够看这里)。