系列文章
Android 进程间通信(一) – Android 多进程模式
Android 进程间通信(二) – 理解 Binder 的机制
Android 进程间通信(三) --通过 AIDL 理解Binder,并手写Binder服务
上一章,已经学习了 Binder 的通信原理,这里再通过 AIDL 了,再来捋一遍,并自己写个 Binder。
如果你对 AIDL 不熟悉,可以参考这篇文章 AIDL使用详解及进程回调
一. AIDL 基本使用
这里也是用上面的代码,首先是任务类 TaskInfo,需要继承 Parcelable 接口,让as 帮你实现方法即可,如下:
public class TaskInfo implements Parcelable {
public int id;
public String url;
public int progress;
public TaskInfo() {
}
protected TaskInfo(Parcel in) {
id = in.readInt();
url = in.readString();
progress = in.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(url);
dest.writeInt(progress);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<TaskInfo> CREATOR = new Creator<TaskInfo>() {
@Override
public TaskInfo createFromParcel(Parcel in) {
return new TaskInfo(in);
}
@Override
public TaskInfo[] newArray(int size) {
return new TaskInfo[size];
}
};
@Override
public String toString() {
return "TaskInfo{" +
"id=" + id +
", url='" + url + '\'' +
", progress=" + progress +
'}';
}
}
然后是 TaskInfo.aidl
//注意这个 TaskInfo 必须是在 com.example.ipcdemo 下,不如会提示找不到。
parcelable TaskInfo;
IRemoteService.aidl :
//记得导入 TaskInfo.aidl 的包
import com.example.ipcdemo.TaskInfo;
interface IRemoteService {
//两数之和
int add(int num1,int num2);
//添加一个任务
TaskInfo addTask(in TaskInfo info);
}
然后 build 一下,就会发现,在 build 下生成了 IRemoteService.java
接着看这个 IRemoteService 的 java ,代码有点长,可以略过:
public interface IRemoteService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.ipcdemo.IRemoteService
{
private static final java.lang.String DESCRIPTOR = "com.example.ipcdemo.IRemoteService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.ipcdemo.IRemoteService interface,
* generating a proxy if needed.
*/
public static com.example.ipcdemo.IRemoteService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.ipcdemo.IRemoteService))) {
return ((com.example.ipcdemo.IRemoteService)iin);
}
return new com.example.ipcdemo.IRemoteService.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);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_addTask:
{
data.enforceInterface(descriptor);
com.example.ipcdemo.TaskInfo _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.ipcdemo.TaskInfo.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
com.example.ipcdemo.TaskInfo _result = this.addTask(_arg0);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.ipcdemo.IRemoteService
{
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 int add(int num1, int num2) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(num1);
_data.writeInt(num2);
boolean _status = mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().add(num1, num2);
}
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
//添加一个任务
@Override public com.example.ipcdemo.TaskInfo addTask(com.example.ipcdemo.TaskInfo info) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.example.ipcdemo.TaskInfo _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((info!=null)) {
_data.writeInt(1);
info.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_addTask, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().addTask(info);
}
_reply.readException();
if ((0!=_reply.readInt())) {
_result = com.example.ipcdemo.TaskInfo.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.example.ipcdemo.IRemoteService sDefaultImpl;
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public static boolean setDefaultImpl(com.example.ipcdemo.IRemoteService impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.example.ipcdemo.IRemoteService getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
/** Default implementation for IRemoteService. */
public static class Default implements com.example.ipcdemo.IRemoteService
{
//两数之和
@Override public int add(int num1, int num2) throws android.os.RemoteException
{
return 0;
}
//添加一个任务
@Override public com.example.ipcdemo.TaskInfo addTask(com.example.ipcdemo.TaskInfo info) throws android.os.RemoteException
{
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
//两数之和
public int add(int num1, int num2) throws android.os.RemoteException;
//添加一个任务
public com.example.ipcdemo.TaskInfo addTask(com.example.ipcdemo.TaskInfo info) throws android.os.RemoteException;
在解释之前,先了解一些概念:
- IInterface : AIDL 文件必须继承的接口,它只有一个方法 IBinder asBinder() ,实现它的类,代表的是能够跨进程传输 Binder 对象,或者 Binder 代理对象,比如上面的代码中,返回this,表示 Stub 这个内部类是具有跨进程的作用的。
- IBinder : 实现这个接口的对象具备跨进程书传输的能力,在跨进程数据流经驱动时,驱动会识别 Binder 类型的数据,从而自动完成不同进程 Binder 本地对象以及 Binder 代理对象的转换。
二. 分析Binder 流程
首先,我们看看我们在服务中时怎么构建 Binder 服务的:
//AIDL 的服务,
IRemoteService.Stub mBinder = new IRemoteService.Stub() {
@Override
public int add(int num1, int num2) throws RemoteException {
/**
* 这里为具体的实现方法,比如直接返回两数之和
*/
Log.d(TAG, "zsr add: 接收到客户端传递的两个数字: "+num1+" "+num2);
return (num1 + num2);
}
@Override
public TaskInfo addTask(TaskInfo info) throws RemoteException {
Log.d("zsr", "收到客户端的信息: "+info);
info.id = 0;
info.progress = 50;
// mHandler.sendEmptyMessage(1);
// mTaskInfo = info;
return info;
}
};
可以看到,使用的是 IRemoteService 的子类 Stub ,那我们从这个类开始分析好了。
DESCRIPTOR
看到在 Stub 子类中,实现了一个标记字符串,用包名加类型表示:
private static final java.lang.String DESCRIPTOR = "com.example.ipcdemo.IRemoteService";
然后在它的构造方法中,注册当前的 Binder 和 字符串:
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
后面需要从 ServerManager 中拿到binder 时,就需要这个字符串了,比如:
queryLocalInterface(DESCRIPTOR)
asInterface
将服务端的 Binder 对象转换成客户端所有的 AIDL 对象;比如:
IRemoteService mBinder = IRemoteService.Stub.asInterface(service);
这个转换是分进程的,如果同个进程,则直接返回自身即可,如果是跨进程,则需要使用它的代理类,去转换了。从代码也可以看出来:
public static com.example.ipcdemo.IRemoteService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.ipcdemo.IRemoteService))) {
return ((com.example.ipcdemo.IRemoteService)iin);
}
return new com.example.ipcdemo.IRemoteService.Stub.Proxy(obj);
}
asBinder
从上面的介绍中已经知晓,返回的对象,具备跨进程的能力,这里返回 this,表示 Stub。
onTransact
该方法运行在服务端的Binder线程池中,数据的写入和结果读取都在这。
当客户端发起跨进程请求时,远程请求会通过底层封装后,交由次方法来处理;从 code 中,可以知道当前执行的是哪个方法,比如我们定义的,add() 和 addTask() 它们的 index 为:
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addTask = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
这样,我们就可以通过 code 去拿到对应的方法:
继续看 onTransact(int code, Parcel data, Parcel reply, int flags),它的参数有可以序列换和反序列化的 Parcel 参数,data 和 reply;
我们可以从 data 中取出参数(如果方法有参数的话),注意有顺序,然后把结构写入 replay ,这样当你的方法有返回值的时候,replay 返回的就是你需要的。
注意,onTransact 需要 返回 true,返回false 则表示请求失败。
IRemoteService#Proxy
从上面到说,当跨进程调用时,asInterface 返回的是代理类的实力,它也是实现 IRemoteService 接口,在Android 进程间通信(二) – 理解 Binder 的机制 我们知道,Binder 的代理模式,其实就是当 A 访问 B 的 object 方法,B 不会把的 object 给到 A,而是给它一个具体相同方法的代理类,然后A 通过这个代理把参数传递给 B,B 计算之后,把结果再传递给 A;从而实现跨进程的作用。
而它也确实是这么做的:
首先通过 _data 把参数都写进去,接着使用mRemote.transact 把参数传递给服务,它胡调用 onTransact 方法,然后通过线程池,把结果写入 reply ,最后再返回给 Binder,完成了跨进程的动作。
这样,我们就完成了 AIDL 的分析
三. 自定义 AIDL 服务
从上面看,AIDL 中,系统生成的逻辑比较乱,且所有类都集成在一起,但我们理清楚之后,还是比较容易理解的。那么这里,我们自己来写一个,而不是使用系统生成的。
首先,定义一个接口,让它集成 IInterface,并添加 add 和 addTask 方法,且定义好方法的 id:
public interface IRemote extends IInterface {
static final java.lang.String DESCRIPTOR = "com.example.ipcdemo.aidl.IRemote";
static final int TRANSACTION_add = (IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addTask = (IBinder.FIRST_CALL_TRANSACTION + 1);
//两数之和
public int add(int num1, int num2) throws RemoteException;
//添加一个任务
public TaskInfo addTask(TaskInfo info) throws RemoteException;
}
然后创建一个具备跨进程的实现类 IRemoteLmpl ,集成 Binder 和实现 IRemote 接口:
public class IRemoteImpl extends Binder implements IRemote {
@Override
public int add(int num1, int num2) throws RemoteException {
return 0;
}
@Override
public TaskInfo addTask(TaskInfo info) throws RemoteException {
return null;
}
@Override
public IBinder asBinder() {
return this;
}
}
接着,我们应该在的构造方法中,把binder 和 字符串描述注册到服务中:
public IRemoteImpl() {
this.attachInterface(this,DESCRIPTOR);
}
实现供客户端调用的服务端转换的 AIDL 对象的方法 asInterface:
public static IRemote asInterface(IBinder obj){
if (obj == null) {
return null;
}
if (obj instanceof IRemote){
return (IRemote) obj;
}
return new Proxy(obj);
}
其中 Proxy 代理类,我们后面再写。
接着,编写 IRemote 的具体实现方法 onTransact :
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
//通过 code 对应的方法
switch (code) {
case TRANSACTION_add:
data.enforceInterface(DESCRIPTOR);
//接受到参数数据
int num1 = data.readInt();
int num2 = data.readInt();
int result = this.add(num1, num2);
reply.writeNoException();
//把结果返回到返回参数中
reply.writeInt(result);
return true;
case TRANSACTION_addTask:
data.enforceInterface(DESCRIPTOR);
TaskInfo info = null;
if (data.readInt() != 0) {
//通过反序列化拿到数据
info = TaskInfo.CREATOR.createFromParcel(data);
}
TaskInfo resultInfo = this.addTask(info);
reply.writeNoException();
if ((resultInfo!=null)) {
reply.writeInt(1);
info.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
default:
return super.onTransact(code, data, reply, flags);
}
}
无法就是一些数据的转换,没啥问题,接着,实现代理类,方便跨进程调用:
/**
* 跨进程的代理类
*/
private static class Proxy implements IRemote {
private IBinder mRemote;
Proxy(IBinder iBinder) {
mRemote = iBinder;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public IBinder asBinder() {
return mRemote;
}
@Override
public int add(int num1, int num2) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
int reslut;
try {
data.writeInterfaceToken(DESCRIPTOR);
data.writeInt(num1);
data.writeInt(num2);
/**
* 可以看到,当跨进程被调用的时候,只是把调用方的参数,给自身的方法运行,然后再把结果返回回去
*/
mRemote.transact(TRANSACTION_add, data, replay, 0);
replay.readException();
reslut = replay.readInt();
} finally {
data.recycle();
replay.recycle();
}
return reslut;
}
@Override
public TaskInfo addTask(TaskInfo info) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
TaskInfo result;
try {
data.writeInterfaceToken(DESCRIPTOR);
if ((info != null)) {
//用数据 0,1 ,来区分是否写入成功
data.writeInt(1);
info.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
mRemote.transact(TRANSACTION_addTask, data, replay, 0);
replay.readException();
if ((0 != replay.readInt())) {
result = TaskInfo.CREATOR.createFromParcel(replay);
} else {
result = null;
}
} finally {
data.recycle();
replay.recycle();
}
return result;
}
}
这样,我们的代码就写完了
接着修改给其他调用的 RemoteService 的 AIDL 服务:
//AIDL 的服务,
IRemoteImpl mBinder = new IRemoteImpl() {
@Override
public int add(int num1, int num2) throws RemoteException {
/**
* 这里为具体的实现方法,比如直接返回两数之和
*/
Log.d(TAG, "zsr add: 接收到客户端传递的两个数字: "+num1+" "+num2);
return (num1 + num2);
}
@Override
public TaskInfo addTask(TaskInfo info) throws RemoteException {
Log.d("zsr", "收到客户端的信息: "+info);
info.id = 0;
info.progress = 50;
// mHandler.sendEmptyMessage(1);
// mTaskInfo = info;
return info;
}
};
然后,把这两个方法复制到 客户端:
绑定服务端服务:
//绑定 AIDL 服务
intent.setClassName("com.example.ipcdemo","com.example.ipcdemo.service.RemoteService");
class RemoteService implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// mBinder = IRemoteService.Stub.asInterface(service);
mBinder = IRemoteImpl.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
然后在点击事件中,调用 AIDL 方法,拿到数据:
public void testAidl(View view) {
try {
if (mBinder != null) {
TaskInfo info = new TaskInfo();
info.url = "www.google.com";
TaskInfo taskInfo = mBinder.addTask(info);
int num = mBinder.add(2,3);
Log.d(TAG, "zsr testAidl: 获取到服务端数据: "+taskInfo+" "+num);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
打印如下;
这样,我们不通过 AIDL 也实现了跨进程通信了。
工程代码:https://gitee.com/zhengshaorui/IpcDemo