什么是AIDL
aidl是 Android Interface definition language的缩写,也就是安卓接口定义语言
为什么要有AIDL
AIDL允许你定义客户端与服务端达成一致的程序接口使用进程间通信相互交流。 在Android上面,一个进程不能正常的访问另一个进程的内存。 所以说,他们需要分解他们的对象为操作系统可以理解的基本单位,然后为你把这些对象按次序跨越进程边界 ,书写这些代码是单调冗长的,所以android使用AIDL为你处理这个问题。
注意:使用AIDL只有在你允许来自不同应用的客户端跨进程通信访问你的service,并且想要在你的service种处理多线程的时候才是必要的。 如果你不需要执行不同应用之间的IPC并发,你应该通过实现Binder建立你的接口,或者如果你想执行IPC,但是不需要处理多线程。那么使用Messenger实现你的接口。
如何使用aidl完成进程间通信
1.建立.aidl文件
AIDL使用一个简单的语法让你声明一个带有一个或者多个带有参数和返回值方法的接口 参数和返回值可以是任何类型,甚至是AIDL生成的接口
你必须使用java语言构建.aidl文件 每一个.aidl文件必须定义一个简单的接口并且要求只有接口声明和方法签名
默认的,AIDL支持下面数据类型
ava语言中的所有基本数据类型(比如int、long、char、boolean等等)
- String
- CharSequence
- List
List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是你定义的parcelable List可以使用范型(例如,List) 接收端的实际类经常是一个ArrayList,尽管方法是使用List接口生成的
- Map
Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是你定义的parcelable 范型map是不被支持的(比如这种形式Map) 接收端的实际类经常是一个HashMap,尽管方法是使用Map接口生成的
当定义你的service接口的时候,注意:
- 方法可以接收0或多个参数,并且有返回值或者返回void
- 所有非基本数据类型要求要求一个定向的tag来指定数据是去往哪个方向的 无论是输入、输出,还是输入输出(参加下面的例子) 基本数据类型是默认支持的,并且不能是其他的。
- .aidl文件中的所有的代码注释都在生成的IBinder接口中(除了在import和包声明之前的注释)
- 只支持方法,你不可以在AIDL暴露静态域
下面给出代码
以Android Stduio为例,我们看看如何使用AIDL进行进程间通信
1.创建IRemoteService .aidl文件
package com.example.aidlserver;
interface IRemoteService {
int getPid();
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
但是此时并没有AIDL的java文件产生,其实android studio也是带有自动生成的,只不过需要确认一些信息后才能生成。此时,我们可以在目录 build–>generated–>source–>aidl–>test–>debug下面发现还没有任何文件
此时,打开AndroidManifest.xml,确认package的值,如我这个
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.aidlserver">
关键性的一步,确认aidl文件所在的包名和AndroidMainifest.xml的package名是否一致。如果一致,点击 Build–>Make Project,生成相应的java文件。如果不一致,则改aidl的包名,改成一致,再点击生成,生成效果如图。
我们看看生成的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.aidlserver.IRemoteService {
private static final java.lang.String DESCRIPTOR = "com.example.aidlserver.IRemoteService";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.aidlserver.IRemoteService interface,
* generating a proxy if needed.
*/
public static com.example.aidlserver.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.aidlserver.IRemoteService))) {
return ((com.example.aidlserver.IRemoteService) iin);
}
return new com.example.aidlserver.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 {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getPid: {
data.enforceInterface(DESCRIPTOR);
int _result = this.getPid();
reply.writeNoException();
reply.writeInt(_result);
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;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.aidlserver.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 getPid() 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);
mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@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);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public int getPid() throws android.os.RemoteException;
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}
我们只看重点部分,这里有一个内部类Stub继承Binder,实现了IRemoteService接口,这个类中有一个方法asInterface,接收一个IBinder对象,并且返回一个IRemoteService类型的对象。
定义远程服务,暴漏接口给客户端
public class RemoteService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
@Override
public int getPid() throws RemoteException {
return Process.myPid();
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
};
}
同时,我们需要在清单文件中注册这个服务,并且添加一个action,因为我们是通过远程来调用服务的,所以必须指定一个action,用来隐式启动。
<service android:name=".RemoteService">
<intent-filter>
<action android:name="com.lxn.remote"/>
</intent-filter>
</service>
现在,当一个客户端(比如一个activity)调用bindService()来连接到这个service,这个客户端的onServiceConnected()回调函数接收service中onBind()方法返回的mBinder实例
客户端必须可以访问接口类,所以如果客户端和服务端在不同的应用中,那么客户端所在的应用必须有一份.aidl文件的副本,同样,在客户端我们创建一个aidl文件夹,然后把服务器端的aidl文件拷贝到这个目录下,注意:这个时候要保证文件所在的包名和服务器端aidl的包名一样,而不是和当前应用的包名一样,否则会报错。
当客户端在onServiceConnected()回调方法中接收到IBinder时,它必须调用你的ServiceInterface.Stub.asInterface(service)来把返回参数映射到你的ServiceInterface类型上。例如:
private IRemoteService remoteService;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
remoteService = IRemoteService.Stub.asInterface(service);
try {
int pid = remoteService.getPid();
int currentPid = Process.myPid();
System.out.println("currentPid: "+currentPid+", remotePid: "+pid);
remoteService.basicTypes(1,12,true,3,3,"aa");
} catch (RemoteException e) {
e.printStackTrace();
}
System.out.println("=====bind Success");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Toast.makeText(MainActivity.this, "service disConneted unexpected", Toast.LENGTH_SHORT).show();
remoteService = null;
}
};
完整代码
服务端:
public class RemoteService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
@Override
public int getPid() throws RemoteException {
System.out.println("=====Thread getPid: "+Thread.currentThread().getName());
return Process.myPid();
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
System.out.println("=====Thread basicTypes: "+Thread.currentThread().getName());
}
};
}
客户端:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void bindService(View view){
System.out.println("=====begin bindService");
Intent service = new Intent("com.lxn.remote");
//通过bindService绑定服务
bindService(service,conn,BIND_AUTO_CREATE);
}
private IRemoteService remoteService;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
remoteService = IRemoteService.Stub.asInterface(service);
try {
int pid = remoteService.getPid();
int currentPid = Process.myPid();
System.out.println("===currentPid: "+currentPid+", remotePid: "+pid);
remoteService.basicTypes(1,12,true,3,3,"aa");
} catch (RemoteException e) {
e.printStackTrace();
}
System.out.println("=====bind Success");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Toast.makeText(MainActivity.this, "service disConneted unexpected", Toast.LENGTH_SHORT).show();
remoteService = null;
}
};
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
}
我们点击客户端的绑定服务按钮,然后在控制台进行结果的输出
那么AIDL是什么原理呢?
AIDL其实通过我们写的aidl文件,帮助我们生成了一个接口,一个Stub类用于服务端,一个Proxy类用于客户端调用。关于详细的细节,我在这里就不讨论了,可以看下面这篇文章:
http://blog.csdn.net/lmj623565791/article/details/38461079
最后给出代码下载位置