代码演示
我们模仿AIDL工具帮我们生成的类以便加入日志:
// IPersonManager.aidl
package com.example.chricservice;
// Declare any non-default types here with import statements
import com.example.chricservice.Person;
interface IPersonManager {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void addPerson(in Person person);
List<Person> getPersonList();
void setData(in byte[] data);
}
AIDL生成代码结构如下图:
我们只关注核心代码 如下:
package com.example.chricbinder;
public interface IPersonManager extends IInterface {
void addPerson(Person person) throws RemoteException;
List<Person> getPersonList() throws RemoteException;
void setData(byte[] data) throws RemoteException;
public static abstract class Stub extends Binder implements IPersonManager {
static final String DESCRIPTOR = "com.example.chricbinder.IPersonManager";
static final int TRANSACTION_addPerson = IBinder.FIRST_CALL_TRANSACTION;
static final int TRANSACTION_getPersonList = IBinder.FIRST_CALL_TRANSACTION + 1;
static final int TRANSACTION_setData = IBinder.FIRST_CALL_TRANSACTION + 2;
public static IPersonManager asInterface(IBinder binder) {
if ((binder == null)) {
return null;
}
//尝试检索一个接口的本地实现用于此Binder对象。如果返回null,则需要实例化一个代理类来调用transact()方法。
IInterface iin = binder.queryLocalInterface(DESCRIPTOR);
if ((iin != null) && (iin instanceof IPersonManager)) {
Log.e("BinderChric", " 【CLIENT】(IPersonManager) iin " );
return (IPersonManager) iin;
}
Log.e("BinderChric", "【CLIENT】 new Proxy(binder) " + binder);
return new Proxy(binder);
}
@Override
public IBinder asBinder() {
return this;
}
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
String DESCRIPTOR = "com.example.chricbinder.IPersonManager";
switch (code) {
case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true;
case TRANSACTION_addPerson:
Log.e("BinderChric", "【SERVER】==Stub,onTransact addPerson: " + Thread.currentThread());
data.enforceInterface(DESCRIPTOR);
Person arg0 = null;
if ((0 != data.readInt())) {
arg0 = Person.CREATOR.createFromParcel(data);
}
this.addPerson(arg0);
reply.writeNoException();
return true;
case TRANSACTION_getPersonList:
Log.e("BinderChric", "【SERVER】==Stub,onTransact PersonList: " + Thread.currentThread());
data.enforceInterface(DESCRIPTOR);
List<Person> result = this.getPersonList();
reply.writeNoException();
reply.writeTypedList(result);
return true;
case TRANSACTION_setData:
data.enforceInterface(DESCRIPTOR);
byte[] _arg0;
_arg0 = data.createByteArray();
this.setData(_arg0);
reply.writeNoException();
return true;
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements IPersonManager {
private IBinder mRemote;
public Proxy(IBinder remote) {
mRemote = remote;
}
@Override
public void addPerson(Person person) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
if ((person != null)) {
data.writeInt(1);
person.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
Log.e("BinderChric", "【CLIENT】==Proxy transact addPerson: " + Thread.currentThread());
mRemote.transact(Stub.TRANSACTION_addPerson, data, reply, 0);
reply.readException();
} finally {
reply.recycle();
data.recycle();
}
}
@Override
public List<Person> getPersonList() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
List<Person> result;
try {
data.writeInterfaceToken(DESCRIPTOR);
Log.e("BinderChric", "【CLIENT】==Proxy transact getPersonList: " + Thread.currentThread());
mRemote.transact(Stub.TRANSACTION_getPersonList, data, reply, 0);
reply.readException();
result = reply.createTypedArrayList(Person.CREATOR);
} finally {
reply.recycle();
data.recycle();
}
return result;
}
@Override
public void setData(byte[] data) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeByteArray(data);
// 同步:0 异步:IBinder.FLAG_ONEWAY
mRemote.transact(Stub.TRANSACTION_setData, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public IBinder asBinder() {
return mRemote;
}
}
}
}
客户端Activity代码:
package com.example.chricbinder.client;
public class ClientActivity extends AppCompatActivity {
Button button1;
Button button2;
Button button3;
Button button4;
IPersonManager pmsProxy;
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.w("BinderChric", "【CLIENT】== onServiceConnected");
// proxy
pmsProxy = IPersonManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
pmsProxy = null;
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1 = findViewById(R.id.button1);
button2 = findViewById(R.id.button2);
button3 = findViewById(R.id.button3);
button4 = findViewById(R.id.button4);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(ClientActivity.this, RemoteService.class);
intent.setAction("com.chric.binder");
boolean success = bindService(intent, serviceConnection, BIND_AUTO_CREATE);
Log.w("BinderChric", "【CLIENT】== bindService success =" + success);
}
});
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Person person = new Person("BinderChric", 28);
Log.w("BinderChric", "【CLIENT】== addPerson:" + person);
pmsProxy.addPerson(person);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
List<Person> personList = pmsProxy.getPersonList();
Log.w("BinderChric", "【CLIENT】== personList:" + personList.toString());
} catch (RemoteException e) {
}
}
});
button4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(serviceConnection);
}
});
}
}
服务端代码:
package com.example.chricbinder.server;
public class RemoteService extends Service {
// 这里模仿ActivityManagerService extends IActivityManager.Stub
IBinder pms = new PersonManagerService();
// 这里使用匿名内部类实现Stub
// private ArrayList<Person> persons = new ArrayList<>();
//
// IBinder pms = new IPersonManager.Stub() {
// @Override
// public void addPerson(Person person) throws RemoteException {
// persons.add(person);
// }
//
// @Override
// public List<Person> getPersonList() throws RemoteException {
// return persons;
// }
//
// @Override
// public void setData(byte[] data) throws RemoteException {
//
// }
// };
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.w("BinderChric", "【SERVER】== RemoteService onBind");
return pms;
}
@Override
public void onCreate() {
Log.w("BinderChric", "【SERVER】== RemoteService onCreate ");
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
}
public static class PersonManagerService extends IPersonManager.Stub{
private ArrayList<Person> persons = new ArrayList<>();
@Override
public void addPerson(Person person) throws RemoteException {
persons.add(person);
}
@Override
public List<Person> getPersonList() throws RemoteException {
return persons;
}
@Override
public void setData(byte[] data) throws RemoteException {
}
}
}
运行日志如下(这里客户端和服务端在一个进程,当然大家可以把服务端运行在另外一个进程,这样CS两端都需要写IPersonManager接口协议):
W/BinderChric: 【CLIENT】== bindService success =true
W/BinderChric: 【SERVER】== RemoteService onCreate
W/BinderChric: 【SERVER】== RemoteService onBind
W/BinderChric: 【CLIENT】== onServiceConnected
E/BinderChric: 【CLIENT】 new Proxy(binder) com.example.chricbinder.server.RemoteService$PersonManagerService@a27f4bf
W/BinderChric: 【CLIENT】== addPerson:Person{name='BinderChric', age=28}
E/BinderChric: 【CLIENT】==Proxy transact addPerson: Thread[main,5,main]
E/BinderChric: 【SERVER】==Stub, onTransact addPerson: Thread[main,5,main]
E/BinderChric: 【CLIENT】==Proxy transact getPersonList: Thread[main,5,main]
E/BinderChric: 【SERVER】==Stub, onTransact getPersonList: Thread[main,5,main]
W/BinderChric: 【CLIENT】== personList:[Person{name='BinderChric', age=28}]
总结
binder
基于
AIDL
的通信流程图如下:
Client
端: Client
Activity
;
Server
端: Remote
Service
;
Service
的对外
aidl
接口如上面代码所示,
Client
Activity已经通过BindService拿到了Remote
Service的IBinder
对象
AIDL通信的基本步骤如下:
1. Client通过ServiceConnection获取到Server的Binder,并且封装成一个Proxy。
2. 通过Proxy来同步调用IPC方法(addPerson,Parcel data和Parcel reply),同时通过
person.writeToParcel(data, 0)将参数传给Binder,最终触发Binder的 transact方法。
person.writeToParcel(data, 0)将参数传给Binder,最终触发Binder的 transact方法。
3. Binder的transact方法最终会触发到Server上Stub的onTransact方法。
4. Server上Stub的onTransact方法中,会先从Parcel data中解析中参数,然后将参数带入真正的方法中执行,然后将 结果写入Parcel reply后传回(例如调用getPersonList方法中result = reply.createTypedArrayList(Person.CREATOR))。
5. 请注意: Client的IPC方法中,执行Binder的transact时,是同步阻塞等待的, 一直到Server逻辑执行结束后才会继 续执行。当然,如果IPC方法是oneWay的方式,那么就是非阻塞的等待。
6. 当Server返回结果后, Client从Parcel中取出返回值,于是实现了一次IPC调用。
7.aidl是一个使用 binder的标准方案,该方案的代码同样的可以通过用户自己编写的方式完成;
aidl 文件build后会生成一个java文件,这个java文件的意义在于将核心的binder驱动封装成为java层可以直接调用的 代码,同时也处理了将java层的数据格式转换为Parcel格式数据进行跨进程传递的一个功能。
后记
1.客户端是如何通过bindservice获取服务端的BinderProxy对象的?
2.Binder驱动及周边是什么?BpBinder、BBinder是什么?
后面会继续更新。