Android?創(chuàng)建AIDL文件使用教程
前言
AIDL(Android Interface Definition Language)是一種 IDL 語(yǔ)言,用于生成可以在 Android 設(shè)備上兩個(gè)進(jìn)程之間進(jìn)行進(jìn)程間通信(IPC)的代碼。 通過(guò) AIDL,可以在一個(gè)進(jìn)程中獲取另一個(gè)進(jìn)程的數(shù)據(jù)和調(diào)用其暴露出來(lái)的方法,從而滿足進(jìn)程間通信的需求。通常,暴露方法給其他應(yīng)用進(jìn)行調(diào)用的應(yīng)用稱為服務(wù)端,調(diào)用其他應(yīng)用的方法的應(yīng)用稱為客戶端,客戶端通過(guò)綁定服務(wù)端的 Service 來(lái)進(jìn)行交互。
官方文檔中對(duì) AIDL 有這樣一段介紹:
Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.
第一句很重要,“只有當(dāng)你允許來(lái)自不同的客戶端訪問(wèn)你的服務(wù)并且需要處理多線程問(wèn)題時(shí)你才必須使用AIDL”,其他情況下你都可以選擇其他方法,如使用 Messenger,也能跨進(jìn)程通信??梢?AIDL 是處理多線程、多客戶端并發(fā)訪問(wèn)的,而 Messenger 是單線程處理。 下面介紹 AIDL 的使用方法。
1 創(chuàng)建 AIDL 文件
AIDL 文件可以分為兩類。一類用來(lái)聲明實(shí)現(xiàn)了 Parcelable 接口的數(shù)據(jù)類型,以供其他 AIDL 文件使用那些非默認(rèn)支持的數(shù)據(jù)類型。還有一類是用來(lái)定義接口方法,聲明要暴露哪些接口給客戶端調(diào)用。在 AIDL 文件中需要明確標(biāo)明引用到的數(shù)據(jù)類型所在的包名,即使兩個(gè)文件處在同個(gè)包名下。
默認(rèn)情況下,AIDL 支持下列數(shù)據(jù)類型:
- 八種基本數(shù)據(jù)類型:byte、char、short、int、long、float、double、boolean
- String,CharSequence
- List類型。List承載的數(shù)據(jù)必須是AIDL支持的類型,或者是其它聲明的AIDL對(duì)象
- Map類型。Map承載的數(shù)據(jù)必須是AIDL支持的類型,或者是其它聲明的AIDL對(duì)象
客戶端和服務(wù)端都需要?jiǎng)?chuàng)建,我們先在服務(wù)端中創(chuàng)建,然后復(fù)制到客戶端即可。在 Android Studio 中右鍵點(diǎn)擊新建一個(gè) AIDL 文件,
如圖所示:
創(chuàng)建完成后,系統(tǒng)就會(huì)默認(rèn)創(chuàng)建一個(gè) aidl 文件夾,文件夾下的目錄結(jié)構(gòu)即是工程的包名,AIDL 文件就在其中。
如圖所示:
文件中會(huì)有一個(gè)默認(rèn)方法,可以刪除掉,也可以新增其他方法。
2 實(shí)現(xiàn)接口
創(chuàng)建或修改過(guò) AIDL 文件后需要 build 下工程,Android SDK 工具會(huì)生成以 .aidl 文件命名的 .java 接口文件(例如,IRemoteService.aidl 生成的文件名是 IRemoteService.java),在進(jìn)程間通信中真正起作用的就是該文件。生成的接口包含一個(gè)名為 Stub 的子類(例如,IRemoteService.Stub),該子類是其父接口的抽象實(shí)現(xiàn),并且會(huì)聲明 AIDL 文件中的所有方法。 如要實(shí)現(xiàn) AIDL 生成的接口,請(qǐng)實(shí)例化生成的 Binder 子類(例如,IRemoteService.Stub),并實(shí)現(xiàn)繼承自 AIDL 文件的方法。
以下是使用匿名內(nèi)部類實(shí)現(xiàn) IRemoteService 接口的示例:
private final IRemoteService.Stub binder = new IRemoteService.Stub() { public int getPid(){ return Process.myPid(); } public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) { // Does nothing } };
現(xiàn)在,binder 是 Stub 類的一個(gè)實(shí)例(一個(gè) Binder),其定義了服務(wù)端的 RPC 接口。
3 服務(wù)端公開接口
在為服務(wù)端實(shí)現(xiàn)接口后,需要向客戶端公開該接口,以便客戶端進(jìn)行綁定。創(chuàng)建 Service 并實(shí)現(xiàn) onBind(),從而返回生成的 Stub 的類實(shí)例。
以下是服務(wù)端的示例代碼:
public class RemoteService extends Service { private final String TAG = "RemoteService"; @Override public void onCreate() { super.onCreate(); } @Override public IBinder onBind(Intent intent) { // Return the interface Log.d(TAG, "onBind"); return binder; } private final IRemoteService.Stub binder = new IRemoteService.Stub() { public int getPid() { return Process.myPid(); } public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) { Log.d(TAG, "basicTypes anInt:" + anInt + ";aLong:" + aLong + ";aBoolean:" + aBoolean + ";aFloat:" + aFloat + ";aDouble:" + aDouble + ";aString:" + aString); } }; }
我們還需要在 Manefest 文件中注冊(cè)我們創(chuàng)建的這個(gè) Service,否則客戶端無(wú)法綁定服務(wù)。
<service android:name=".RemoteService" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.example.aidl"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </service>
4 客戶端調(diào)用 IPC 方法
當(dāng)客戶端(如 Activity)調(diào)用 bindService() 以連接此服務(wù)時(shí),客戶端的 onServiceConnected() 回調(diào)會(huì)接收服務(wù)端的 onBind() 方法所返回的 binder 實(shí)例。
客戶端還必須擁有接口類的訪問(wèn)權(quán)限,因此如果客戶端和服務(wù)端在不同應(yīng)用內(nèi),則客戶端應(yīng)用的 src/ 目錄內(nèi)必須包含 .aidl 文件(該文件會(huì)生成 android.os.Binder 接口,進(jìn)而為客戶端提供 AIDL 方法的訪問(wèn)權(quán)限)的副本。所以我們需要把服務(wù)端的 aidl 文件夾整個(gè)復(fù)制到客戶端的 java 文件夾同個(gè)層級(jí)下,不需要改動(dòng)任何代碼。
當(dāng)客戶端在 onServiceConnected() 回調(diào)中收到 IBinder 時(shí),它必須調(diào)用 IRemoteService.Stub.asInterface(service),以將返回的參數(shù)轉(zhuǎn)換成 IRemoteService 類型。例如:
IRemoteService iRemoteService; private ServiceConnection mConnection = new ServiceConnection() { // Called when the connection with the service is established public void onServiceConnected(ComponentName className, IBinder service) { // Following the example above for an AIDL interface, // this gets an instance of the IRemoteInterface, which we can use to call on the service iRemoteService = IRemoteService.Stub.asInterface(service); } // Called when the connection with the service disconnects unexpectedly public void onServiceDisconnected(ComponentName className) { Log.e(TAG, "Service has unexpectedly disconnected"); iRemoteService = null; } };
獲得了 iRemoteService 對(duì)象,我們就可以調(diào)用 AIDL 中定義的方法了。如要斷開連接,可以調(diào)用unbindService() 方法。
以下是客戶端的示例代碼:
public class MainActivity extends AppCompatActivity { private final String TAG = "ClientActivity"; private IRemoteService iRemoteService; private Button mBindServiceButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBindServiceButton = findViewById(R.id.btn_bind_service); mBindServiceButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String text = mBindServiceButton.getText().toString(); if ("Bind Service".equals(text)) { Intent intent = new Intent(); intent.setAction("com.example.aidl"); intent.setPackage("com.example.aidl.server"); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } else { unbindService(mConnection); mBindServiceButton.setText("Bind Service"); } } }); } ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { Log.d(TAG, "onServiceDisconnected"); iRemoteService = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d(TAG, "onServiceConnected"); iRemoteService = IRemoteService.Stub.asInterface(service); try { int pid = iRemoteService.getPid(); int currentPid = Process.myPid(); Log.d(TAG, "currentPID: " + currentPid + ", remotePID: " + pid); iRemoteService.basicTypes(12, 123, true, 123.4f, 123.45, "服務(wù)端你好,我是客戶端"); } catch (RemoteException e) { e.printStackTrace(); } mBindServiceButton.setText("Unbind Service"); } }; }
5 通過(guò) IPC 傳遞對(duì)象
除了上面默認(rèn)支持的數(shù)據(jù)類型,AIDL 還可以傳遞對(duì)象,但是該類必須實(shí)現(xiàn) Parcelable 接口。而該類是兩個(gè)應(yīng)用間都需要使用到的,所以也需要在 AIDL 文件中聲明該類,為了避免出現(xiàn)類名重復(fù)導(dǎo)致無(wú)法創(chuàng)建 AIDL 文件的錯(cuò)誤,這里需要先創(chuàng)建 AIDL 文件,之后再創(chuàng)建類。 先在服務(wù)端新建一個(gè) AIDL 文件,比如 Rect.aidl,
示例如下:
// Rect.aidl package com.example.aidl.server; // Declare Rect so AIDL can find it and knows that it implements // the parcelable protocol. parcelable Rect;
然后就可以創(chuàng)建 Rect 類了,并使之實(shí)現(xiàn) Parcelable 接口。示例代碼如下:
public class Rect implements Parcelable { private int left; private int top; private int right; private int bottom; public Rect(int left, int top, int right, int bottom) { this.left = left; this.top = top; this.right = right; this.bottom = bottom; } public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() { public Rect createFromParcel(Parcel in) { return new Rect(in); } public Rect[] newArray(int size) { return new Rect[size]; } }; private Rect(Parcel in) { readFromParcel(in); } @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(left); out.writeInt(top); out.writeInt(right); out.writeInt(bottom); } public void readFromParcel(Parcel in) { left = in.readInt(); top = in.readInt(); right = in.readInt(); bottom = in.readInt(); } @Override public int describeContents() { return 0; } @NonNull @Override public String toString() { return "Rect[left:" + left + ",top:" + top + ",right:" + right + ",bottom:" + bottom + "]"; } }
這樣我們就可以在之前創(chuàng)建的 IRemoteService.aidl 中新增一個(gè)方法來(lái)傳遞 Rect 對(duì)象了,示例代碼如下:
// IRemoteService.aidl package com.example.aidl.server; import com.example.aidl.server.Rect; // Declare any non-default types here with import statements interface IRemoteService { /** * 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); int getPid(); void addRectInOut(inout Rect rect); }
注意這里需要明確導(dǎo)包:
import com.example.aidl.server.Rect;
然后將新增的 Rect.aidl 文件和 Rect.java 文件還有修改的 IRemoteService.aidl 文件同步到客戶端相同路徑下,
如圖所示:
build 下工程,就可以在客戶端調(diào)用到該 addRectInOut 方法了。
示例代碼如下:
ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { iRemoteService = IRemoteService.Stub.asInterface(service); try { iRemoteService.addRectInOut(new Rect(1, 2, 3, 4)); } catch (RemoteException e) { e.printStackTrace(); } } };
到此這篇關(guān)于Android 創(chuàng)建AIDL文件使用教程的文章就介紹到這了,更多相關(guān)Android AIDL 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Android App中的AsyncTask異步任務(wù)執(zhí)行方式
這篇文章主要介紹了Android App中的AsyncTask異步任務(wù)執(zhí)行方式,文中舉了一個(gè)打開網(wǎng)絡(luò)圖片的例子幫助大家直觀理解,需要的朋友可以參考下2016-04-04Android 通過(guò)代碼設(shè)置、打開wifi熱點(diǎn)及熱點(diǎn)連接的實(shí)現(xiàn)代碼
這篇文章主要介紹了Android 通過(guò)代碼設(shè)置、打開wifi熱點(diǎn)及熱點(diǎn)連接的實(shí)現(xiàn)代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2018-05-05android多線程斷點(diǎn)下載-帶進(jìn)度條和百分比進(jìn)度顯示效果
下面小編就為大家?guī)?lái)一篇android多線程斷點(diǎn)下載-帶進(jìn)度條和百分比進(jìn)度顯示效果。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06Android clipChildren屬性實(shí)例詳解
本文主要介紹Android clipChildren的屬性,這里對(duì)clipChildren屬性做了一個(gè)小例子,展示了效果圖和實(shí)例代碼,方便大家觀看理解2016-07-07Android?Studio實(shí)現(xiàn)簡(jiǎn)單計(jì)算器開發(fā)
這篇文章主要為大家詳細(xì)介紹了Android?Studio實(shí)現(xiàn)簡(jiǎn)單計(jì)算器開發(fā),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05最常見的猜拳小游戲Android代碼實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了最常見的猜拳小游戲Android代碼實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08Android中絕對(duì)音量和相對(duì)音量設(shè)置
大家好,本篇文章主要講的是Android中絕對(duì)音量和相對(duì)音量設(shè)置,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下2022-01-01Android View進(jìn)行手勢(shì)識(shí)別詳解
本文主要介紹 Android View進(jìn)行手勢(shì)識(shí)別,這里整理了相關(guān)資料和簡(jiǎn)單示例,有興趣的小伙伴可以參考下2016-08-08