一、什么是AIDL
AIDL是android接口定义语言,可以利用它定义客户端与服务端进行进程间通信 (IPC) 时都认可的编程接口。跨进程通信,不同的进程,运行在不同的VM虚拟机上,每个进程都有独立的内存空间,管理各自的数据,就像“我住长江头,君住长江尾。日日思君不见君,共饮长江水”。AIDL就是“我”与“君”沟通的桥梁。
二、AIDL支持的数据类型
“我”与“君”的联系的桥梁已经有了,但是这个桥梁上都有什么在跑呢?默认情况下,AIDL 支持下列数据类型:
- Java 编程语言中的所有原语类型(如
int
、long
、char
、boolean
等等) String
CharSequence
List
List
中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。可选择将List
用作“通用”类(例如,List<String>
)。另一端实际接收的具体类始终是ArrayList
,但生成的方法使用的是List
接口。Map
Map
中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。不支持通用 Map(如Map<String,Integer>
形式的 Map)。另一端实际接收的具体类始终是HashMap
,但生成的方法使用的是Map
接口。使用Map类型,有一点特别要注意,map中的key和value必须都是可序列化类型,否则使用会有问题。
三、通信传递的数据包
知道了数据类型,我们就可以飞鸽传书,一诉相思之苦,但是这个书不是普通的书,android定义通信的数据必须是序列化后的而数据,android默认使用的是Parcelable,实现Parcelable数据包很简单,只要去实现Parcelable的接口就可以了。我们定义个情书类实现了Parcelable接口。
/**
* <The trouble with the world is that the stupid are sure and the intelligent are full of doubt.>
* <p>
* jacky.aidltest
* <p>
* 作者:Jacky.Ao on 2018/3/16 13:32
* <p>
* 邮箱: jiazhi.ao@gmail.com
*/
public class Loveletter implements Parcelable {
//情书内容
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public int describeContents() {
return 0;
}
//写入数据
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.content);
}
//读取数据
public void readToParcel(Parcel dest) {
content = dest.readString();
}
public Loveletter() {
}
protected Loveletter(Parcel in) {
this.content = in.readString();
}
public static final Creator<Loveletter> CREATOR = new Creator<Loveletter>() {
@Override
public Loveletter createFromParcel(Parcel source) {
return new Loveletter(source);
}
@Override
public Loveletter[] newArray(int size) {
return new Loveletter[size];
}
};
}
写入数据和读取数据顺序要保持一致。如果用AS编码,有生成Parcelabale的插件,默认只支持写入数据,如果需要读取数据,需要自己去写,可以参考上面读取数据的代码。
四、飞鸽传书,以解相思
情书已经准备好了,如何传递出去的呢?Loveletter是自定义数据类型,所以我们要定义2个aidl文件:
// Loveletter.aidl
package jacky.aidltest;
// Declare any non-default types here with import statements
parcelable Loveletter;
// ILoveletter.aidl
package jacky.aidltest;
// Declare any non-default types here with import statements
import jacky.aidltest.Loveletter;
interface ILoveletter {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
String myConfession();
}
如果是自定义类型,需要新增一个aidl文件,声明为Parcelable类型。ILoveletter定义一个接口“我的告白”。aidl文件编译器会默认编译,如果用的as编程可以配置下编译路径。
sourceSets {
main {
java.srcDirs = ['src/main/java', 'src/main/aidl']
}
}
下面就开始书写情书了,就是写服务端代码:
/**
* <The trouble with the world is that the stupid are sure and the intelligent are full of doubt.>
* <p>
* jacky.aidltest
* <p>
* 作者:Jacky.Ao on 2018/3/16 14:03
* <p>
* 邮箱: jiazhi.ao@gmail.com
*/
public class LoveLetterService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final ILoveletter.Stub mBinder = new ILoveletter.Stub() {
@Override
public String myConfession() throws RemoteException {
return "I love you";
}
};
}
在服务端里,终于扯破嗓子喊出了这句“I love you”。但是“君”如何接收到这句滚烫的表白呢,下面看“君”即客户端如何解码数据包的。
public class KnockoutActivity extends Activity {
private TextView textView;
private ILoveletter mIRemoteService;
private String content;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_knockout);
textView = (TextView) findViewById(R.id.content);
bindService();
}
@Override
protected void onResume() {
super.onResume();
}
private void bindService() {
Intent intent = new Intent(this, LoveletterService.class);
intent.setAction(LoveletterService.class.getSimpleName());
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Following the example above for an AIDL interface,
// this gets an instance of the IRemoteInterface, which we can use to call on the service
mIRemoteService = ILoveletter.Stub.asInterface(service);
if (mIRemoteService != null) {
try {
content = mIRemoteService.myConfession();
textView.setText(content);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e("Service has unexpectedly disconnected");
mIRemoteService = null;
}
};
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mConnection);
}
}
至此终于打印出“I love you”这句告白了。
四、解密告白
服务端onbinder方法返回了ILoveletter.Stub对象类型为IBinder类型。客户端bindservice传入ServiceConnection,当与服务建立连接时会调用onServiceConnected这里也传入一个IBinder对象,这个IBinder就是LoveletterService里的mBinder对象。兴趣的可以跟踪下源码(PMS,ContextImpl.java,LoadedApk.java)。如何来获取mIRemoteService的实例呢,看下ILoveletter.Stub.asInterface(service)接口实现:
/**
* Cast an IBinder object into an jacky.aidltest.ILoveletter interface,
* generating a proxy if needed.
*/
public static jacky.aidltest.ILoveletter asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof jacky.aidltest.ILoveletter))) {
return ((jacky.aidltest.ILoveletter)iin);
}
Log.i("return proxy.");
return new jacky.aidltest.ILoveletter.Stub.Proxy(obj);
}
首先判断下传入的binder对象是否为空,为空直接return,然后通描述符检查是否在统一进程里,如果是同一个进程,直接返回,如果不在一个进程则返回一个Proxy对象。我们看下Proxy部分代码:
private static class Proxy implements jacky.aidltest.ILoveletter
{
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 java.lang.String myConfession() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_myConfession, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
如果client和server都在一个进程里,就直接调用getConfession接口,返回“I love you”。如果不在同一个进程,client端拿到Proxy对象,proxy持有server端Binder对象mRemote。初始化2个数据包,_data,_reply,_data写入描述符,进行数据包签名,用于onTransact校验,然后通过mRemote对象调用onTransact,并将_data,_reply传入,看下onTransact接口实现:
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
Log.i("onTransact: " + code);
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_myConfession:
{
data.enforceInterface(DESCRIPTOR);
String _result = this.myConfession();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
,data.enforceInterface(DESCRIPTOR)校验签名,调用myConfession返回值保存在_result中,然后将result写入reply数据包中。返回true,如果返回false,则无法传回client端。proxy部分通过onTransact从回传来的reply数据包中读取数据。
这样客户端“佳人”,拆开信封mIRemoteService.myConfession()就看见“我”的表白“I love you”。