俩app(也就是俩进程的意思),分别为client和server(模拟binder C/S架构),server端提供的服务为一个加法计算的服务(方便理解,从最简单的入手),client端本地通过aidl远程去访问server端的服务,并得到计算结果。
1.展开server项目目录
2.创建CalcAidl.aidl:鼠标右击src文件夹new—Aidl File—>CalcAidl.aidl
3.编写CalcAidl.aidl
// CalcAidl.aidl
package zj.com.aidl_server;
// Declare any non-default types here with import statements
interface CalcAidl {
int calc(int a,int b);
}
4.clean project,确保生成build/gen/source/packagename/CalcAidl.java
5.将CalcAidl.aidl拷贝到客户端项目下,注意在客户端的包名路径要和服务端一样(重要)
6.创建并编写CalcService.java
public class CalcService extends Service {
@Override
public IBinder onBind(Intent intent) {
return mIBinder;
}
IBinder mIBinder = new CalcAidl.Stub() {
@Override
public int calc(int a, int b) throws RemoteException {
return a + b;
}
};
}
1.确保已经拷贝了CalcAidl.aidl在对应相同的包名路径下。并clean项目,确保gen目录下生成了CalcAidl.java
2.定义界面
3.通过绑定远程service,初始化远程iBinder实例
......
private void prepareAidl() {
Intent intent = new Intent();
intent.setComponent(new ComponentName("zj.com.aidl_server","zj.com.aidl_server.CalcService"));
bindService(intent,con, Context.BIND_AUTO_CREATE);
}
private ServiceConnection con = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mCalcAidl = CalcAidl.Stub.asInterface(service);//初始化远程iBinder实例
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
......
4.点击计算的时候,调用远程的“计算”方法,算出结果反馈到本地界面上。
calcbtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int num1 = Integer.parseInt(MainActivity.this.num1.getText().toString());
int num2 = Integer.parseInt(MainActivity.this.num2.getText().toString());
//调远程服务接口
try {
int calc = mCalcAidl.calc(num1, num2);
result.setText("" + calc);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
1.先让service端运行起来
2.再让client端运行起来
3.操作client端调用远程服务的方法进行计算
/**
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\android\\demo\\IPC_AIDL\\Aidl_Server\\app\\src\\main\\aidl\\zj\\com\\aidl_server\\CalcAidl.aidl
*/
package zj.com.aidl_server;
// Declare any non-default types here with import statements
public interface CalcAidl extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
* 这个Stub内部类实际上就是一个Binder
*/
public static abstract class Stub extends android.os.Binder implements zj.com.aidl_server.CalcAidl {
//Binder唯一标识,一般用当前Binder的类名表示。
private static final java.lang.String DESCRIPTOR = "zj.com.aidl_server.CalcAidl";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an zj.com.aidl_server.CalcAidl interface,
* generating a proxy if needed.
* 用于将服务端的Binder对象准换成客户端所需要的AIDL接口类型对象,这种转换过程是区分进程的,如果客户端和服务端位于
* 同一进程,那么此方法返回的就是服务端的Stub对象本身,否则返回系统封装后的Stub.proxy对象
*/
public static zj.com.aidl_server.CalcAidl asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof zj.com.aidl_server.CalcAidl))) {
return ((zj.com.aidl_server.CalcAidl) iin);
}
return new zj.com.aidl_server.CalcAidl.Stub.Proxy(obj);
}
//此方法用于返回当前Binder对象
@Override
public android.os.IBinder asBinder() {
return this;
}
/**
* 这个方法运行在服务端中的Binder线程池当中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法处理。
*/
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_calc: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.calc(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements zj.com.aidl_server.CalcAidl {
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 calc(int a, int b) 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(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_calc, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_calc = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public int calc(int a, int b) throws android.os.RemoteException;
}
大致的注释我注了一下,但是我这里还是单独的将每个方法和元素拧出来说一下。
public static abstract class Stub extends android.os.Binder implements zj.com.aidl_server.CalcAidl { …
Stub即Binder
DESCRIPTOR
Binder唯一标识,一般用当前Binder的类名表示
asInterface
用于将服务端的Binder对象准换成客户端所需要的AIDL接口类型对象,这种转换过程是区分进程的,如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身,否则返回系统封装后的Stub.proxy对象
asBinder
此方法用于返回当前Binder对象
onTransact { switch (code) { … }
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_calc: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.calc(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
这个方法运行在服务端中的Binder线程池当中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法处理。
方法体内有一个 “java switch (code) {… “,这个code是让其知道需要执行哪一个方法
方法体内有一个 “_arg1 = data.readInt(); “,这个data能够得到一个参数
方法体内有一个 “reply.writeInt(_result);”,这个reply携带结果并返回
另外,这个方法的返回值返回false时,那么客户端的请求会失败,我们可以利用这一特征来做权限验证,让经过我们授权的客户端才可以调用我们的服务。
@Override
public int calc(int a, int b) 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(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_calc, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
1.这个方法的运行由客户端发起
2.创建需要的Parcel对象,_data,_reply,_result,并writeInt一些无聊的小数据
3.m**Remote.transact(Stub.TRANSACTION_calc, _data, _reply, 0);**transact发起RPC(远程过程调用)
4.当前线程挂起
5.远程服务端的onTransact被调用,RPC过程返回
6.客户端挂起想成被唤醒,从_reply中取出RPC过程返回的结果,最后返回_reply中的数据
经过这一波分析和学习,我们知道,AIDL,实际上是我们用来帮助我们生成Binder的一种手段,理论上我们完全可以脱离AIDL来手写一个Binder。并且这里也只是应用并未涉及到原理。
《android开发艺术探索》
Demo:https://github.com/zj614android/CaclAIDL