AIDL
AIDL的作用
? ? 在Android平臺(tái),每個(gè)應(yīng)用程序都是一個(gè)單獨(dú)的JVM,都運(yùn)行在自己的進(jìn)程空間里, 通常,一個(gè)進(jìn)程不允許訪問(wèn)另一個(gè)進(jìn)程的內(nèi)存空間(一個(gè)應(yīng)用不能訪問(wèn)另一個(gè)應(yīng)用)。當(dāng)用戶(程序開(kāi)發(fā)人員)想在一個(gè)App中訪問(wèn)另一個(gè)App的進(jìn)程空間的時(shí)候,就需要進(jìn)程間通信。在Android中,遠(yuǎn)程服務(wù)為我們提供了實(shí)現(xiàn)進(jìn)程間通信的方式,其中,AIDL是應(yīng)用程序開(kāi)發(fā)人員常的一種方式。
? ? AIDL (Android Interface Definition Language) 是一種IDL 語(yǔ)言,用于生成可以在Android設(shè)備上兩個(gè)進(jìn)程之間進(jìn)行進(jìn)程間通信(interprocess communication, IPC)的代碼。如果在一個(gè)進(jìn)程中(例如Activity)要調(diào)用另一個(gè)進(jìn)程中(例如Service)對(duì)象的操作,就可以使用AIDL生成可序列化的參數(shù)。換句比較淺顯的話來(lái)說(shuō),就是我這個(gè)App應(yīng)用的activity,需要調(diào)用其他App應(yīng)用的Service.當(dāng)然同一App應(yīng)用的activity 與service也可以在不同進(jìn)程間,這可以設(shè)置Service配置中,android:process=":remote"。
? ??
? ? AIDL IPC機(jī)制是面向接口的,像COM或Corba一樣,但是更加輕量級(jí)。它是使用代理類在客戶端和實(shí)現(xiàn)端傳遞數(shù)據(jù)。
? ? 可以看出,aidl的適用場(chǎng)景為: 只有你允許客戶端從不同的應(yīng)用程序?yàn)榱诉M(jìn)程間的通信而去訪問(wèn)你的service時(shí),你可以使用AIDl來(lái)實(shí)現(xiàn)。例如,百度地圖給我們提供了下面的service:com.baidu.location.f,我們只要在我們的應(yīng)用程序的manifest.xml文件中聲明這個(gè)service,就能用它提供的服務(wù)了。:
? ? <service
? ? ? ? android:name="com.baidu.location.f"
? ? ? ? android:enabled="true"
? ? ? ? android:process=":remote" >
? ? </service>
定義AIDL接口
? ? AIDL接口文件,和普通的接口內(nèi)容沒(méi)有什么特別,只是它的擴(kuò)展名為.aidl。保存在src目錄下。如果其他應(yīng)用程序需要IPC,則那些應(yīng)用程序的src也要帶有這個(gè)文件。Android SDK tools就會(huì)在gen目錄自動(dòng)生成一個(gè)IBinder接口文件。service必須適當(dāng)?shù)貙?shí)現(xiàn)這個(gè)IBinder接口。那么客戶端程序就能綁定這個(gè)service并在IPC(Inter-Process Communication,進(jìn)程間通信)時(shí)從IBinder調(diào)用方法。
? ? 每個(gè)aidl文件只能定義一個(gè)接口,而且只能是接口的聲明和方法的聲明。
1.創(chuàng)建.aidl文件
? ? AIDL只支持方法,不能定義靜態(tài)成員,并且方法也不能有類似public等的修飾符;AIDL使用簡(jiǎn)單的語(yǔ)法來(lái)聲明接口,描述其方法以及方法的參數(shù)和返回值。這些參數(shù)和返回值可以是任何類型,甚至是其他AIDL生成的接口。
? ? 其中對(duì)于Java編程語(yǔ)言的基本數(shù)據(jù)類型 (int, long, char, boolean等),String和CharSequence,集合接口類型List和Map,不需要import 語(yǔ)句。
? ? 而如果需要在AIDL中使用其他AIDL接口類型,需要import,即使是在相同包結(jié)構(gòu)下。AIDL允許傳遞實(shí)現(xiàn)Parcelable接口的類,需要import.
? ? 需要特別注意的是,對(duì)于非基本數(shù)據(jù)類型,也不是String和CharSequence類型的,需要有方向指示,包括in、out和inout,in表示由客戶端設(shè)置,out表示由服務(wù)端設(shè)置,inout是兩者均可設(shè)置。
? ? AIDL只支持接口方法,不能公開(kāi)static變量。
? ? 例如 (IMyService.aidl):?
? ? package com.demo;?
? ??
? ? import com.demo.Person;?
? ??
? ? interface IMyService {?
? ? ? ? void savePersonInfo(in Person person);?
? ? ? ? List<Person> getAllPerson();?
? ? }
2.實(shí)現(xiàn)接口
? ? 創(chuàng)建一個(gè)類實(shí)現(xiàn)剛才那個(gè)aidl的接口:
? ? public class RemoteService extends Service {?
? ? ? ? private LinkedList<Person> personList = new LinkedList<Person>();?
? ? ? ? @Override?
? ? ? ? public IBinder onBind(Intent intent) {?
? ? ? ? ? ? return mBinder;?
? ? ? ? }
? ??
? ? ? ? private final IMyService.Stub mBinder = new IMyService.Stub(){
? ? ? ? ? ? @Override?
? ? ? ? ? ? public void savePersonInfo(Person person) throws RemoteException {?
? ? ? ? ? ? ? ? if (person != null){?
? ? ? ? ? ? ? ? ? ? personList.add(person);?
? ? ? ? ? ? ? ? }?
? ? ? ? ? ? }?
? ? ? ? ? ??
? ? ? ? ? ? @Override?
? ? ? ? ? ? public List<Person> getAllPerson() throws RemoteException {?
? ? ? ? ? ? ? ? return personList;?
? ? ? ? ? ? }?
? ? ? ? };?
? ? }
?
? ? 這里會(huì)看到有一個(gè)名為IMyService.Stub類,查看aidl文件生成的Java文件源代碼就能發(fā)現(xiàn)有這么一段代碼:
? ? public static abstract class Stub extends android.os.Binder implements com.demo.IMyService
? ? 原來(lái)Stub類就是繼承于Binder類,也就是說(shuō)RemoteService類和普通的Service類沒(méi)什么不同,只是所返回的IBinder對(duì)象比較特別,是一個(gè)實(shí)現(xiàn)了AIDL接口的Binder。
? ? 接下來(lái)就是關(guān)于所傳遞的數(shù)據(jù)Bean——Person類,是一個(gè)序列化的類,這里使用Parcelable 接口來(lái)序列化,是Android提供的一個(gè)比Serializable 效率更高的序列化類。
? ? Parcelable需要實(shí)現(xiàn)三個(gè)函數(shù):
? ? 1) void writeToParcel(Parcel dest, int flags) 將需要序列化存儲(chǔ)的數(shù)據(jù)寫(xiě)入外部提供的Parcel對(duì)象dest。而看了網(wǎng)上的代碼例子,個(gè)人猜測(cè),讀取Parcel數(shù)據(jù)的次序要和這里的write次序一致,否則可能會(huì)讀錯(cuò)數(shù)據(jù)。具體情況我沒(méi)試驗(yàn)過(guò)!
? ? 2) describeContents() 沒(méi)搞懂有什么用,反正直接返回0也可以
? ? 3) static final Parcelable.Creator對(duì)象CREATOR ?這個(gè)CREATOR命名是固定的,而它對(duì)應(yīng)的接口有兩個(gè)方法:
? ? createFromParcel(Parcel source) 實(shí)現(xiàn)從source創(chuàng)建出JavaBean實(shí)例的功能
? ? newArray(int size) 創(chuàng)建一個(gè)類型為T(mén),長(zhǎng)度為size的數(shù)組,僅一句話(return new T[size])即可。估計(jì)本方法是供外部類反序列化本類數(shù)組使用。
仔細(xì)觀察Person類的代碼和上面所說(shuō)的內(nèi)容:
public class Person implements Parcelable {?
? ? private String name;?
? ? private String telNumber;?
? ? private int age;?
? ??
? ? public Person() {}?
? ? public Person(Parcel pl){?
? ? ? ? name = pl.readString();?
? ? ? ? telNumber = pl.readString();?
? ? ? ? age = pl.readInt();?
? ? }?
? ??
? ? public String getName() {?
? ? ? ? return name;?
? ? }?
? ? public void setName(String name) {?
? ? ? ? this.name = name;?
? ? }?
? ? public String getTelNumber() {?
? ? ? ? return telNumber;?
? ? }?
? ? public void setTelNumber(String telNumber) {?
? ? ? ? this.telNumber = telNumber;?
? ? }?
? ? public int getAge() {?
? ? ? ? return age;?
? ? }?
? ? public void setAge(int age) {?
?
? ? }?
? ? @Override?
? ? public int describeContents() {?
? ? ? ? return 0;?
? ? }?
? ? @Override?
? ? public void writeToParcel(Parcel dest, int flags) {?
? ? ? ? dest.writeString(name);?
? ? ? ? dest.writeString(telNumber);?
? ? ? ? dest.writeInt(age);?
? ? }?
? ??
? ? public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {?
? ??
? ? ? ? @Override?
? ? ? ? public Person createFromParcel(Parcel source) {?
? ? ? ? ? ? return new Person(source);?
? ? ? ? }?
? ? ? ? @Override?
? ? ? ? public Person[] newArray(int size) {?
? ? ? ? ? ? return new Person[size];?
? ? ? ? }?
? ? };?
}
然后創(chuàng)建Person.aidl文件,注意這里的parcelable和原來(lái)實(shí)現(xiàn)的Parcelable 接口,開(kāi)頭的字母p一個(gè)小寫(xiě)一個(gè)大寫(xiě):
package com.demo;?
parcelable Person;
?
? ? 對(duì)于實(shí)現(xiàn)AIDL接口,官方還提醒我們:
? ? 1. 調(diào)用者是不能保證在主線程執(zhí)行的,所以從一調(diào)用的開(kāi)始就需要考慮多線程處理,以及確保線程安全;
? ? 2. IPC調(diào)用是同步的。如果你知道一個(gè)IPC服務(wù)需要超過(guò)幾毫秒的時(shí)間才能完成地話,你應(yīng)該避免在Activity的主線程中調(diào)用。也就是IPC調(diào)用會(huì)掛起應(yīng)用程序?qū)е陆缑媸ロ憫?yīng),這種情況應(yīng)該考慮單獨(dú)開(kāi)啟一個(gè)線程來(lái)處理。
? ? 3. 拋出的異常是不能返回給調(diào)用者(跨進(jìn)程拋異常處理是不可取的)。
?
3. 客戶端獲取接口
? ? 客戶端如何獲取AIDL接口呢?通過(guò)IMyService.Stub.asInterface(service)來(lái)得到IMyService對(duì)象:
private IMyService mRemoteService;?
private ServiceConnection mRemoteConnection = new ServiceConnection() { ? ?
? ? public void onServiceConnected(ComponentName className, IBinder service) { ? ?
? ? ? ? mRemoteService = IMyService.Stub.asInterface(service); ? ?
? ? } ? ?
? ? public void onServiceDisconnected(ComponentName className) { ? ?
? ? ? ? mRemoteService = null; ? ?
? ? } ? ?
};
? ? 在生成的IMyService.java里面會(huì)找到這樣的代碼:
public static com.demo.IMyService asInterface(android.os.IBinder obj) {...}
?
而service的綁定沒(méi)有什么不同:
if (mIsRemoteBound) {?
? ? unbindService(mRemoteConnection);?
}else{?
? ? bindService(new Intent("com.demo.IMyService"),mRemoteConnection, Context.BIND_AUTO_CREATE);?
}
mIsRemoteBound = !mIsRemoteBound;
通過(guò)IPC調(diào)用/傳遞數(shù)據(jù)
? ? 客戶端綁定service后就能通過(guò)IPC來(lái)調(diào)用/傳遞數(shù)據(jù)了,直接調(diào)用service對(duì)象的接口方法:
addPersonButton.setOnClickListener(?
? ? new View.OnClickListener(){?
? ? ? ? private int index = 0;?
? ?
? ? ? ? @Override?
? ? ? ? public void onClick(View view) {?
? ? ? ? ? ? Person person = new Person();?
? ? ? ? ? ? index = index + 1;?
? ? ? ? ? ? person.setName("Person" + index);?
? ? ? ? ? ? person.setAge(20);?
? ? ? ? ? ? person.setTelNumber("123456");?
? ? ? ? ? ? try {?
? ? ? ? ? ? ? ? mRemoteService.savePersonInfo(person);?
? ? ? ? ? ? } catch (RemoteException e) {?
? ? ? ? ? ? ? ? e.printStackTrace();?
? ? ? ? ? ? }?
? ? ? ? }?
? ? });?
listPersonButton.setOnClickListener(?
? ? new View.OnClickListener(){?
? ?@Override?
? ? ? ? public void onClick(View view) {?
? ? ? ? ? ? List<Person> list = null;?
? ? ? ? ? ? try {?
? ? ? ? ? ? ? ? list = mRemoteService.getAllPerson();?
? ? ? ? ? ? } catch (RemoteException e) {?
? ? ? ? ? ? ? ? e.printStackTrace();?
? ? ? ? ? ? }?
? ? ? ? ? ? if (list != null){?
? ? ? ? ? ? ? ? StringBuilder text = new StringBuilder();?
? ? ? ? ? ? ? ? for(Person person : list){?
? ? ? ? ? ? ? ? text.append("\nPerson name:");?
? ? ? ? ? ? ? ? text.append(person.getName());?
? ? ? ? ? ? ? ? text.append("\n age :");?
? ? ? ? ? ? ? ? text.append(person.getAge());?
? ? ? ? ? ? ? ? text.append("\n tel number:");?
? ? ? ? ? ? ? ? text.append(person.getTelNumber());?
? ? ? ? ? ? }?
? ? ? ? ? ? inputPersonEdit.setText(text);?
? ? ? ? }else {?
? ? ? ? ? ? Toast.makeText(ServiceActivity.this, "get data error",?
? ? ? ? ? ? Toast.LENGTH_SHORT).show();?
? ? ? ? }?
? ? }?
});
Permission權(quán)限
? ? 如果Service在AndroidManifest.xml中聲明了全局的強(qiáng)制的訪問(wèn)權(quán)限,其他引用必須聲明權(quán)限才能來(lái)start,stop或bind這個(gè)service.
? ? 另外,service可以通過(guò)權(quán)限來(lái)保護(hù)她的IPC方法調(diào)用,通過(guò)調(diào)用checkCallingPermission(String)方法來(lái)確保可以執(zhí)行這個(gè)操作。
AndroidManifest.xml的Service元素
<service android:name=".RemoteService" android:process=":remote">?
? ? <intent-filter>?
? ? ? ? <action android:name="com.demo.IMyService" />?
? ? </intent-filter>?
</service>
? ? 這里的android:process=":remote",一開(kāi)始我沒(méi)有添加的,在同一個(gè)程序里使用IPC,即同一個(gè)程序作為客戶端/服務(wù)器端,結(jié)果運(yùn)行mRemoteService = IMyService.Stub.asInterface(service);時(shí)提示空指針異常。觀察了人家的在不同程序里進(jìn)行IPC的代碼,也是沒(méi)有這個(gè)android:process=":remote"的。
? ? 也就是說(shuō)android:process=":remote",代表在應(yīng)用程序里,當(dāng)需要該service時(shí),會(huì)自動(dòng)創(chuàng)建新的進(jìn)程。而如果是android:process="remote",沒(méi)有“:”分號(hào)的,則創(chuàng)建全局進(jìn)程,不同的應(yīng)用程序共享該進(jìn)程。
?
?
?
?
?
?
?
?
?
?
?
?
假設(shè)A應(yīng)用需要與B應(yīng)用進(jìn)行通信,調(diào)用B應(yīng)用中的download(String path)方法,B應(yīng)用以Service方式向A應(yīng)用提供服務(wù)。需要下面四個(gè)步驟:?
1> 在B應(yīng)用中創(chuàng)建*.aidl文件,aidl文件的定義和接口的定義很相類,如:在cn.itcast.aidl包下創(chuàng)建IDownloadService.aidl文件,內(nèi)容如下:
package cn.itcast.aidl;
interface IDownloadService {
? ? void download(String path);
}
當(dāng)完成aidl文件創(chuàng)建后,eclipse會(huì)自動(dòng)在項(xiàng)目的gen目錄中同步生成IDownloadService.java接口文件。接口文件中生成一個(gè)Stub的抽象類,里面包括aidl定義的方法,還包括一些其它輔助方法。值得關(guān)注的是asInterface(IBinder iBinder),它返回接口類型的實(shí)例,對(duì)于遠(yuǎn)程服務(wù)調(diào)用,遠(yuǎn)程服務(wù)返回給客戶端的對(duì)象為代理對(duì)象,客戶端在onServiceConnected(ComponentName name, IBinder service)方法引用該對(duì)象時(shí)不能直接強(qiáng)轉(zhuǎn)成接口類型的實(shí)例,而應(yīng)該使用asInterface(IBinder iBinder)進(jìn)行類型轉(zhuǎn)換。
編寫(xiě)Aidl文件時(shí),需要注意下面幾點(diǎn):?
? ? 1.接口名和aidl文件名相同。
? ? 2.接口和方法前不用加訪問(wèn)權(quán)限修飾符public,private,protected等,也不能用final,static。
? ? 3.Aidl默認(rèn)支持的類型包話java基本類型(int、long、boolean等)和(String、List、Map、CharSequence),使用這些類型時(shí)不需要import聲明。對(duì)于List和Map中的元素類型必須是Aidl支持的類型。如果使用自定義類型作為參數(shù)或返回值,自定義類型必須實(shí)現(xiàn)Parcelable接口。
? ? 4.自定義類型和AIDL生成的其它接口類型在aidl描述文件中,應(yīng)該顯式import,即便在該類和定義的包在同一個(gè)包中。
? ? 5.在aidl文件中所有非Java基本類型參數(shù)必須加上in、out、inout標(biāo)記,以指明參數(shù)是輸入?yún)?shù)、輸出參數(shù)還是輸入輸出參數(shù)。
? ? 6.Java原始類型默認(rèn)的標(biāo)記為in,不能為其它標(biāo)記。
2> 在B應(yīng)用中實(shí)現(xiàn)aidl文件生成的接口(本例是IDownloadService),但并非直接實(shí)現(xiàn)接口,而是通過(guò)繼承接口的Stub來(lái)實(shí)現(xiàn)(Stub抽象類內(nèi)部實(shí)現(xiàn)了aidl接口),并且實(shí)現(xiàn)接口方法的代碼。內(nèi)容如下:
public class ServiceBinder extends IDownloadService.Stub {
? ? @Override
? ? public void download(String path) throws RemoteException {
? ? ? ? Log.i("DownloadService", path);
? ? }
}
3> 在B應(yīng)用中創(chuàng)建一個(gè)Service(服務(wù)),在服務(wù)的onBind(Intent intent)方法中返回實(shí)現(xiàn)了aidl接口的對(duì)象(本例是ServiceBinder)。內(nèi)容如下:
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);
? ? ? ? }
? ? }
}
其他應(yīng)用可以通過(guò)隱式意圖訪問(wèn)服務(wù),意圖的動(dòng)作可以自定義,AndroidManifest.xml配置代碼如下:
<service android:name=".DownloadService" >
? ? <intent-filter>
? ? ? ? <action android:name="cn.itcast.process.aidl.DownloadService" />
? ? </intent-filter>
</service>
4> 把B應(yīng)用中aidl文件所在package連同aidl文件一起拷貝到客戶端A應(yīng)用,eclipse會(huì)自動(dòng)在A應(yīng)用的gen目錄中為aidl文件同步生成IDownloadService.java接口文件,接下來(lái)就可以在A應(yīng)用中實(shí)現(xiàn)與B應(yīng)用通信,代碼如下:
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);//綁定到服務(wù)
? ? }
? ??
? ? @Override
? ? protected void onDestroy() {
? ? ? ? super.onDestroy();
? ? ? ? this.unbindService(serviceConnection);//解除服務(wù)
? ? }
? ??
? ? 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;
? ? ? ? }
? ? };
}
轉(zhuǎn)載于:https://blog.51cto.com/yym631/1744740
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
- 上一篇: 小新潮7000的缺陷
- 下一篇: 迷你世界团子怎么驯服(24期迷你世界一)