網站首頁 編程語言 正文
前言
AIDL(Android Interface Definition Language)是一種 IDL 語言,用于生成可以在 Android 設備上兩個進程之間進行進程間通信(IPC)的代碼。?通過 AIDL,可以在一個進程中獲取另一個進程的數據和調用其暴露出來的方法,從而滿足進程間通信的需求。通常,暴露方法給其他應用進行調用的應用稱為服務端,調用其他應用的方法的應用稱為客戶端,客戶端通過綁定服務端的 Service 來進行交互。
官方文檔中對 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.
第一句很重要,“只有當你允許來自不同的客戶端訪問你的服務并且需要處理多線程問題時你才必須使用AIDL”,其他情況下你都可以選擇其他方法,如使用 Messenger,也能跨進程通信。可見 AIDL 是處理多線程、多客戶端并發訪問的,而 Messenger 是單線程處理。 下面介紹 AIDL 的使用方法。
1 創建 AIDL 文件
AIDL 文件可以分為兩類。一類用來聲明實現了 Parcelable 接口的數據類型,以供其他 AIDL 文件使用那些非默認支持的數據類型。還有一類是用來定義接口方法,聲明要暴露哪些接口給客戶端調用。在 AIDL 文件中需要明確標明引用到的數據類型所在的包名,即使兩個文件處在同個包名下。
默認情況下,AIDL 支持下列數據類型:
- 八種基本數據類型:byte、char、short、int、long、float、double、boolean
- String,CharSequence
- List類型。List承載的數據必須是AIDL支持的類型,或者是其它聲明的AIDL對象
- Map類型。Map承載的數據必須是AIDL支持的類型,或者是其它聲明的AIDL對象
客戶端和服務端都需要創建,我們先在服務端中創建,然后復制到客戶端即可。在 Android Studio 中右鍵點擊新建一個 AIDL 文件,
如圖所示:
創建完成后,系統就會默認創建一個 aidl 文件夾,文件夾下的目錄結構即是工程的包名,AIDL 文件就在其中。
如圖所示:
文件中會有一個默認方法,可以刪除掉,也可以新增其他方法。
2 實現接口
創建或修改過 AIDL 文件后需要 build 下工程,Android SDK 工具會生成以 .aidl 文件命名的 .java 接口文件(例如,IRemoteService.aidl 生成的文件名是 IRemoteService.java),在進程間通信中真正起作用的就是該文件。生成的接口包含一個名為 Stub 的子類(例如,IRemoteService.Stub),該子類是其父接口的抽象實現,并且會聲明 AIDL 文件中的所有方法。 如要實現 AIDL 生成的接口,請實例化生成的 Binder 子類(例如,IRemoteService.Stub),并實現繼承自 AIDL 文件的方法。
以下是使用匿名內部類實現 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
}
};
現在,binder 是 Stub 類的一個實例(一個 Binder),其定義了服務端的 RPC 接口。
3 服務端公開接口
在為服務端實現接口后,需要向客戶端公開該接口,以便客戶端進行綁定。創建 Service 并實現 onBind(),從而返回生成的 Stub 的類實例。
以下是服務端的示例代碼:
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 文件中注冊我們創建的這個 Service,否則客戶端無法綁定服務。
<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 客戶端調用 IPC 方法
當客戶端(如 Activity)調用 bindService() 以連接此服務時,客戶端的 onServiceConnected() 回調會接收服務端的 onBind() 方法所返回的 binder 實例。
客戶端還必須擁有接口類的訪問權限,因此如果客戶端和服務端在不同應用內,則客戶端應用的 src/ 目錄內必須包含 .aidl 文件(該文件會生成 android.os.Binder 接口,進而為客戶端提供 AIDL 方法的訪問權限)的副本。所以我們需要把服務端的 aidl 文件夾整個復制到客戶端的 java 文件夾同個層級下,不需要改動任何代碼。
當客戶端在 onServiceConnected() 回調中收到 IBinder 時,它必須調用 IRemoteService.Stub.asInterface(service),以將返回的參數轉換成 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 對象,我們就可以調用 AIDL 中定義的方法了。如要斷開連接,可以調用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,
"服務端你好,我是客戶端");
} catch (RemoteException e) {
e.printStackTrace();
}
mBindServiceButton.setText("Unbind Service");
}
};
}
5 通過 IPC 傳遞對象
除了上面默認支持的數據類型,AIDL 還可以傳遞對象,但是該類必須實現 Parcelable 接口。而該類是兩個應用間都需要使用到的,所以也需要在 AIDL 文件中聲明該類,為了避免出現類名重復導致無法創建 AIDL 文件的錯誤,這里需要先創建 AIDL 文件,之后再創建類。 先在服務端新建一個 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;
然后就可以創建 Rect 類了,并使之實現 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 + "]";
}
}
這樣我們就可以在之前創建的 IRemoteService.aidl 中新增一個方法來傳遞 Rect 對象了,示例代碼如下:
// 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);
}
注意這里需要明確導包:
import com.example.aidl.server.Rect;
然后將新增的 Rect.aidl 文件和 Rect.java 文件還有修改的 IRemoteService.aidl 文件同步到客戶端相同路徑下,
如圖所示:
build 下工程,就可以在客戶端調用到該 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();
}
}
};
原文鏈接:https://juejin.cn/post/7125316537749241864
相關推薦
- 2022-08-14 Android實現顯示和隱藏密碼功能的示例代碼_Android
- 2022-11-30 C++之值傳遞&指針傳遞&引用傳遞的示例詳解_C 語言
- 2022-07-24 基于python實現雙向鏈表_python
- 2022-12-29 Python?PyQt5實現拖拽與剪貼板功能詳解_python
- 2022-07-12 linux中的火墻策略優化
- 2022-09-23 基于React路由跳轉的幾種方式_React
- 2022-05-08 python如何生成密碼字典_python
- 2022-06-02 關于Python使用turtle庫畫任意圖的問題_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支