Android 接口定义语言 (AIDL)

Android 接口定义语言 (AIDL)

Android 接口定义语言 (AIDL) 会将对象分解成基本数据类型,操作系统可通过识别这些基本数据类型并将其编组到各进程中来执行进程间通信 (IPC)。

新建 AIDL 文件时如果提示 Requires setting the buildFeatures.aidl to true in the build file 需要在 app 目录下的 build.gradle 文件中的 app 下添加:

buildFeatures {
aidl true
}

定义 AIDL 接口

创建.aidl 文件

AIDL 支持以下数据类型:

  • 八种基本数据类型:bytecharshortintlongfloatdoubleboolean

  • String

  • CharSequence

  • List

    List 中的所有元素必须是以上支持的数据类型或 AIDL 生成的其他接口或 Parcelable 类型,支持泛型。

  • Map

    Map 中的所有元素必须是以上支持的数据类型或 AIDL 生成的其他接口或 Parcelable 类型,不支持泛型 MAP。

tip:

  • 定义的其他数据类型,必须写 improt 语句。

  • 所有非基本数据类型参数需要指示数据走向的方向标记,可以是 inoutinout

    基本数据类型默认为 in,不能是其他方向。

.aidl 文件实例:

IRemoteService.aidl
// IRemoteService.aidl
package com.example.android;

// Declare any non-default types here with import statements.

/** Example service interface */
interface IRemoteService {
/** Request the process ID of this service. */
int getPid();

/** 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);
}

.aidl 文件保存至项目的 src/ 目录内,构建应用时 SDK 工具会在项目的 gen/ 目录中生成 IBinder 接口文件。

实现接口

生成的接口包含 Stub 子类,该子类是其父类接口的抽象实现,并且会声明.aild 文件中的所有方法。

使用匿名实例实现 IRemoteService 接口:

private val binder = object : IRemoteService.Stub() {

override fun getPid(): Int =
Process.myPid()

override fun basicTypes(
anInt: Int,
aLong: Long,
aBoolean: Boolean,
aFloat: Float,
aDouble: Double,
aString: String
) {
// Does nothing
}
}

向客户端公开接口

继承 Service 并实现 onBind(),从而实现生成的 Stub 的类的实例:

class RemoteService : Service() {

override fun onCreate() {
super.onCreate()
}

override fun onBind(intent: Intent): IBinder {
// Return the interface
return binder
}

private val binder = object : IRemoteService.Stub() {
override fun getPid(): Int {
return Process.myPid()
}

override fun basicTypes(
anInt: Int,
aLong: Long,
aBoolean: Boolean,
aFloat: Float,
aDouble: Double,
aString: String
) {
// Does nothing
}
}
}

当客户端(如 Activity)调用 bindService() 以连接此服务时,客户端的 onServiceConnected() 回调会接收服务的 onBind() 方法所返回的 binder 实例。

如果客户端和服务再不同应用内,则客户端应用的 src/ 目录内必须包含.aidl 文件。

当客户端在 onServiceConnected() 回调中受到 IBinder 时,它必须调用 YourServiceInterface.Stub.asInterface(service),以将返回的参数转换成 YourServiceInterface 类型。

var iRemoteService: IRemoteService? = null

val mConnection = object : ServiceConnection {

// Called when the connection with the service is established
override fun onServiceConnected(className: ComponentName, service: IBinder) {
// 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
override fun onServiceDisconnected(className: ComponentName) {
Log.e(TAG, "Service has unexpectedly disconnected")
iRemoteService = null
}
}

通过 IPC 传递对象

Rect.aidl 文件可创建 Parcelable 类型的 Rect 类:

package android.graphics;

// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
parcelable Rect;

Rect 类实现 Parcelable 协议:

import android.os.Parcel
import android.os.Parcelable

class Rect() : Parcelable {
var left: Int = 0
var top: Int = 0
var right: Int = 0
var bottom: Int = 0

companion object CREATOR : Parcelable.Creator<Rect> {
override fun createFromParcel(parcel: Parcel): Rect {
return Rect(parcel)
}

override fun newArray(size: Int): Array<Rect> {
return Array(size) { Rect() }
}
}

private constructor(inParcel: Parcel) : this() {
readFromParcel(inParcel)
}

override fun writeToParcel(outParcel: Parcel, flags: Int) {
outParcel.writeInt(left)
outParcel.writeInt(top)
outParcel.writeInt(right)
outParcel.writeInt(bottom)
}

private fun readFromParcel(inParcel: Parcel) {
left = inParcel.readInt()
top = inParcel.readInt()
right = inParcel.readInt()
bottom = inParcel.readInt()
}

override fun describeContents(): Int {
return 0
}
}

带 Bundle 参数(包含 Parcelable 类型)的方法

如果 AIDL 接口包含接收 Bundle 作为参数(预计包含 Parcelable 类型)的方法,则在尝试从 Bundle 读取之前,必须通过调用 Bundle.setClassLoader(ClassLoader) 设置 Bundle 的类加载器,否则会遇到 ClassNotFoundException

例如有.aidl 文件:

// IRectInsideBundle.aidl
package com.example.android;

/** Example service interface */
interface IRectInsideBundle {
/** Rect parcelable is stored in the bundle with key "rect" */
void saveRect(in Bundle bundle);
}

读取 Rect 前在 Bundle 中完成 ClassLoader 设置。

private val binder = object : IRectInsideBundle.Stub() {
override fun saveRect(bundle: Bundle) {
bundle.classLoader = classLoader
val rect = bundle.getParcelable<Rect>("rect")
process(rect) // Do more with the parcelable
}
}
可靠便捷的VPN服务 100 G 月流量,5台设备可用,一年仅需99元!
请我一杯咖啡吧!
八亩山啊 微信 微信
八亩山啊 支付宝 支付宝
八亩山啊 比特币 比特币
  • 本文作者: 八亩山啊
  • 本文链接: https://mumo.fun/posts/29/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!