什麼是aidl:
aidl是 Android Interface definition language的縮寫,一看就明白,它是一種android內部進程通訊接口的描述語言,經過它咱們能夠定義進程間的通訊接口
icp:interprocess communication :內部進程通訊。java
在 Android中, 每一個應用程序都有本身的進程,當須要在不一樣的進程之間傳遞對象時,該如何實現呢? 顯然, Java中是不支持跨進程內存共享的。所以要傳遞對象, 須要把對象解析成操做系統可以理解的數據格式, 以達到跨界對象訪問的目的。在JavaEE中,採用RMI經過序列化傳遞對象。在Android中, 則採用AIDL(Android Interface Definition Language:接口定義語言)方式實現。
android
(1)在Eclipse Android工程的Java包目錄中創建一個擴展名爲aidl的文件。該文件的語法相似於Java代碼,但會稍有不一樣。
(2)若是aidl文件的內容是正確的,ADT會自動生成一個Java接口文件(*.java)。
(3)創建一個服務類(Service的子類)。
(4)實現由aidl文件生成的Java接口。
(5)在AndroidManifest.xml文件中配置AIDL服務,尤爲要注意的是,<action>標籤中android:name的屬性值就是客戶端要引用該服務的ID,也就是Intent類的參數值。 dom
AIDL 是一種接口定義語言,用於約束兩個進程間的通信規則,供編譯器生成代碼,實現Android設備上的兩個進程間通訊(IPC)。AIDL的IPC機制和 EJB所採用的CORBA很相似,進程之間的通訊信息,首先會被轉換成AIDL協議消息,而後發送給對方,對方收到AIDL協議消息後再轉換成相應的對 象。因爲進程之間的通訊信息須要雙向轉換,因此android採用代理類在背後實現了信息的雙向轉換,代理類由android編譯器生成,對開發人員來講 是透明的。
eclipse
實現進程通訊,通常須要下面四個步驟:ide
假設A應用須要與B應用進行通訊,調用B應用中的download(String path)方法,B應用以Service方式向A應用提供服務。須要下面四個步驟:this
1> 在B應用中建立*.aidl文件,aidl文件的定義和接口的定義很相類,如:在cn.itcast.aidl包下建立IDownloadService.aidl文件,內容以下:操作系統
package cn.itcast.aidl;代理
interface IDownloadService {xml
void download(String path);對象
}
當 完成aidl文件建立後,eclipse會自動在項目的gen目錄中同步生成IDownloadService.java接口文件。接口文件中生成一個 Stub的抽象類,裏面包括aidl定義的方法,還包括一些其它輔助方法。值得關注的是asInterface(IBinder iBinder),它返回接口類型的實例,對於遠程服務調用,遠程服務返回給客戶端的對象爲代理對象,客戶端在 onServiceConnected(ComponentName name, IBinder service)方法引用該對象時不能直接強轉成接口類型的實例,而應該使用asInterface(IBinder iBinder)進行類型轉換。
編寫Aidl文件時,須要注意下面幾點:
1.接口名和aidl文件名相同。
2.接口和方法前不用加訪問權限修飾符public,private,protected等,也不能用final,static。
3.Aidl默認支持的類型包話java基本類型(int、long、boolean等)和(String、List、Map、 CharSequence),使用這些類型時不須要import聲明。對於List和Map中的元素類型必須是Aidl支持的類型。若是使用自定義類型做 爲參數或返回值,自定義類型必須實現Parcelable接口。
4.自定義類型和AIDL生成的其它接口類型在aidl描述文件中,應該顯式import,即使在該類和定義的包在同一個包中。
5.在aidl文件中全部非Java基本類型參數必須加上in、out、inout標記,以指明參數是輸入參數、輸出參數仍是輸入輸出參數。
6.Java原始類型默認的標記爲in,不能爲其它標記。
2> 在B應用中實現aidl文件生成的接口(本例是IDownloadService),但並不是直接實現接口,而是經過繼承接口的Stub來實現(Stub抽象類內部實現了aidl接口),而且實現接口方法的代碼。內容以下:
public class ServiceBinder extends IDownloadService.Stub {
@Override
public void download(String path) throws RemoteException {
Log.i("DownloadService", path);
}
}
3> 在B應用中建立一個Service(服務),在服務的onBind(Intent intent)方法中返回實現了aidl接口的對象(本例是ServiceBinder)。內容以下:
public class DownloadService extends Service {
private ServiceBinder serviceBinder = new ServiceBinder();
@Override
public IBinder onBind(Intent intent) {
return serviceBinder;
}
public class ServiceBinder extends IDownloadService.Stub {
@Override
public void download(String path) throws RemoteException {
Log.i("DownloadService", path);
}
}
}
其餘應用能夠經過隱式意圖訪問服務,意圖的動做能夠自定義,AndroidManifest.xml配置代碼以下:
<service android:name=".DownloadService" >
<intent-filter>
<action android:name="cn.itcast.process.aidl.DownloadService" />
</intent-filter>
</service>
4> 把B應用中aidl文件所在package連同aidl文件一塊兒拷貝到客戶端A應用,eclipse會自動在A應用的gen目錄中爲aidl文件同步生成IDownloadService.java接口文件,接下來就能夠在A應用中實現與B應用通訊,代碼以下:
public class ClientActivity extends Activity {
private IDownloadService downloadService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.bindService(new Intent("cn.itcast.process.aidl.DownloadService"), this.serviceConnection, BIND_AUTO_CREATE);//綁定到服務
}
@Override
protected void onDestroy() {
super.onDestroy();
this.unbindService(serviceConnection);//解除服務
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
downloadService = IDownloadService.Stub.asInterface(service);
try {
downloadService.download("http://www.itcast.cn");
} catch (RemoteException e) {
Log.e("ClientActivity", e.toString());
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
downloadService = null;
}
};
}
Aidl默認支持的類型包話java基本類型(int、long、boolean等)和(String、List、Map、CharSequence),若是要傳遞自定義的類型該如何實現呢?
進程間傳遞自定義類型的實現過程請參見頁面下方備註欄:
1> 建立自定義類型,並實現Parcelable接口,使其支持parcelable協議。如:在cn.itcast.domain包下建立Person.java:
package cn.itcast.domain;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable
private Integer id;
private String name;
public Person(){}
public Person(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {//把javanbean中的數據寫到Parcel
dest.writeInt(this.id);
dest.writeString(this.name);
}
//添加一個靜態成員,名爲CREATOR,該對象實現了Parcelable.Creator接口
public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>(){
@Override
public Person createFromParcel(Parcel source) {//從Parcel中讀取數據,返回person對象
return new Person(source.readInt(), source.readString());
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
}
2> 在自定義類型所在包下建立一個aidl文件對自定義類型進行聲明,文件的名稱與自定義類型同名。
package cn.itcast.domain;
parcelable Person;
3> 在接口aidl文件中使用自定義類型,須要使用import顯式導入,本例在cn.itcast.aidl包下建立IPersonService.aidl文件,內容以下:
package cn.itcast.aidl;
import cn.itcast.domain.Person;
interface IPersonService {
void save(in Person person);
}
4> 在實現aidl文件生成的接口(本例是IPersonService),但並不是直接實現接口,而是經過繼承接口的Stub來實現(Stub抽象類內部實現了aidl接口),而且實現接口方法的代碼。內容以下:
public class ServiceBinder extends IPersonService.Stub {
@Override
public void save(Person person) throws RemoteException {
Log.i("PersonService", person.getId()+"="+ person.getName());
}
}
5> 建立一個Service(服務),在服務的onBind(Intent intent)方法中返回實現了aidl接口的對象(本例是ServiceBinder)。內容以下:
public class PersonService extends Service {
private ServiceBinder serviceBinder = new ServiceBinder();
@Override
public IBinder onBind(Intent intent) {
return serviceBinder;
}
public class ServiceBinder extends IPersonService.Stub {
@Override
public void save(Person person) throws RemoteException {
Log.i("PersonService", person.getId()+"="+ person.getName());
}
}
}
其餘應用能夠經過隱式意圖訪問服務,意圖的動做能夠自定義,AndroidManifest.xml配置代碼以下:
<service android:name=".PersonService" >
<intent-filter>
<action android:name="cn.itcast.process.aidl.PersonService " />
</intent-filter>
</service>
6> 把應用中的aidl文件和所在package一塊兒拷貝到客戶端應用的src目錄下,eclipse會自動在客戶端應用的gen目錄中爲aidl文件同步生 成IPersonService.java接口文件,接下來再把自定義類型文件和類型聲明aidl文件及所在package一塊兒拷貝到客戶端應用的src 目錄下。
最後就能夠在客戶端應用中實現與遠程服務的通訊,代碼以下:
public class ClientActivity extends Activity {
private IPersonService personService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.bindService(new Intent("cn.itcast.process.aidl.PersonService"), this.serviceConnection, BIND_AUTO_CREATE);//綁定到服務
}
@Override
protected void onDestroy() {
super.onDestroy();
this.unbindService(serviceConnection);//解除服務
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
personService = IPersonService.Stub.asInterface(service);
try {
personService.save(new Person(56,"liming"));
} catch (RemoteException e) {
Log.e("ClientActivity", e.toString());
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
personService = null;
}
};
}
最後咱們在總結一下android中 本地服務和 AIDL服務的區別: 本地服務不支持onBind(),它從onBind()返回null,這種類型的服務只能由承載服務的應用程序組件訪問。能夠調用 startService()來調用本地服務。AIDL服務能夠同時供 同一進程內的組件和其餘應用程序的組件使用。這種類型的服務在AIDL 文件中爲自身與其客戶端定義一個契約。服務實現 AIDL契約,而客戶端綁定到 AIDL定義。服務經過從 onBind()方法 返回AIDL接口的實現,來實現契約。客戶端經過調用 bindService()來綁定到AIDL服務,並調用 unBindService()來從服務斷開。