一。AIDL 的作用
由于 android 的沙箱机制,每一个程序是运行在独立的进程中,每一个进程都有一个独立的 Dalvik VM,即每个进程自行管理内存,独自占有系统资源,并且进程之间是不能进行内存共享的,aidl 的作用就是用来解决进程之间互相通信的问题。
Messenger 是串行的通信方式,AIDL 是并行的通信方式。通信双方一个是 Service 端一个是 Client 端,这种关系是相对的。
在插件化应用中,每一个插件也是一个独立的 apk,相当于一个进程,aidl 很好的解决了不同插件之间的数据交互和通信。
二。相关知识
2.1 Parcelable 序列化
Parcelable 不同于 SerialLizable 序列化,Serialiable 方式是将整个对象进行序列化,Parcelable 是将一个完整的对象进行分解,分解之后的没一部分都是 Intent 支持传递的数据类型。
package com.chris_jason.pluginlibrary;
import android.os.Parcel;
import android.os.Parcelable;
/**
* 通信的消息类,实现 Parcelable 接口实现序列化
*/
public class HMessage implements Parcelable{
//消息内容
private String content;
//进程 id
private int pid;
public HMessage(){
}
protected HMessage(Parcel in) {
content = in.readString();
pid = in.readInt();
}
public static final Creator<HMessage> CREATOR = new Creator<HMessage>() {
@Override
public HMessage createFromParcel(Parcel in) {
return new HMessage(in);
}
@Override
public HMessage[] newArray(int size) {
return new HMessage[size];
}
};
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
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(content);
dest.writeInt(pid);
}
}
2.2 Binder
三。实现 aidl 步骤
新建 aidl 文件,添加自己的接口,sayHello,这个方法返回的是基础数据类型,sayMsg返回的是 HMessage 类型,需要先用 Parcelable 方式实现序列化。aidl 中引用了自定义类型的时候需要在头部导入该类。
```
// IRemoteService.aidl
package com.chris_jason.pluginlibrary;
//这里导入自定义类型,不然 aidl 文件会构建失败
import com.chris_jason.pluginlibrary.HMessage;
interface IRemoteService {
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
String sayHello();
HMessage sayMsg();
}
```
编写 Service 类,实现 aidl 接口中的方法
package com.chris_jason.pluginlibrary;
import android.app.Service;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
/**
* Created by mayikang on 17/8/31.
*/
public class RemoteService extends Service {
private String TAG="RemoteService";
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG,"onBind");
//这里代理了 binder
return mBinder;
}
@Override
public void onCreate() {
Log.e(TAG,"onCreate");
super.onCreate();
}
@Override
public void unbindService(ServiceConnection conn) {
Log.e(TAG,"unbindService");
super.unbindService(conn);
}
@Override
public boolean onUnbind(Intent intent) {
Log.e(TAG,"onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.e(TAG,"onDestroy");
super.onDestroy();
}
//在这里实例化了 aidl 接口,返回一个自定义的 binder
public IRemoteService.Stub mBinder=new IRemoteService.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public String sayHello() throws RemoteException {
return "hello-aidl";
}
@Override
public HMessage sayMsg() throws RemoteException {
HMessage msg=new HMessage();
msg.setContent("AIDL-MSG");
msg.setPid(Process.myPid());
return msg;
}
};
}
在 manifest 中注册该 service,并且为该 service 开启了一个新的进程:remote,exported:true 表示在其他进程中允许调用该组件,intent 中的 action 是用来过滤请求的
“`
package com.chris_jason.pluginlibrary;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.sctjsj.basemodule.base.ui.act.BaseAppcompatActivity;
public class MainActivity extends BaseAppcompatActivity {
private IRemoteService remoteService;
private String TAG="MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
findViewById(R.id.say).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent();
intent.setPackage("com.chris_jason.pluginlibrary");
intent.setAction("com.chris_jason.pluginlibrary.RemoteService");
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
});
findViewById(R.id.disconnect).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mServiceConnection!=null ){
try{
unbindService(mServiceConnection);
}catch (Exception e){
Log.e("excep",e.toString());
}
}
}
});
}
@Override
public int initLayout() {
return R.layout.activity_main;
}
@Override
public void reloadData() {
}
ServiceConnection mServiceConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG,"onServiceConnected");
remoteService=IRemoteService.Stub.asInterface(service);
try {
String str=remoteService.sayHello();
HMessage msg=remoteService.sayMsg();
Log.e(TAG,str);
Log.e(TAG,msg.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG,"onServiceDisconnected");
}
};
}
在 logcat 可以看到输出结果,日志会在不同的进程中输出,证明了这次通信是在两个进程之间进行通信的
四。除了这种玩法,还可以在一个工程中建两个 module,并且都指定为 android 工程,而不是 library,这种场景就像 apk1调用 apk2一样。Service 和 Client 的区别在于 Client 中没有 Service 类,Service 的实现 aidl 接口还是在服务端中,其他没有区别。
五。在 Service 和 Client两端进行通信的时候,如果有自定义类型的数据,aidl 文件夹中必须包含和该类同名的 aidl 文件,并且要在使用了该类的aidl 文件中手动导入该类,而且必须要保证两个端中该类的包结构和类名一致,不然会找不到该类