与Binder相关的几个类
可以参考类图,这些类的职责都在类图中写出来了
Binder的流程图
光看这两张图,可能对这些依然没有什么概念,下面结合一个具体的AIDL例子,来具体看下这些类、流程都是怎么工作的。
AIDL的原理
看下面一个例子,我们先定义一个aidl接口,然后绑定服务,在client中获取该接口的实例:
// IMyAidlInterface.aidl
package com.example.myapplication.aidl;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void myTest(int anInt);
}
在需要的地方绑定服务:
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 我们这里拿到的对象其实就是其Stub的内部类Proxy对象
myAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private void bindAIDLService() {
Intent intent = new Intent();
bindService(intent, connection, BIND_AUTO_CREATE);
}
下面看一下编译之后系统自动生成的java类,我们结合上面的两张图来理解Binder类的各个职责及流程:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\Demo\\MyTest\\app\\src\\main\\aidl\\com\\example\\myapplication\\aidl\\IMyAidlInterface.aidl
*/
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.myapplication.aidl.IMyAidlInterface {
// 描述符
private static final java.lang.String DESCRIPTOR = "com.example.myapplication.aidl.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.myapplication.aidl.IMyAidlInterface interface,
* generating a proxy if needed.
* 这里就是我们在将IBinder转化为具体的aidl接口时调用的方法
*/
public static com.example.myapplication.aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj==null)) {
return null;
}
// 先检查本地是否有aidl对象,如果client和server在同一进程,则会走进这个回调
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.myapplication.aidl.IMyAidlInterface))) {
return ((com.example.myapplication.aidl.IMyAidlInterface)iin);
}
// 在不同进程,就返回一个Proxy对象
return new com.example.myapplication.aidl.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_myTest: {
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
this.myTest(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
// 其实是Server在Client中的本地代理对象,实现了在aidl中定义的方法,其实是将参数序列化
// 后,调用mRemote(也就是BinderProxy)去处理;一般情况下我们在绑定服务后,拿到的远程对象,
// 就是这个Proxy
private static class Proxy implements com.example.myapplication.aidl.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;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
* 这个方法将参数序列化后,交给mRemote去处理,transact中传入了每个参数对应的编号;
* 这时,client调用方法线程会挂起,等待server响应;
* 等服务端处理结束后,将数据返回至binder驱动,后者唤醒挂起的线程,这时一次跨进程通信就完成了
*/
@Override public void myTest(int anInt) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
mRemote.transact(Stub.TRANSACTION_myTest, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_myTest = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void myTest(int anInt) throws android.os.RemoteException;
}
可以看到,一共生成了三个类:
- IMyAidlInterface extends android.os.IInterface,client中获取到的是这个接口,用于client和server通信
- IMyAidlInterface.Stub
- IMyAidlInterface.Stub.Proxy:server的本地代理接口,我们调用的方法都是由它来处理,传递给binder对象的
下面我们将上面三个类单独拆分出来看下
首先是继承自IInterface接口的IMyAidlInterface接口,只声明了一个方法,也就是我们定义的aidl方法
所有可以在Binder中传输的接口都需要继承自IInterface接口
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void myTest(int anInt) throws android.os.RemoteException;
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.myapplication.aidl.IMyAidlInterface {
// 描述符,Binder的唯一标识
private static final java.lang.String DESCRIPTOR = "com.example.myapplication.aidl.IMyAidlInterface";
static final int TRANSACTION_myTest = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
/** Construct the stub at attach it to the interface. */
public Stub(){
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.myapplication.aidl.IMyAidlInterface interface,
* generating a proxy if needed.
* 这里就是我们在将IBinder转化为具体的aidl接口时调用的方法
*/
public static com.example.myapplication.aidl.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj==null)) {
return null;
}
// 先检查本地是否有aidl对象,如果client和server在同一进程,则会走进这个回调
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.myapplication.aidl.IMyAidlInterface))) {
return ((com.example.myapplication.aidl.IMyAidlInterface)iin);
}
// 在不同进程,就返回一个Proxy对象
return new com.example.myapplication.aidl.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_myTest: {
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
this.myTest(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
}
这里的Stub就是一个Binder的子类,当客户端和服务端都处于同一个进程时,方法调用不会走跨进程的transact过程,只有二者在不同进程时,才会走transact,该过程由Proxy类来完成
- asInterface:用于将服务端的Binder对象转化为客户端所需的AIDL接口类型的对象;依然区分进程,如果是同进程,那返回的是服务端的Stub本身,否则,返回的是封装后的Stub.Proxy对象
- asBinder:返回当前Binder对象
- onTransact:该方法运行在服务端中的Binder线程池,客户端发起的远程请求会通过底层封装后交给该方法来处理。服务端通过code参数可以确认调用的是哪个方法,然后从data中取出参数,执行目标方法; 当目标方法执行完后,向reply中写入返回值,再通过Binder返回给客户端
// 其实是Server在Client中的本地代理对象,实现了在aidl中定义的方法,其实是将参数序列化
// 后,调用mRemote(也就是BinderProxy)去处理;一般情况下我们在绑定服务后,拿到的远程对象,
// 就是这个Proxy
private static class Proxy implements com.example.myapplication.aidl.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;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
* 这个方法将参数序列化后,交给mRemote去处理,transact中传入了每个参数对应的编号;
* 这时,client调用方法线程会挂起,等待server响应;
* 等服务端处理结束后,将数据返回至binder驱动,后者唤醒挂起的线程,这时一次跨进程通信就完成了
*/
@Override public void myTest(int anInt) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
mRemote.transact(Stub.TRANSACTION_myTest, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
- myTest:该方法运行在客户端,客户端调用时,首先创建输入参数_data,将对应的参数写入 _data,然后调用transact()方法发起远程调用,然后当前线程会挂起,服务端的transact方法得到调用返回结果后,该线程继续执行,从 _reply中取出返回值并返回
注意
- 客户端发起远程请求时,当前线程会被挂起直到服务端返回数据,如果一个远程方法可能是耗时的,那就不能在主线程中去发起调用
- 服务端的Binder方法是运行在Binder线程池中的,因此binder方法无论耗时与否,都应该采用同步的方法去实现,因为它已经运行在一个子线程中了
参考:https://www.jianshu.com/p/062a6e4f5cbe