这两天开始学习IPC的内容,从AIDL开始
AIDL是安卓接口定义语言的简称,用于进程间通信。现在记录一下使用步骤
1、建立Person类,实现Parcelable接口
AIDL默认支持的数据类型有:八种基本数据类型、String、List
如果要使用AIDL传递类对象,就必须让类实现Parcelable接口,并且要定义一个方法,名曰readFromParcel
完整的Person代码如下
public class Person implements Parcelable{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() { // 无参构造方法必须写上,原因后面说
}
.. get/set + toString()
public static final Creator CREATOR = new Creator() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
protected Person(Parcel in) {
name = in.readString(); // read和write的顺序最好一致
age = in.readInt();
}
public Person readFromParcel(Parcel parcel) {
name = parcel.readString();
age = parcel.readInt();
return this;
}
}
2、在Person类的同一个包下,建立Person.aidl,名字一定要和Person类名一致
这个是为了给aidl引入Person类
// Person.aidl
package com.example.songzeceng.studyofipc;
// 引入Person序列化类
// Person.aidl包名要和Person.java包名一样
parcelable Person;
3、也是在这个包下,建立负责IPC的aidl文件
先贴代码
// IPersonManagerInterface.aidl
package com.example.songzeceng.studyofipc;
import com.example.songzeceng.studyofipc.Person;
// 即便是在同一包下,也要手动导入Person类
// Declare any non-default types here with import statements
interface IPersonManagerInterface {
List getPeople();
void addPerson(in Person person);
Person updatePerson(out Person person);
Person updatePerson2(inout Person person);
}
aidl方法传入自定义类对象,in、out、inout必须写(aidl默认支持的类型不用写,默认且只能是in),否则报错。
关于参数前的in、out和inout,跨进程时,in参数会把参数的内容传给aidl,但其改动不会同步到调用进程;out参数不会把参数的属性传给aidl(aidl获取的参数对象属性为空),但其改动会同步到调用进程;inout参数则是in和out的综合。不跨进程时,三者则是摆设,详情请参见第6步的实际操作
4、编译,生成和aidl同名的java文件(我这儿是IPersonManagerInterface.java)
文件内容一会儿再说
5、编写Service,处理信息
代码如下
public class PeopleService extends Service {
private static final String TAG = "PeopleService";
private LinkedList peopleList = new LinkedList<>();
private Random random = new Random();
private final Stub peopleManager = new Stub() { // IPersonManagerInterface.Stub
@Override
public List getPeople() throws RemoteException {
return peopleList;
}
@Override
public void addPerson(Person person) throws RemoteException {
boolean isNull = person == null; // 参数为in
logger("in person is null--" + isNull);
person.setAge(person.getAge() + 1);
peopleList.add(person);
}
@Override
public Person updatePerson(Person person) throws RemoteException {
boolean isNull = person == null; // 参数为out
logger("out person is null--" + isNull);
if (isNull) {
person = new Person();
} else {
logger(person.toString());
}
person.setAge(random.nextInt() % 40);
person.setName("jason");
return person;
}
@Override
public Person updatePerson2(Person person) throws RemoteException {
boolean isNull = person == null; // 参数为inout
logger("inout person is null--" + isNull);
if (isNull) {
person = new Person();
} else {
logger(person.toString());
}
person.setAge(random.nextInt() % 40);
person.setName("mike");
return person;
}
};
private void logger(String msg) {
Log.i(TAG, msg);
}
@Override
public void onCreate() {
Person p = new Person("szc", 21);
peopleList.add(p);
}
@Override
public IBinder onBind(Intent intent) {
logger("有连接请求");
logger(intent.toString());
return peopleManager; // 返回Stub,因为Stub实现了IBinder接口
}
}
在清单文件中注册这个Service的时候
要加上exported,以便别的进程获取service;再写上process(进程名),一般就写:remote,这样进程名就是调用方包名:remote
清单文件中注册信息如下
6、MainActivity中使用
6.1、同一个进程下
同一个进程,就是在Service所在项目里的MainActivity里进行绑定,Activity的代码如下
public class MainActivity extends Activity {
public static final String TAG = "MainActivity";
private static boolean isConnected = false;
private Person p = new Person("Dustin", 27);
private IPersonManagerInterface.Stub peopleManager = null;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (peopleManager == null) {
peopleManager = (IPersonManagerInterface.Stub) IPersonManagerInterface.Stub.asInterface(service); // 此处的service,就是Service的onBind()方法返回的Stub,必须经过这个方法才能还原成Stub类对象
}
isConnected = true;
if (peopleManager != null) {
try {
LinkedList people = (LinkedList) peopleManager.getPeople();
logger(people.toString());
logger("=================");
logger("before add");
logger(p.toString());
logger("=================");
peopleManager.addPerson(p); // 验证in
logger(p.toString());
logger("=================");
peopleManager.updatePerson(p); // 验证out
logger(p.toString());
logger("=================");
peopleManager.updatePerson2(p); // 验证in/out
logger(p.toString());
logger("=================");
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
logger(name + "已经断开连接");
isConnected = false;
}
};
private void logger(String info) {
Log.i(TAG, info);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onStop() {
super.onStop();
tryDisconnectService();
}
@Override
protected void onStart() {
super.onStart();
tryConnectService();
}
private void tryConnectService() {
logger("try to connect service");
if (!isConnected) {
Intent intent = new Intent(this, PeopleService.class);
intent.setAction("com.example.songzeceng");
bindService(intent, connection, BIND_AUTO_CREATE);
}
}
private void tryDisconnectService() {
logger("try to disconnect service");
if (isConnected) {
unbindService(connection);
isConnected = false;
}
}
}
运行结果如下
为了简洁,我把传入in参数的方法称为in方法,传入out参数的方法称为out方法,传入inout参数的方法称为inout方法
我的Service里的日志都是改变参数前打的,只是看看传入的对象及参数是不是null
in方法:如果像网上说的,in方法传入的对象发生的改变,不会同步到外界,但事实却是外界的实参发生了变化
out方法:如果像网上说的,out方法传入的对象属性为空,但在同进程下,参数并不为空
所以我才觉得,统一进程内,in、out、inout似乎是摆设
6.2、不同进程下
为了验证aidl的跨进程,我新建了一个项目Client,把上面的项目做为Client的模块依赖,具体操作参见文章安卓开发学习之为项目添加模块依赖
MainActivity的代码和刚才的几乎一样,如下
public class MainActivity extends Activity {
public static final String TAG = "MainActivity";
private IPersonManagerInterface personManager = null;
private boolean isConnected = false;
private Person p = new Person("Dustin", 27);
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
logger(name.toString()); // BinderProxy
isConnected = true;
if (service == null) {
logger("service is null");
return;
}
try {
logger(service.getClass().getCanonicalName());
if (personManager == null) {
personManager = IPersonManagerInterface.Stub.asInterface(service);
// Stub.proxy
logger(personManager.getClass().getCanonicalName());
}
if (personManager != null) {
logger("before add");
logger(p.toString());
logger("=================");
personManager.addPerson(p);
logger(p.toString());
logger("=================");
ArrayList people = (ArrayList) personManager.getPeople();
// proxy返回的是ArrayList
logger(people.toString());
logger("=================");
personManager.updatePerson(p);
// out和inout似乎并不起作用
logger(p.toString());
logger("=================");
personManager.updatePerson2(p);
logger(p.toString());
logger("=================");
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
isConnected = false;
}
};
private void logger(String s) {
Log.i(TAG, s);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onStart() {
super.onStart();
if (!isConnected) {
Intent intent = new Intent(this, PeopleService.class);
intent.setAction("com.example.songzeceng");
bindService(intent, connection, BIND_AUTO_CREATE);
}
}
@Override
protected void onStop() {
super.onStop();
if (isConnected) {
unbindService(connection);
isConnected = false;
}
}
}
运行后Client进程的日志截图
可见,out和inout的参数对象,果然和Service同步发生了变化
再看service进程的日志
out参数的属性确实是空
这个java文件是我们编写IPersonManagerInterface.aidl后,系统编译后生成的,路径是aidl所在工程下的build\generated\source\aidl\debug\包名目录下,如图所示
打开之,格式化(mac的快捷键是option+command+l),这个IPersonManagerInterface继承了IInterface,明显是用来IPC的,而IInterface里方法asBinder()由内部类实现,而我们在aidl里定义的四个方法,就放在这个接口里
public interface IPersonManagerInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.songzeceng.studyofipc.IPersonManagerInterface {
...
private static class Proxy implements com.example.songzeceng.studyofipc.IPersonManagerInterface {
...
}
...
}
public java.util.List getPeople() throws android.os.RemoteException;
public void addPerson(com.example.songzeceng.studyofipc.Person person) throws android.os.RemoteException;
public com.example.songzeceng.studyofipc.Person updatePerson(com.example.songzeceng.studyofipc.Person person) throws android.os.RemoteException;
public com.example.songzeceng.studyofipc.Person updatePerson2(com.example.songzeceng.studyofipc.Person person) throws android.os.RemoteException;
}
可以看到,这个接口有一个静态内部类,Stub。它是抽象类,继承于Binder,实现了外面的接口IPersonManagerInterface
而Stub也有一个静态内部类,叫做Proxy,但只是实现了外层的接口
下面我花开两朵,各表一枝
Stub是工作在aidl进程的(我们的Service实例化了一个Stub,所以我们的aidl进程就是Service进程)
源码如下
public static abstract class Stub extends android.os.Binder implements com.example.songzeceng.studyofipc.IPersonManagerInterface {
private static final java.lang.String DESCRIPTOR = "com.example.songzeceng.studyofipc.IPersonManagerInterface";
// 描述符
/**
* Construct the stub at attach it to the interface.
*/
public Stub() { // 构造方法
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.songzeceng.studyofipc.IPersonManagerInterface interface,
* generating a proxy if needed.
*/
// 我们在onServiceConnected()方法中,就是用这个asInterface()方法,来把Service的onBind()返回的IBinder对象转化为Stub对象或Proxy对象
public static com.example.songzeceng.studyofipc.IPersonManagerInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); // 寻找本地Stub接口,也就是当前进程的Stub接口
// 如果找到了,就表示和aidl是在一个进程里
// 否则,就不是一个进程里,比如Client
if (((iin != null) && (iin instanceof com.example.songzeceng.studyofipc.IPersonManagerInterface))) {
// 在一个进程里,返回的是Service类里的Stub对象
return ((com.example.songzeceng.studyofipc.IPersonManagerInterface) iin);
}
// 不在一个进程里,就返回一个Proxy,所以Client获取的是proxy
// 传入的参数,就是Service的onBind()方法返回值
return new com.example.songzeceng.studyofipc.IPersonManagerInterface.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 {
.. // 此方法仅用于ipc,如果是一个进程里用不到
}
private static class Proxy implements com.example.songzeceng.studyofipc.IPersonManagerInterface {
...
}
static final int TRANSACTION_getPeople = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_updatePerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_updatePerson2 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
}
可以得出的一个结论就是:如果调用方和aidl在一个进程里,asInterface()获取到的就是Service.onBind()方法的返回结果,调用的就是Service里的Stub的方法,再加上同一进程用的是同一块内存空间,所以才导致in、out、inout没了实际作用
Proxy是IPC时,工作在调用方进程的,通过IBinder.transact()方法调用aidl进程的Stub.onTransact()方法
代码如下
private static class Proxy implements com.example.songzeceng.studyofipc.IPersonManagerInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) { // 构造方法由Stub.asInterface()调用,传过来是Service.onBind()的返回值
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public java.util.List getPeople() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain(); // 传入参数
android.os.Parcel _reply = android.os.Parcel.obtain(); // 返回结果
java.util.List _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPeople, _data, _reply, 0); // 调用Stub.onTransact()
_reply.readException();
_result = _reply.createTypedArrayList(com.example.songzeceng.studyofipc.Person.CREATOR); // 写入结果
} finally {
_reply.recycle();
_data.recycle();
}
return _result; // 返回结果
}
@Override
public void addPerson(com.example.songzeceng.studyofipc.Person person) throws android.os.RemoteException { // in方法
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((person != null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0); // 把传入参数person写入data,做为Stub.onTransact()的参数
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0); // reply是空,调用Stub.onTransact()
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
} // 没有返回结果
}
@Override // out方法
public com.example.songzeceng.studyofipc.Person updatePerson(com.example.songzeceng.studyofipc.Person person) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.example.songzeceng.studyofipc.Person _result;
try {
_data.writeInterfaceToken(DESCRIPTOR); // 并没有写入参数person
mRemote.transact(Stub.TRANSACTION_updatePerson, _data, _reply, 0); // 调用Stub.onTransact(),但这时data和reply都是空的
_reply.readException();
if ((0 != _reply.readInt())) {
_result = com.example.songzeceng.studyofipc.Person.CREATOR.createFromParcel(_reply); // 根据reply读取结果
} else {
_result = null;
}
if ((0 != _reply.readInt())) {
person.readFromParcel(_reply); // 根据reply读取person,调用Person.readFromParcel()方法。由于是引用传递,所以会同步到调用方进程
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public com.example.songzeceng.studyofipc.Person updatePerson2(com.example.songzeceng.studyofipc.Person person) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.example.songzeceng.studyofipc.Person _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((person != null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0); // 写入person参数
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_updatePerson2, _data, _reply, 0); // 调用Stub.onTransact()方法
_reply.readException();
if ((0 != _reply.readInt())) {
_result = com.example.songzeceng.studyofipc.Person.CREATOR.createFromParcel(_reply); // 读取结果
} else {
_result = null;
}
if ((0 != _reply.readInt())) {
person.readFromParcel(_reply); // 读取结果给参数person
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
清楚说明了in、out、inout的工作机制,而且可以看到,关键是调用了Stub.onTransact()方法,代码如下
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
// 这个方法用于IPC,也就是不在一个进程里的情况(Client)
// 此时this指针指向的也是Service.onBind()的返回值
// data里面是Client向Service传过来的参数
// reply则是Service向Client返回的结果
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getPeople: {
// 处理列表
data.enforceInterface(DESCRIPTOR);
java.util.List _result = this.getPeople();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addPerson: {
// in方法
data.enforceInterface(DESCRIPTOR);
com.example.songzeceng.studyofipc.Person _arg0;
// 读取参数
if ((0 != data.readInt())) {
// in方法走这里,调用了Person.CREATOR的createFromParcel()方法
_arg0 = com.example.songzeceng.studyofipc.Person.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addPerson(_arg0);
// 没有往reply里写东西
reply.writeNoException();
return true;
}
case TRANSACTION_updatePerson: {
// out方法
data.enforceInterface(DESCRIPTOR);
com.example.songzeceng.studyofipc.Person _arg0;
_arg0 = new com.example.songzeceng.studyofipc.Person();
// 参数是直接用无参构造方法new出来的,所以IPC时out方法里参数属性是空
com.example.songzeceng.studyofipc.Person _result = this.updatePerson(_arg0);
// 获取返回值
reply.writeNoException();
// 写入结果reply
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
if ((_arg0 != null)) {
// 似乎还要同步arg0,但arg0的生命周期只在这个方法之内
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_updatePerson2: {
// inout方法
data.enforceInterface(DESCRIPTOR);
com.example.songzeceng.studyofipc.Person _arg0;
if ((0 != data.readInt())) {
// 读取参数
_arg0 = com.example.songzeceng.studyofipc.Person.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
com.example.songzeceng.studyofipc.Person _result = this.updatePerson2(_arg0);
reply.writeNoException();
if ((_result != null)) {
// 写入结果
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
if ((_arg0 != null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
注释写得很清楚,无需赘言
AIDL是Android跨进程通信的重要手段,应该掌握一下