一辅愿、介紹
AIDL(Android Interface Definition Language)是一種接口定義語言竿报。
它允許你定義一個(gè)編程接口何吝,用于約束兩個(gè)進(jìn)程間的通訊規(guī)則,提供給編譯器生成代碼鹃唯,實(shí)現(xiàn)Android設(shè)備上的兩個(gè)進(jìn)程間通信(IPC)爱榕。
在Android系統(tǒng)中,一個(gè)進(jìn)程通常不能訪問另一個(gè)進(jìn)程的內(nèi)存坡慌。所以他們需要將他們的對(duì)象拆解成操作系統(tǒng)所能識(shí)別的原語數(shù)據(jù)(primitives)黔酥,然后傳入到另一個(gè)進(jìn)程之后再替你組裝成對(duì)象。
也就是說進(jìn)程之間的通信信息洪橘,首先會(huì)被轉(zhuǎn)換成AIDL協(xié)議消息跪者,然后發(fā)送給對(duì)方,對(duì)方收到AIDL協(xié)議消息后再轉(zhuǎn)換成相應(yīng)的對(duì)象熄求。
由于進(jìn)程之間的通信信息需要雙向轉(zhuǎn)換渣玲,所以Android采用代理類在背后實(shí)現(xiàn)了信息的雙向轉(zhuǎn)換,代理類由Android編譯器生成弟晚。
二忘衍、適用場景
一般適用于為其它應(yīng)用程序提供公共服務(wù)的Service,這種Service即為系統(tǒng)常駐的Service(如:天氣服務(wù)等)指巡。
三淑履、優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
1.AIDL有自己的獨(dú)立進(jìn)程,不會(huì)受到其它進(jìn)程的影響藻雪;
2.可以被其它進(jìn)程復(fù)用秘噪,提供公共服務(wù);
3.具有很高的靈活性勉耀。
缺點(diǎn)
相對(duì)普通服務(wù)指煎,占用系統(tǒng)資源較多,使用AIDL進(jìn)行IPC也相對(duì)麻煩便斥。
四至壤、具體操作(此處我們創(chuàng)建一個(gè)服務(wù)端程序,一個(gè)客戶端程序)
1.生成AIDL文件(服務(wù)端創(chuàng)建)
選擇 new->aidl->aidl file
填寫創(chuàng)建的名稱
會(huì)在main目錄下為你創(chuàng)建一個(gè)aidl文件夾枢纠,并在里面與你的包名對(duì)應(yīng)創(chuàng)建了你的aidl文件像街,此文件默認(rèn)會(huì)給你創(chuàng)建一個(gè)方法,該方法只是作為類型案例解釋晋渺,可以不需要
點(diǎn)擊Gradle圖標(biāo)讓編譯器為我們生成對(duì)應(yīng)AIDL的 類 文件
2.創(chuàng)建Service提供服務(wù)镰绎,AIDL涉及到IPC通信,所以需要使用綁定服務(wù)木西。
public class MyService extends Service{
@Nullable
@Override
public IBinder onBind(Intent intent) {
return aidl;
}
IMyAidlInterface.Stub aidl = new IMyAidlInterface.Stub(){
public static final String TAG = "AIDL";
//此處重寫所有的抽象方法,實(shí)現(xiàn)提供對(duì)外暴露的接口
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
Log.i(TAG,"服務(wù)正在執(zhí)行...");
}
};
}
3.注冊(cè)服務(wù)
<application android:allowBackup="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<!--注冊(cè)服務(wù)-->
<service android:name=".MyService"
android:process="com.my.remote.service" >
<intent-filter>
<action android:name="com.myaidl.server"/>
</intent-filter>
</service>
</application>
如果客戶端與服務(wù)端在同個(gè)App中闺鲸,AndroidManifest.xml中設(shè)置Remote Service的andorid:process屬性時(shí)亏较,如果被設(shè)置的進(jìn)程名是以一個(gè)冒號(hào)(:)開頭的,則這個(gè)新的進(jìn)程對(duì)于這個(gè)應(yīng)用來說是私有的既忆,當(dāng)它被需要或者這個(gè)服務(wù)需要在新進(jìn)程中運(yùn)行的時(shí)候,這個(gè)新進(jìn)程將會(huì)被創(chuàng)建。如果這個(gè)進(jìn)程的名字是以小寫字符開頭的,則這個(gè)服務(wù)將運(yùn)行在一個(gè)以這個(gè)名字命名的全局的進(jìn)程中,當(dāng)然前提是它有相應(yīng)的權(quán)限重绷。這將允許在不同應(yīng)用中的各種組件可以共享一個(gè)進(jìn)程,從而減少資源的占用纵寝。
4.拷貝服務(wù)端aidl文件到客戶端目錄下(文件夾路徑不可變化)
點(diǎn)擊Gradle圖標(biāo)讓編譯器為我們生成對(duì)應(yīng)AIDL的 類 文件
5.客戶端連接代碼
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
//服務(wù)注冊(cè)的行為
intent.setAction("com.myaidl.server");
//5.0之后 需要設(shè)置服務(wù)所在的包名(服務(wù)APP的服務(wù)所在包名)
//否則會(huì)報(bào) IllegalArgumentException: Service Intent must be explicit
intent.setPackage("nightingale.aidl_server");
//綁定服務(wù)
bindService(intent,conn,BIND_AUTO_CREATE);
}
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//通過我們自己定義的AIDL文件的Stub.asInterface方法 傳入服務(wù)傳遞過來的iBinder對(duì)象 返回我們的AIDL對(duì)象
IMyAidlInterface aidl = IMyAidlInterface.Stub.asInterface(iBinder);
try {
//此處方法為你在AIDL文件中,自己定義的方法 此處對(duì)應(yīng)我們的AIDL文件
aidl.basicTypes(0,0,true,0,0,null);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onDestroy() {
unbindService(conn);
super.onDestroy();
}
}
如果無法調(diào)用AIDL對(duì)象(自定義的IMyAidlInterface)那么需要使用Gradle 構(gòu)建一下论寨,或者檢查你是否拷貝了服務(wù)端的aidl文件到客戶端
6.安裝服務(wù)端,安裝客戶端執(zhí)行結(jié)果
五爽茴、AIDL參數(shù)的傳遞(基礎(chǔ)數(shù)據(jù)類型 與 自定類型)
AIDL默認(rèn)支持下面幾種數(shù)據(jù)類型:
Java編程語言中的基礎(chǔ)數(shù)據(jù)類型(int,long,char,boolean等)
String葬凳,CharSequence,List室奏,Map火焰,在List 中的所有元素必須是上面支持的類型或者是其他由AIDL生成的接口,或者你申明的實(shí)現(xiàn)了Parcelable接口的類型胧沫。
List可能被選用為泛型類昌简,比如 List<String>.實(shí)際在接受服務(wù)一側(cè)生成的類為永遠(yuǎn)是 ArrayList。盡管生成的方法使用的是 List接口绒怨。
Map中的雖有元素必須是上面類型或者是其他由AIDL生成的接口纯赎,或者你申明的實(shí)現(xiàn)了Parcelable接口的類型。泛型例如 Map<String,Integer>不支持南蹂。實(shí)際在接受服務(wù)一側(cè)生成的類為永遠(yuǎn)是HashMap犬金。盡管生成的方法使用的是 Map接口。
你必須要為每一個(gè)上面未列出類型添加import申明六剥,盡管他們是作為接口定義在同一個(gè)包里面晚顷。
示例:
1.寫一個(gè)類 實(shí)現(xiàn) Parcelable
public class User implements Parcelable {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(){
}
protected User(Parcel in) {
name = in.readString();
age = in.readInt();
}
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(name);
parcel.writeInt(age);
}
}
2.在AIDL中定義獲取該類的方法
package nightingale.aidl_server;
//***導(dǎo)入對(duì)應(yīng)的類,必須***
import nightingale.aidl_server.User;
interface IMyAidlInterface {
//默認(rèn)的 可刪除 可不官 這里案例使用 所以 仍在這里吧
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
//獲取用戶對(duì)象
User getUser();
}
3.在aidl文件夾中聲明User對(duì)象
4.拷貝aidl中的文件到客戶端
5.拷貝服務(wù)端的User類到客戶端
注意:此處拷貝的時(shí)候,拷貝過去的類所在包名需要跟服務(wù)器所在包名一致疗疟,此處我創(chuàng)建了一個(gè) aidl_server包
6.服務(wù)端Service代碼
public class MyService extends Service{
@Nullable
@Override
public IBinder onBind(Intent intent) {
return aidl;
}
IMyAidlInterface.Stub aidl = new IMyAidlInterface.Stub(){
public static final String TAG = "AIDL";
//此處重寫所有的抽象方法,實(shí)現(xiàn)提供對(duì)外暴露的接口
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
Log.i(TAG,"服務(wù)正在執(zhí)行...");
}
@Override
public User getUser() throws RemoteException {
return null;
}
};
}
7.客戶端使用代碼
public class MainActivity extends AppCompatActivity {
private String TAG="aidl";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
//服務(wù)注冊(cè)的行為
intent.setAction("com.myaidl.server");
//5.0之后 需要設(shè)置服務(wù)所在的包名(服務(wù)APP的服務(wù)所在包名)
//否則會(huì)報(bào) IllegalArgumentException: Service Intent must be explicit
intent.setPackage("nightingale.aidl_server");
//綁定服務(wù)
bindService(intent,conn,BIND_AUTO_CREATE);
}
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//通過我們自己定義的AIDL文件的Stub.asInterface方法 傳入服務(wù)傳遞過來的iBinder對(duì)象 返回我們的AIDL對(duì)象
IMyAidlInterface aidl = IMyAidlInterface.Stub.asInterface(iBinder);
try {
User user = aidl.getUser();
String name = user.getName();
if(!TextUtils.isEmpty(name)){
Log.i(TAG,name);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onDestroy() {
unbindService(conn);
super.onDestroy();
}
}
執(zhí)行結(jié)果
至此 AIDL的相關(guān)操作就介紹完了??该默,不好請(qǐng)吐槽!謝謝
關(guān)于Messenger的介紹策彤,推薦閱讀:陳育 Android IPC機(jī)制(五):詳解Bundle與“信使”——Messenger:http://www.reibang.com/p/6e23037d6d20