原文首發(fā)于微信公眾號:jzman-blog,歡迎關(guān)注交流!
北關(guān)網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)公司!從網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、成都響應(yīng)式網(wǎng)站建設(shè)公司等網(wǎng)站項目制作,到程序開發(fā),運營維護。創(chuàng)新互聯(lián)公司成立與2013年到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗和運維經(jīng)驗,來保證我們的工作的順利進行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)公司。
AIDL(Android 接口定義語言),可以使用它定義客戶端與服務(wù)端進程間通信(IPC)的編程接口,在 Android 中,進程之間無法共享內(nèi)存(用戶空間),不同進程之間的通信一般使用 AIDL 來處理。
主要流程就是在 .aidl 文件中定義 AIDL 接口,并將其添加到應(yīng)用工程的 src 目錄下,創(chuàng)建完成之后 rebuild,Android SDK 工具會自動生成基于該 .aidl 文件的 IBinder 接口,具體的業(yè)務(wù)對象實現(xiàn)這個接口,這個具體的業(yè)務(wù)對象也是 IBinder 對象,當綁定服務(wù)的時候會根據(jù)實際情況返回具體的通信對象(本地還是代理),最后
將客戶端綁定到該服務(wù)上,之后就可以調(diào)用 IBinder 中的方法來進行進程間通信(IPC),下面將從以下幾個方面學習 AIDL 的使用:
在 AIDL 中可以通過可帶參數(shù)以及返回值的一個或多個方法來聲明接口,參數(shù)和返回值可以是任意類型,AIDL 中支持的數(shù)據(jù)類型如下:
如果業(yè)務(wù)方法中參數(shù)或返回值類型為 List 或 Map 時:
List 中的所有元素都必須是 AIDL 支持的數(shù)據(jù)類型、其他 AIDL 生成的接口或自己聲明的可打包類型??蛇x擇將 List 用作“通用”類(例如,List<String>)。另一端實際接收的具體類始終是 ArrayList,但生成的方法使用的是 List 接口。
Map 中的所有元素都必須是 AIDL 支持的數(shù)據(jù)類型、其他 AIDL 生成的接口或您聲明的可打包類型。 不支持通用 Map(如 Map<String,Integer> 形式的 Map)。 另一端實際接收的具體類始終是 HashMap,但生成的方法使用的是 Map 接口。
當然,AIDL 也支持自定義數(shù)據(jù)類型,會在下文中介紹。
首先,在工程的 src 目錄下創(chuàng)建 .aidl 文件,具體如下圖所示:
然后,在 .aidl 文件中添加具體的業(yè)務(wù)方法,文件內(nèi)容如下:
// IPersonAidlInterface.aidl
package com.manu.aidldemo;
// Declare any non-default types here with import statements
interface IPersonAidlInterface {
//具體的業(yè)務(wù)
void setName(String name);
void setAge(int age);
String getInfo();
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
}
然后,重新 rebuild project , Android SDK 工具會在相應(yīng)的目錄生成對應(yīng)的與 .aidl 文件同名的 .java 接口文件,具體目錄如下:
上一步只是使用 AIDL 定義了基本的業(yè)務(wù)操作,rebuild 之后會生成與 .aidl 相同文件名的 .java 文件,生成的這個接口文件中有一個名稱為 Stub 的一個子類,這個子類也是其父接口的抽象實現(xiàn),主要用于生成 .aidl 文件中的所有方法,Stub 類聲明具體如下:
// Stub
public static abstract class Stub extends android.os.Binder implements com.manu.aidldemo.IPersonAidlInterface
顯然,Stub 實現(xiàn)了本地接口且繼承了 Binder 對象,介于 Binder 對象在系統(tǒng)底層的支持下,Stub 對象就具有了遠程傳輸數(shù)據(jù)的能力,在生成 Stub 對象的時候會調(diào)用 asInterface 方法,具體如下:
// asInterface
public static com.manu.aidldemo.IPersonAidlInterface asInterface(android.os.IBinder obj){
if ((obj==null)) {
return null;
}
// 檢索 Binder 對象是否是本地接口的實現(xiàn)
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.manu.aidldemo.IPersonAidlInterface))) {
return ((com.manu.aidldemo.IPersonAidlInterface)iin);
}
return new com.manu.aidldemo.IPersonAidlInterface.Stub.Proxy(obj);
}
asInterface 這個方法在 Stub 創(chuàng)建的時候會調(diào)用,主要功能就是檢索 Binder 對象是否是本地接口的實現(xiàn),根據(jù) queryLocalInterface() 方法返回值判斷是否使用代理對象,這個檢索過程應(yīng)該由系統(tǒng)底層支持,如果返回為 null,則創(chuàng)建 Stub 的代理對象,反之使用本地對象來傳輸數(shù)據(jù),下面看一下 Binder 為什么具有遠程通信的能力,因為 Stub 繼承了 Binder 類,Binder 類具體如下:
// Binder
public class Binder implements IBinder {
//...
}
下面是官網(wǎng)對 IBinder 接口的描述:
遠程對象的基礎(chǔ)接口,輕量級遠程過程調(diào)用機制的核心部分,專為執(zhí)行進程內(nèi)和跨進程調(diào)用時的高性能而設(shè)計。該接口描述了與可遠程對象交互的抽象協(xié)議。不要直接實現(xiàn)這個接口,而是從Binder擴展。
這里我們知道 Binder 實現(xiàn)了 IBinder 接口,也就是說 Binder 具備了遠程通信的能力,當不同進程之間(遠程)之間通信時,顯然使用的是 Stub 的代理對象,這個代理類里面具體的業(yè)務(wù)處理邏輯,如下面這個方法,具體如下:
//具體的業(yè)務(wù)
@Override
public void setName(java.lang.String name) throws android.os.RemoteException{
// 將數(shù)據(jù)序列化
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
// 這個方法會最終調(diào)用 onTransact 方法
mRemote.transact(Stub.TRANSACTION_setName, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
這里主要就是將數(shù)據(jù)序列化,然后在系統(tǒng)跨進程支持下最終調(diào)用 onTransact() 方法,下面是 onTransact() 方法,具體如下:
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{
switch (code){
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_setName:
{
//...
// 最終調(diào)用了 Stub 里面的業(yè)務(wù)方法
this.setName(_arg0);
//...
}
}
}
顯然,這個方法在當系統(tǒng)回調(diào)給開發(fā)者的時候,傳遞回來的 code 是一個常量,在跨進程時,每個具體的服務(wù)(方法)都會對應(yīng)一個編號,然后根據(jù)這個編號來執(zhí)行相應(yīng)的服務(wù)(業(yè)務(wù)),這里說到了最后要執(zhí)行的具體業(yè)務(wù),那么這個業(yè)務(wù)要體現(xiàn)在什么地方呢,從上面可知 Stub 是一個抽象類,那么它所提供的具體業(yè)務(wù)必然需要一個具體的實現(xiàn)類來完成,下面實現(xiàn)這個具體的業(yè)務(wù)類,具體如下:
/**
* Created by jzman
* Powered by 2018/3/8 0008.
*/
public class IPersonImpl extends IPersonAidlInterface.Stub {
private String name;
private int age;
@Override
public void setName(String name) throws RemoteException {
this.name = name;
}
@Override
public void setAge(int age) throws RemoteException {
this.age = age;
}
@Override
public String getInfo() throws RemoteException {
return "My name is "+name+", age is "+age+"!";
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
}
這個類就是對外提供的具體業(yè)務(wù)類,同時其實例也是一個 Binder 對象。
創(chuàng)建一個 Service 以便對外提供具體的業(yè)務(wù),具體如下:
// Service
public class PersonService extends Service {
public PersonService() {
}
@Override
public IBinder onBind(Intent intent) {
return new IPersonImpl();
}
}
當外部調(diào)用 bindService() 方法綁定服務(wù)時,就會調(diào)用 onBind() 方法返回 IBinder 對象,這個 IBinder 對象也是具體的業(yè)務(wù)對象,如這里的 onBind() 方法返回的也是具體的業(yè)務(wù)對象,兩者是統(tǒng)一的。此外,創(chuàng)建的 Service 要在 AndroidManifest.xml 文件中聲明,具體如下:
<service
android:name=".PersonService"
android:enabled="true"
android:exported="true"
android:process=":remote">
</service>
其中使用 process 關(guān)鍵字表示為該服務(wù)開啟一個獨立的進程,remote 可任意,表示進程名稱,":"將會在主進程(進程名為包名)添加新名稱作為新進程的名稱,如 com.manu.study 將會變成 com.manu.study:remote。
通過上面幾步完成了服務(wù)的搭建,并將服務(wù)運行在獨立進程中,下面主要就是客戶端的具體調(diào)用了,具體實現(xiàn)參考如下:
// Client
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private IPersonAidlInterface iPersonAidlInterface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void bindServiceClick(View view) {
Log.i(TAG,"綁定服務(wù)...");
Intent intent = new Intent(this,PersonService.class);
// 綁定服務(wù)時自動創(chuàng)建服務(wù)
bindService(intent,conn, Context.BIND_AUTO_CREATE);
}
public void unbindServiceClick(View view) {
Log.i(TAG,"解綁服務(wù)...");
unbindService(conn);
}
public void callRemoteClick(View view) {
Log.i(TAG,"遠程調(diào)用具體服務(wù)...");
try {
iPersonAidlInterface.setName("Tom");
iPersonAidlInterface.setAge(10);
String info = iPersonAidlInterface.getInfo();
System.out.println("這是遠程調(diào)用的服務(wù)信息:"+info);
} catch (RemoteException e) {
e.printStackTrace();
}
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 根據(jù)實際情況返回 IBinder 的本地對象或其代理對象
iPersonAidlInterface = IPersonAidlInterface.Stub.asInterface(service);
System.out.println("具體的業(yè)務(wù)對象:"+iPersonAidlInterface);
}
@Override
public void onServiceDisconnected(ComponentName name) {
// Service 意外中斷時調(diào)用
}
};
}
上述代碼是客戶端調(diào)用具體服務(wù)的過程。
通過前面幾步,服務(wù)端與客戶端已經(jīng)完成,下面來驗證能否調(diào)用具體的業(yè)務(wù),這里分兩種情況:
創(chuàng)建 Service 的時候不要在 AndroidManifest.xml 文件中不要使用 process 開啟獨立進程即可,此時服務(wù)進程默認與客戶端屬于統(tǒng)一進程,結(jié)果如下:
創(chuàng)建 Service 的時候在 AndroidManifest.xml 文件中使用 process 開啟獨立進程即可,這個在上文中提到過,此時,服務(wù)進程與客戶端進程位于不同進程,結(jié)果如下:
顯然,如果服務(wù)與客戶端處于不同進程,也就是常常說的進程間通信,具體是由 IBinder 對象的代理對象完成,反之,使用本地對象,也就是本地的 IBinder 對象,具體就是實現(xiàn) AIDL 的業(yè)務(wù)類所生成的對象來完成。下篇文章將介紹如何在 AIDL 中使用自定義類型。
網(wǎng)站欄目:Android進階之AIDL的使用詳解
分享鏈接:http://www.rwnh.cn/article32/ggdosc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站排名、電子商務(wù)、品牌網(wǎng)站設(shè)計、全網(wǎng)營銷推廣、自適應(yīng)網(wǎng)站、定制開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)