AIDL,应该说是所有用跨进程通信的最好选择,但是本身缺点也很大:
1.两个应用进程必须要包名一致
2.定义完方法后需要重新编译,特别是有parcable类型数据需要传递的时候,需要手动添加包
3.自动编译生成的java类复杂看不懂
那么为什么会有以上缺点呢?我们可不可以改进呢?答案是肯定的。
我们先来看一个最基本的aidl:
interface IRootService01 {
//这是示例代码,可以注掉
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
//我们自己定义的一个方法,参数是String类型
void setData(String data);
}
编译之后生成代码,可以大概看一眼就可以,实在是太麻烦了:
public interface IRootService01 extends android.os.IInterface
{
/** Default implementation for IRootService01. */
public static class Default implements com.ftabchina.aidl.service.IRootService01
{
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
}
@Override public void setData(java.lang.String data) throws android.os.RemoteException
{
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.ftabchina.aidl.service.IRootService01
{
private static final java.lang.String DESCRIPTOR = "com.ftabchina.aidl.service.IRootService01";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.ftabchina.aidl.service.IRootService01 interface,
* generating a proxy if needed.
*/
public static com.ftabchina.aidl.service.IRootService01 asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.ftabchina.aidl.service.IRootService01))) {
return ((com.ftabchina.aidl.service.IRootService01)iin);
}
return new com.ftabchina.aidl.service.IRootService01.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_basicTypes:
{
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_setData:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
this.setData(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.ftabchina.aidl.service.IRootService01
{
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.
*/
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 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);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public void setData(java.lang.String data) 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.writeString(data);
boolean _status = mRemote.transact(Stub.TRANSACTION_setData, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().setData(data);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public static com.ftabchina.aidl.service.IRootService01 sDefaultImpl;
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_setData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public static boolean setDefaultImpl(com.ftabchina.aidl.service.IRootService01 impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.ftabchina.aidl.service.IRootService01 getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public void setData(java.lang.String data) throws android.os.RemoteException;
}
是不是看到之后头疼了?好麻烦啊,别着急,咱们先格式化一下:
public interface IRootService01 extends android.os.IInterface {
/**
* Default implementation for IRootService01.
*/
public static class Default implements com.ftabchina.aidl.service.IRootService01 {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
}
@Override
public void setData(java.lang.String data) throws android.os.RemoteException {
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.ftabchina.aidl.service.IRootService01 {
private static final java.lang.String DESCRIPTOR = "com.ftabchina.aidl.service.IRootService01";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.ftabchina.aidl.service.IRootService01 interface,
* generating a proxy if needed.
*/
public static com.ftabchina.aidl.service.IRootService01 asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.ftabchina.aidl.service.IRootService01))) {
return ((com.ftabchina.aidl.service.IRootService01) iin);
}
return new com.ftabchina.aidl.service.IRootService01.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_basicTypes: {
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0 != data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_setData: {
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
this.setData(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.ftabchina.aidl.service.IRootService01 {
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.
*/
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 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);
_data.writeLong(aLong);
_data.writeInt(((aBoolean) ? (1) : (0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString);
return;
}
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void setData(java.lang.String data) 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.writeString(data);
boolean _status = mRemote.transact(Stub.TRANSACTION_setData, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().setData(data);
return;
}
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
public static com.ftabchina.aidl.service.IRootService01 sDefaultImpl;
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_setData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public static boolean setDefaultImpl(com.ftabchina.aidl.service.IRootService01 impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.ftabchina.aidl.service.IRootService01 getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public void setData(java.lang.String data) throws android.os.RemoteException;
}
稍微清晰了一点,其实这些代码在自己的AS里查看会好一些,我们再把类代码整理一下,因为里面所有的类都是静态类,那么我们完全可以把代码抽取成普通类:
最后我们可以拆成 Default类(没用,可以删掉),IRootService01接口、Proxy代理类,Stub类,其实就后面三个类有用,这三个类你仔细观察后,又可以合并成一个类:
public class RootService01 extends Binder implements IInterface {
//常量即可
public static final java.lang.String DESCRIPTOR = "TAG";
//区分调用哪个方法常量
public static final int SET_DATA = 0x01;
//跨进程通信用binder
private IBinder mRemote;
//IInterface接口方法,返回自己就可以
@Override
public IBinder asBinder() {
return this;
}
//服务端创建实例用
public RootService01() {
this.attachInterface(this, DESCRIPTOR);
}
//客户端创建实例用,但是这个构造器不对外暴露,只能通过getInstance方法获取实例
private RootService01(IBinder binder) {
mRemote = binder;
}
//客户端获取实例方法
public static RootService01 getInstance(IBinder binder) {
//检查是否为本地服务,也就是不是跨进程通信
IInterface localInterface = binder.queryLocalInterface(DESCRIPTOR);
if (localInterface instanceof RootService01) {
return (RootService01) localInterface;
}
//创建远端通信对象
return new RootService01(binder);
}
/**
* 远端进程执行这个方法,由系统调用
*
* @param code 方法类型,区分调用的哪个方法
* @param data 参数容器
* @param reply 返回值容器
* @param flags 没什么用
* @return 表示是否执行,返回true
* @throws RemoteException
*/
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION:
if (null != reply) reply.writeString(DESCRIPTOR);
return true;
case SET_DATA://setData方法
//定位到参数所在系统内存位置
data.enforceInterface(DESCRIPTOR);
//读取客户端传递过来的参数
String params = data.readString();
//调用服务端setData方法,因此服务端必须重写setData 方法,且不能调用super
setData(params);
//如果有返回值则
//reply.writeString(aaa);
//如果执行到这里,则告诉客户端代码没有执行错误
reply.writeNoException();
return true;
}
return super.onTransact(code, data, reply, flags);
}
/**
* 注意这个方法在客户端和服务端都有
* 客户端:表示向服务端发送数据,可以直接调用这个方法,输入参数
* 服务端:必须要重写这个方法,而且不能调用super,params表示客户端传递过来的参数
*
* @param params
* @throws RemoteException
*/
public void setData(String params) throws RemoteException {
Parcel par = Parcel.obtain();
Parcel relay = Parcel.obtain();
try {
//用来标记参数在系统内存开始的地方
par.writeInterfaceToken(DESCRIPTOR);
//写入参数,多个参数则依次写入
par.writeString(params);
//通过binder将参数发送到系统共享内存处,SET_DATA表示调用哪个方法
mRemote.transact(SET_DATA, par, relay, 0);
//读取错误
relay.readException();
} finally {
par.recycle();
relay.recycle();
}
}
}
2个构造器,一个给服务端用,一个给客户端用,且客户端不可以直接通过构造器创建,4个方法:
asBinder 固定写法,返回this
getInstance 固定写法,先检查是否为同一个进程服务,客户端调用
onTransact 服务端调用方法,写法可以参考示例写,基本上也是固定写法
setData 我们要进行操作的方法,写法可以参考示例,基本上也都是固定写法
其实AIDL 本质就这么一个类,2个构造器,3个方法+我们通信的方法。
这里我们来回复一下AIDL第一个缺点,客户端服务端必须要包名一致,真的有这个必要吗?如果你按照AIDL去实现,那确实是必须要这样,如果按照我上面合成的一个类去写,那就可以随意,为什么?因为客户端和服务端需要同一个字符串类型TAG,用来做什么?用来标记参数在系统内存中开始的地方,比如客户端存到了1开始,那么服务端就可以从1开始取数据。
我们再来看看整理前使用AIDL和合并成一个类使用的区别:
整理前AIDL:(我把上面的类名改成AIDL了,要不和服务类型差不多,不好区分)
客户端:
Intent intent=new Intent(this, RootService.class);
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
IAIDL iaidl = IAIDL.Stub.asInterface(iBinder);
Log.e("TAG", "onServiceConnected: 客户端向服务端发送数据 1234");
try {
iaidl.setData("1234");
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
},Context.BIND_AUTO_CREATE);
服务端:
public class RootService extends Service {
private IBinder mBinder = new IAIDL.Stub() {
@Override
public void setData(String params) throws RemoteException {
Log.e("TAG", "setData: 客户端传递过来数据 " + params);
}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
日志输出为:
跨进程通信成功了。下面用我们修改后的类来实现一下:
Intent intent = new Intent(this, RootService.class);
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
AIDL instance = AIDL.getInstance(iBinder);
Log.e("TAG", "onServiceConnected: 修改后客户端发送数据为: 123456");
try {
instance.setData("123456");
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
}, Context.BIND_AUTO_CREATE);
public class RootService extends Service {
private IBinder mBinder = new AIDL() {
@Override
public void setData(String params) throws RemoteException {
Log.e("TAG", "setData: 修改后客户端传递过来数据为:" + params);
}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
日志输出为:
可以看到依旧实现了进程间通信。
我们总结一下AIDL通信的流程及注意事项:
第一步:建立连接,获取服务端Binder引用
1.调用bindservice,实际上是调用的ContextImp中bindService
2.调用AMS查找是否有这个Servcie,如果有则直接绑定返回
3.AMS通过Binder通信告诉SystemService创建Service进程,AMS通知进程启动服务
4.Service启动后通知AMS,并且将自己的onBind方法中的Binder引用告诉AMS
5.AMS拿到服务端Binder后通知客户端链接成功,并调用connected方法
第二步:开始通信
1.客户端用服务端Binder引用创建AIDL对象,如果是本地服务,则直接返回服务端AIDL对象,否则创建客户端AIDL对象,也就是服务端代理
2.将客户端数据存入Parcl中,通过服务端Binder引用,调用transact 方法,将客户端数据发送出去
3.经过Binder驱动,最终会调用到服务端onTransact方法,将客户端数据以及返回值容器去下,同时根据code码调用本地相应方法
4.Binder驱动此时处于等待状态,等待服务端返回值
5.将服务端执行结果告诉客户端