Binder是Android一个十分重要进程间通信机制,Android系统的很多核心服务AMS,PMS,WMS的使用都是建立在Binder之上的。在对Activity启动流程,App安装流程源码梳理过程中,Binder也是我们经常碰到的。因此,在我们阅读这些源码之前,要弄清Binder是如何使用的。
Binder的使用
我们知道,Binder是基于C/S结构的,就像http接口请求调用。
这里我们想想调用接口时,客户端和服务端做了什么:
- 客户端封装数据,指定某个接口名称,发送数据给服务端
- 服务端等待接收到某个接口请求时,解析请求参数,查询操作相关服务(数据库等)数据,然后封装数据,返回数据
- 客户端返回数据,封装数据(将流封装成对象)
客户端实现
Binder和以上流程类似,我们使用AS分别创建Client,Server两个项目。我们模拟一个考试成绩查询场景,即通过学生名称在服务端查询该学生的成绩。
那么在客户端,就有了如下实现:
//学生
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
对象发送数据。
服务端实现
在服务端,同样创建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;
}
}
}
在清单文件中注册这个服务
<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端调用成绩查询服务,如下
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);
}
}
最终调用结果如下:
服务端
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
客户端
com.iamyours.client E/client: result:100
com.iamyours.client E/client: result:89
com.iamyours.client E/client: result:-1
至此我们简单通过Binder实现一个成绩查询服务。
使用APT实现AIDL
会看上面的代码,我们发现很多代码是耦合在一起的,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
类代码(详细代码可以看这里)。