一、前言
上一篇文章,講述了實現(xiàn)序列化和反序列化的基本方式,是實現(xiàn)進程間通訊的必要條件,而這篇文章主要來講一講IPC的主要方式之一——AIDL方式。除了AIDL方式,IPC還有其他進程間通訊方式,比如Messager、ContentProvider、Socket等,這些以后會講到?,F(xiàn)在先說說AIDL的基本使用方法。
二、什么是AIDL?
AIDL全稱:Android Interface Definition Language,即Android接口定義語言。由于不同的進程不能共享內(nèi)存,所以為了解決進程間通訊的問題,Android使用一種接口定義語言來公開服務的接口,本質(zhì)上,AIDL非常像一個接口,通過公開接口,讓別的進程調(diào)用該接口,從而實現(xiàn)進程間的通訊。
三、使用AIDL
以下結(jié)合一個具體實例來示范AIDL的使用方法。
1、建立.aidl文件
為了方便AIDL的開發(fā),建議把所有和AIDL相關的類和文件放入同一個包中,這樣方便把整個包復制,以便其他模塊或者應用需要用到同一個AIDL。在Android Studio下,專門為AIDL文件創(chuàng)建了一個文件夾,方便我們的管理:

可以看到,筆者新建了一個service模塊,該模塊在manifests的聲明如下:
<service android:name=".MyAidlService"
android:enabled="true"
android:exported="true"></service>
這個模塊為app模塊提供服務,與app模塊處于不同進程,所以模擬了進程間通訊的場景。在service模塊,新建一個AIDL文件夾,然后新建一個包,這里包名為com.chenyu.service,然后新建AIDL文件:IMyAidl.aidl:
// IMyAidl.aidl
package com.chenyu.service;
// Declare any non-default types here with import statements
import com.chenyu.service.Person;
interface IMyAidl {
void addPerson(in Person person);
List<Person> getPersonList();
}
這與定義一個接口的語法基本相同,都是以Interface為關鍵字定義。里面聲明了兩個方法,分別是addPerson(in Person person)與getPersonList()。AIDL中除了基本數(shù)據(jù)類型,其他類型的參數(shù)必須標上方向,in、out、或者inout,in表示輸入型參數(shù),out表示輸出型參數(shù),inout表示輸入輸出型參數(shù),我們要根據(jù)需要實際指定參數(shù)類型,因為底層的數(shù)據(jù)處理開銷非常大,如果不指定類型,編譯將會無法通過。
AIDL支持的數(shù)據(jù)類型:
①基本數(shù)據(jù)類型(int,long,char,boolean,double)
②string和CharSequence
③List:只支持ArrayList,以及里面所有的元素必須能夠被AIDL支持 ④Map:只支持HashMap,以及里面所有的元素必須能夠被AIDL支持 ⑤Parcelable:所有實現(xiàn)了Parcelable接口的對象
⑥AIDL:所有AIDL接口本身也可以在AIDL文件中使用。
注意一下:這里使用了自定義的Parcelable對象:Person類,但是AIDL不認識這個類,所以我們要創(chuàng)建一個與Person類同名的AIDL文件:Person.aidl
// IMyAidl.aidl
package com.chenyu.service;
//聲明Person
parcelable Person;
只有這樣,IMyAidl.aidl才能知道其中的Person是使用了Parcelable接口的類,注意,Person類的包名與Person.aidl的包名一定要相同,即無論其他應用或者其他模塊,只要有AIDL,都應該保證AIDL的所有包結(jié)構(gòu)一致,才能保證順利進行IPC通訊,減少不必要的麻煩。
2、Person類,實現(xiàn)Parcelable接口
在java文件夾中,創(chuàng)建com.chenyu.service包,這樣就與上面的是相同包名了。
package com.chenyu.service;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable {
private String name;
private int age;
private int number;
public Person(Parcel source) {
this.name=source.readString();
this.age=source.readInt();
this.number=source.readInt();
}
//getter、setter method
//...
public Person(int age, String name, int number) {
this.age = age;
this.name = name;
this.number = number;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
dest.writeInt(number);
}
public static final Parcelable.Creator<Person> CREATOR=new Creator<Person>() {
@Override
public Person createFromParcel(Parcel source) {
return new Person(source);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
@Override
public String toString() {
return "Person{" +
"name='" + name + '\\\\'' +
", age=" + age +
", number=" + number +
'}';
}
}
對于Parcelable接口的詳細解析,可參考上一篇文章,這里不再贅述。
3、實現(xiàn)服務端
上面我們定義了一個AIDL接口,接下來要做的是實現(xiàn)這個AIDL接口,在java/com.chenyu.service中,創(chuàng)建MyAidlService.java文件:
package com.chenyu.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
public class MyAidlService extends Service {
private ArrayList<Person> persons;
@Override
public IBinder onBind(Intent intent) {
persons=new ArrayList<Person>();
Log.d("cy", "success bind");
return iBinder;
}
private IBinder iBinder= new IMyAidl.Stub() {
@Override
public void addPerson(Person person) throws RemoteException {
persons.add(person);
}
@Override
public List<Person> getPersonList() throws RemoteException {
return persons;
}
};
@Override
public void onCreate() {
super.onCreate();
Log.d("cy", "onCreate ");
}
}
我們來看一下服務端是如何實現(xiàn)接口的:
?。?)為了實現(xiàn)來自.aidl文件生成的接口,需要繼承Binder接口(例如Ibinder接口),并且實現(xiàn)從.aidl文件中繼承的方法,在上面代碼中,使用匿名實例實現(xiàn)一個叫IMyAidl(定義在IMyAidl.aidl中)的接口,實現(xiàn)了兩個方法,addPerson和getPersonList.
?。?)onBind方法:該方法在客戶端與服務端連接的時候回調(diào),實現(xiàn)客戶端和服務端的綁定,并返回一個Binder實例,這里返回的是iBinder,而IBinder是(1)中實現(xiàn)了接口的匿名實例,即客戶端拿到的實際上實現(xiàn)了接口的一個實例,這樣,客戶端通過Binder就與服務端建立了連接,客戶端通過Binder遠程調(diào)用服務端的實例方法,這樣也即實現(xiàn)了進程間通訊。
4、實現(xiàn)客戶端
在實現(xiàn)客戶端之前,先確保把aidl的包復制過來,就相上面筆者所給出的結(jié)構(gòu)圖一樣,包括Person類也應該復制過來。顯示界面比較簡單,就不貼出來了,主要看Activity的代碼:
package com.chenyu.myaidl;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.chenyu.service.IMyAidl;
import com.chenyu.service.Person;
import java.util.List;
public class MainActivity extends Activity implements View.OnClickListener {
private Button btn;
IMyAidl iMyAidl;
private ServiceConnection conn=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("cylog", "onServiceConnected success");
iMyAidl=IMyAidl.Stub.asInterface(service); // 1
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("cylog", "onServicedisConnected ");
iMyAidl=null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
bindService();
}
private void initView() {
btn= (Button) findViewById(R.id.cal);
btn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
try {
iMyAidl.addPerson(new Person(21, "陳育", 22255));
List<Person> persons = iMyAidl.getPersonList(); // 2
Log.d("cylog",persons.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void bindService() {
Intent intent=new Intent();
intent.setComponent(new ComponentName("com.chenyu.service","com.chenyu.service.MyAidlService"));
bindService(intent,conn, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
}
與綁定一般Service的語法差不多,但是在安卓5.0之后,必須顯式指定Service的包名和類名即:
private void bindService() {
Intent intent=new Intent();
intent.setComponent(new ComponentName("com.chenyu.service","com.chenyu.service.MyAidlService"));
bindService(intent,conn, Context.BIND_AUTO_CREATE);
}
在bindService(intent,conn,Context.BIND_AUTO_CREATE)方法中有幾個參數(shù)需要說明一下,
①conn:該參數(shù)代表了與服務端的連接,即ServiceConnection.
②Context.BIND_AUTO_CREATE:該參數(shù)表示綁定的同時創(chuàng)建一個Service。
在發(fā)出請求綁定成功之后,會回調(diào)①處的代碼,此時,可在回調(diào)方法onServiceConnected()方法中,獲取服務端返回的IMyAidl實例,在客戶端拿到該實例之后,就可以通過調(diào)用相應的方法進行遠程通訊了,比如上述的②處代碼。
最后,看一下運行結(jié)果,先運行service,然后運行app:


可以看出,app端和service端的確是構(gòu)成了進程間通訊,并且完成了進程間通訊。
以上是利用AIDL實現(xiàn)進程通訊的基本方法,希望對大家有所幫助。關于AIDL的核心原理以及Binder,AIDL優(yōu)化,會在下一篇文章詳細講述。