AIDL是什麼,作用是什麼
項目中涉及到兩個應用即兩個進程間進行數據通信,而Android中進程之間的內存地址是相互獨立的,一個進程無法訪問另一個進程的內存地址。這裏簡單介紹下進程的概念,進程是一個程序或者應用的實體,每個進程都擁有自己獨立的內存地址空間。官方文檔上講,爲進行通信,進程需將其對象分解成可供操作系統理解的原語,並將其編組爲可供您操作的對象。編寫執行該編組操作的代碼較爲繁瑣,因此 Android 會使用 AIDL來完成跨進程通信。
Android中的跨進程其實就是IPC(Inter-Process Communication)通信,而AIDL正是IPC通信的機制。
AIDL全稱是Android Interface Definition Language,是安卓接口定義的意思,通過定義相關的接口來實現跨進程通信。
AIDL使用
1.AIDl支持的數據類型
AIDL 使用一種簡單語法,允許您通過一個或多個方法(可接收參數和返回值)來聲明接 口。參數和返回值可爲任意類型,甚至是 AIDL 生成的其他接口。
您必須使用 Java 編程語言構建 .aidl 文件。每個 .aidl 文件均須定義單個接口,並且只需要接口聲明和方法簽名。
默認情況下,AIDL 支持下列數據類型:
Java 編程語言中的所有原語類型(如 int、long、char、boolean 等java基本數據類型)
1.String
2.CharSequence
3.List
List 中的所有元素必須是以上列表中支持的數據類型,或者您所聲明的由 AIDL 生成的其他接口或 Parcelable 類型。您可選擇將 List 用作“泛型”類(例如,List)。儘管生成的方法旨在使用 List 接口,但另一方實際接收的具體類始終是 ArrayList。
4. Map
Map 中的所有元素必須是以上列表中支持的數據類型,或者您所聲明的由 AIDL 生成的其他接口或 Parcelable 類型。不支持泛型 Map(如 Map<String,Integer> 形式的 Map)。儘管生成的方法旨在使用 Map 接口,但另一方實際接收的具體類始終是 HashMap。
5.其他 AIDL 生成的接口
6.實現 Parcelable 的類數據類型
2.編寫AIDL相關代碼
編寫服務端代碼
創建實體類Person類,實現Parcelable接口,用於跨進程通信的實體類。
public class People implements Parcelable {
private String id;
private String name;
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(name);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<People> CREATOR = new Creator<People>() {
@Override
public People createFromParcel(Parcel in) {
People people = new People();
people.setId(in.readString());
people.setName(in.readString());
return people;
}
@Override
public People[] newArray(int size) {
return new People[size];
}
};
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
創建.aldl文件,這裏包括兩個文件,一個是實體類People的文件,一個是接口文件
// People.aidl
package com.uniubi.aidlapplication;
parcelable People;
// People.aidl
package com.uniubi.aidlapplication;
import com.uniubi.aidlapplication.People;
interface IRemoteService {
void basicTypes(int a, long b, in People person, in String d);
}
這裏要記住需要導入People類,否則.aidl文件會找不到。
實現接口
點擊android studio的make project
會在build/generated//Users/chenxuming/generated/aidl_source_output_dir/debug/compileDebugAidl/out/目錄下生成IRemoteService.java文件。
記住一定要在當前moudle的gradle文件裏指定.aidl文件路徑,否則編譯會找不到People這個類,
在這裏吃了個大虧。
sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
java.srcDirs = ['src/main/java', 'src/main/aidl']
resources.srcDirs = ['src/main/java', 'src/main/aidl']
aidl.srcDirs = ['src/main/aidl']
res.srcDirs = ['src/main/res']
assets.srcDirs = ['src/main/assets']
}
}
查看生成的java文件,可以看出接口IRemoteService類繼承android.os.IInterface接口,裏面包含了一個繼承Binder的Sub子類,並且這個Sub子類實現IRemoteService接口,後面我們將會具體講講這個生成的IRemoteService類具體源碼。
創建Service,並在其中創建binder對象,通過onBind方法返回這個binder對象,到此爲止服務端代碼編寫完成。
public class RemoteService extends Service {
private static final String TAG = "RemoteService";
private IRemoteService.Stub binder = new IRemoteService.Stub() {
@Override
public void basicTypes(int a, long b, People person, String d) throws RemoteException {
Log.i(TAG, "basicTypes" + person.getName());
}
};
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
向客戶端公開接口
1.將服務端src/main下面的整個aidl目錄下的文件拷貝到客戶端相同的位置,在客戶端項目下同樣別忘記了在當前module下的gradle配置sourceSets,能讓.aidl文件找到People類,然後make project。
2.綁定遠程服務,這裏必須是綁定遠程服務,“com.uniubi.aidlapplication.RemoteService”是遠程服務的actiob name,通過隱式的方式綁定。
Intent intent1 = new Intent();
intent1.setComponent(new ComponentName("com.uniubi.aidlapplication", "com.uniubi.aidlapplication.RemoteService"));
bindService(intent1, serviceConnection, BIND_AUTO_CREATE);
private IRemoteService iRemoteService;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("MainActivity", "onServiceConnected");
iRemoteService = IRemoteService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("MainActivity", "onServiceDisconnected");
}
};
通過代碼可以看出綁定遠程服務成功後,能拿到IBinder對象,然後通過IRemoteService.Stub.asInterface(service)轉成我們需要的iRemoteService對象,然後通過
iRemoteService對象,就可以進行跨進程請求了。
try {
if(iRemoteService != null){
iRemoteService.basicTypes(1, 2, people, "呵呵");
}
} catch (RemoteException e) {
Log.i("MainActivity", e.getMessage());
e.printStackTrace();
}
AIDL源碼分析
生成的代碼格式比較亂,最好先格式化下方便閱讀。
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/chenxuming/AndroidStudioProjects/AIDLClient/app/src/main/aidl/com/uniubi/aidlapplication/IRemoteService.aidl
*/
package com.uniubi.aidlapplication;
public interface IRemoteService extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.uniubi.aidlapplication.IRemoteService {
private static final java.lang.String DESCRIPTOR = "com.uniubi.aidlapplication.IRemoteService";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.uniubi.aidlapplication.IRemoteService interface,
* generating a proxy if needed.
*/
public static com.uniubi.aidlapplication.IRemoteService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.uniubi.aidlapplication.IRemoteService))) {
return ((com.uniubi.aidlapplication.IRemoteService) iin);
}
return new com.uniubi.aidlapplication.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 {
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();
com.uniubi.aidlapplication.People _arg2;
if ((0 != data.readInt())) {
_arg2 = com.uniubi.aidlapplication.People.CREATOR.createFromParcel(data);
} else {
_arg2 = null;
}
java.lang.String _arg3;
_arg3 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.uniubi.aidlapplication.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 void basicTypes(int a, long b, com.uniubi.aidlapplication.People person, java.lang.String d) 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(a);
_data.writeLong(b);
if ((person != null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
_data.writeString(d);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public void basicTypes(int a, long b, com.uniubi.aidlapplication.People person, java.lang.String d) throws android.os.RemoteException;
}
不難發現IRemoteService這個類由兩部分組成,一部分是聲明的接口,一部分是繼承Binder類的Sub內部類,這個Sub內部類有兩個主要的方法,asInterface() 和onTransact()方法,還有個內部類Proxy,一看名字就知道是代理模式,這裏就不細說代理模式了。
一步步來,首先看下iRemoteService = IRemoteService.Stub.asInterface(service)這行代碼,將服務端的binder對象解析成客戶端的iRemoteService對象,該方法返回的是IRemoteService.Stub.Proxy對象,也就是說iRemoteService對象就是IRemoteService.Stub.Proxy,然後再看調用 iRemoteService.basicTypes(1, 2, people, “呵呵”)的地方,其實看的就是Proxy對象的basicTypes方法,看到關鍵代碼mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);是通過mRemote對象的transact方法來進行通信,mRemote對象就是遠程服務端的Sub對象,點進去查看源碼,主要查看onTransact方法,而上面也講到Sub類裏是實現了Binder類的onTransact方法的(上面已貼出來,這裏不重複貼出)。
public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
發現onTransact是通過code來指定具體調用行爲,關鍵代碼this.basicTypes(_arg0, _arg1, _arg2, _arg3);而這裏的this就是遠程服務端的Sub對象,具體basicTypes方法實現得去服務端看,客戶端是看不到。到了這裏整個客戶端服務端跨進程通信的流程講解完畢。
總結
1.asInterface(android.os.IBinder obj)
用於服務端的Binder對象轉換成客戶端所需的AIDL接口類型的對象,這種轉換過程是區分進程的(如果客戶端和服務端位於同一個進程,那麼此方法返回的就是服務端的Stub對象本身,否則返回的是系統封裝後的Stub.proxy對象)
2.onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
客戶端跨進程請求主要是通過遠程服務binder對象的onTransact方法完成。
code : 確定客戶端請求的目標方法是什麼。
data : 如果目標方法有參數的話,就從data取出目標方法所需的參數。
reply : 當目標方法執行完畢後,如果目標方法有返回值,就向reply中寫入返回值。
flag : 0
3.DESCRIPTOR
Binder的唯一標識,一般用當前AIDL接口名錶示。
總之一切都是通過binder對象來完成,遠程服務端持有binder對象實體,本地客戶端Proxy持有服務端binder對象引用,並通過Sub.asInterface()方法轉換成本地的接口類對象。