通过AIDL分析Binder机制
-
直观来说,Binder是Android中的一个类,它继承了IBinder接口。从IPC角度来说, Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,它 的设备驱动是/dev/binder,该通信方式在Linux中没有;从Android Framework角度来说, Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager,等等)和 相应ManagerService的桥梁;从Android应用层来说,Binder是客户端和服务端进行通信的 媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通 过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通 服务和基于AIDL的服务。
-
Android开发中,Binder主要用在Service中,包括AIDL和Messenger,其中普通Service 中的Binder不涉及进程间通信,所以较为简单,无法触及Binder的核心,而Messenger的底 层其实是AIDL,所以这里选择用AIDL来分析Binder的工作机制。为了分析Binder的工作 机制,我们需要新建一个AIDL示例,SDK会自动为我们生产AIDL所对应的Binder类,然 后我们就可以分析Binder的工作过程。
首先创建service端:以传输Parcelable对象为例子
新建Person.java类
package com.demo.aidlservice;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable {
private String name;
private String age;
public Person(String name, String age) {
this.name = name;
this.age = age;
}
protected Person(Parcel in) {
name = in.readString();
age = in.readString();
}
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeString(age);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
创建aidl文件夹包名必须和包名一致,在aidl文件夹创建一个aidl文件
// IMyAidlInterface.aidl
package com.demo.aidlservice;
// Declare any non-default types here with import statements
import com.demo.aidlservice.Person;
interface IMyAidlInterface {
List<Person> add(in Person person);
}
因为Person是在java类中,IMyAidlInterface 中并不能识别,所以要创建Person.aidl文件
// IMyAidlInterface.aidl
package com.demo.aidlservice;
parcelable Person;
很简单就这一行代码就够了。
最后创建一个service提供给客户端绑定记得要在manifests里注册service
public class IRemoteService extends Service {
private ArrayList<Person> personList;
private static final String TAG = "IRemoteService";
@Override
public IBinder onBind(Intent intent) {
personList = new ArrayList<>();
return iBinder;
}
private IBinder iBinder = new IMyAidlInterface.Stub() {
@Override
public List<com.demo.aidlservice.Person> add(com.demo.aidlservice.Person person) throws RemoteException {
personList.add(person);
return personList;
}
};
}
服务端的结构如下图
- 接下来是客户端的代码
如图所示,创建了和服务端包名一致的aidl来存放两个aidl文件。里面的内容也是完全一样的。然后再看一下MainActivity
public class MainActivity extends AppCompatActivity {
private IMyAidlInterface iMyAidlInterface;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService();
}
private void bindService() {
//禁止使用隐式服务启动服务 只能用显式Intent启动绑定服务
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.demo.aidlservice", "com.demo.aidlservice.IRemoteService"));
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
public void add(View view) {
try {
List<Person> persons = iMyAidlInterface.add(new Person("ludi", "34"));
Log.e("persons", persons.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
初始化的时候绑定service 传入了Context.BIND_AUTO_CREATE,绑定的时候自动创建Service。并且绑定service 5.0以后不允许使用隐式调用方式。点击add的时候调用到服务端add方法,然后返回集合数据。
- 接下来分析一下整体数据的传递逻辑。
首先,当我们编译的系统会生成对应的java类,下面是服务端生成的IMyAidlInterface.java,为了看着方便我格式化了一下代码
package com.demo.aidlservice;
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.demo.aidlservice.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.demo.aidlservice.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.demo.aidlservice.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.demo.aidlservice.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.demo.aidlservice.IMyAidlInterface))) {
return ((com.demo.aidlservice.IMyAidlInterface) iin);
}
return new com.demo.aidlservice.IMyAidlInterface.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_add: {
data.enforceInterface(descriptor);
com.demo.aidlservice.Person _arg0;
if ((0 != data.readInt())) {
_arg0 = com.demo.aidlservice.Person.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
java.util.List<com.demo.aidlservice.Person> _result = this.add(_arg0);
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.demo.aidlservice.IMyAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public java.util.List<com.demo.aidlservice.Person> add(com.demo.aidlservice.Person person) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.demo.aidlservice.Person> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((person != null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.demo.aidlservice.Person.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public java.util.List<com.demo.aidlservice.Person> add(com.demo.aidlservice.Person person) throws android.os.RemoteException;
}
它继承了android.os.IInterface 接口,同时它也还是个接口,所有可以在Binder中自由传输的接口都需要继承IInterface接口。通过它我们可以分析Binder的工作机制,首先它声明了add()方法这个就是我们在.aidl文件中声明的方法,同时对这个方法声明了一个id来标识这个方法,用id来标识在transact过程中标记客户端到底请求了是哪个方法。接着它声明了内部类Stub,继承android.os.Binder,当客户端和服务端是同一个进程时,不会走跨进程的transact过程。当我们通过asInterface获取IMyAidlInterface的时候
public static com.demo.aidlservice.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.demo.aidlservice.IMyAidlInterface))) {
return ((com.demo.aidlservice.IMyAidlInterface) iin);
}
return new com.demo.aidlservice.IMyAidlInterface.Stub.Proxy(obj);
}
返回的return new com.demo.aidlservice.IMyAidlInterface.Stub.Proxy(obj);所以整个transact过程是有Stub的内部代理Proxy完成的。接下来介绍下每个方法的含义。
//Binder唯一标识,一般用当前Binder类名表示。
private static final java.lang.String DESCRIPTOR = "com.demo.aidlservice.IMyAidlInterface";
//用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换过 程是区分进程的,如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的 Stub对象本身,否则返回的是系统封装后的Stub.proxy对象。
public static com.demo.aidlservice.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.demo.aidlservice.IMyAidlInterface))) {
return ((com.demo.aidlservice.IMyAidlInterface) iin);
}
return new com.demo.aidlservice.IMyAidlInterface.Stub.Proxy(obj);
}
//asBinder返回当前Binder对象
private android.os.IBinder mRemote;
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
这个方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求 会通过系统底层封装后交由此方法来处理。该方法的原型为public Boolean onTransact(int code,android.os.Parcel data,android.os.Parcel reply,int flags)。服务端通过code可以确定客户 端所请求的目标方法是什么,接着从data中取出目标方法所需的参数(如果目标方法有参 数的话),然后执行目标方法。当目标方法执行完毕后,就向reply中写入返回值(如果 目标方法有返回值的话),onTransact方法的执行过程就是这样的。需要注意的是,如果 此方法返回false,那么客户端的请求会失败,因此我们可以利用这个特性来做权限验证, 毕竟我们也不希望随便一个进程都能远程调用我们的服务。
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_add: {
data.enforceInterface(descriptor);
com.demo.aidlservice.Person _arg0;
if ((0 != data.readInt())) {
_arg0 = com.demo.aidlservice.Person.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
java.util.List<com.demo.aidlservice.Person> _result = this.add(_arg0);
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
通过上面的分析,读者应该已经了解了Binder的工作机制,但是有两点还是需要额外 说明一下:首先,当客户端发起远程请求时,由于当前线程会被挂起直至服务端进程返回 数据,所以如果一个远程方法是很耗时的,那么不能在UI线程中发起此远程请求;其 次,由于服务端的Binder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都 应该采用同步的方式去实现,因为它已经运行在一个线程中了。