前言
考虑到后面有需要到车载或者其他多程序交互的场景,因此我最近也接触了下AIDL这个东西,AIDL在写文本时也是第一次使用,所以我们首要关注实现AIDL要具备什么东西。
需要注意的是,本文可能存在一些错误解释,希望读者阅读时注意甄别,实践出真知,对内容有兴趣的话就快试试看!
什么是AIDL
Android Interface Definition Language(Android接口定义语言),利用AIDL我们可以生成一套IPC通信代码,也就是进程间通信,我们知道一个APP默认情况下是一个进程,因此宏观上说AIDL可以用于不同APP之间的通信。
AIDL是一种模板语法,类似Java的接口,定义后生成的代码就是Java的接口类,内部有实现了IPC通信,但是由于这些代码是重复的,所以安卓贴心的给我们一个模型生成这些代码。
AIDL生成的接口中存在一个Binder的示例,APP可以通过Service来使用它,其他APP绑定这个Service就可以相互通信了。
实验前提
我们模拟2个APP
- 服务端APP
- 客户端APP
- AIDL模块(供其他模块或者APP引入使用)
实操
我们只是要实现这个跨进程通信,因此功能不太重要,我们下面使用各自办法目的只是为了传递数据给通信双方。
实现AIDL模块
我们先创建一个Android项目,这个项目就作为客户端APP了(为了方便)。
接下来我们创建一个模块,对应图上就是aidl-sdk,我们需要在里边去编写AIDL的模板和Bean类。
启用AIDL
在正式开始前我们得在gradle文件里编写下面的配置,不然待会我们没办法创建AIDL文件。
buildFeatures { aidl = true }
创建AIDL文件
让我们选择AIDL-SDK模块,点击新建,找到AIDL,如果你没有配置上面说的选项,那么这里是没办法勾选的哦。
下面我起个名字叫做IMyAidlInterface ,你会发现多了一个aidl的文件夹,其中就有IMyAidlInterface.adil文件。
我们在里边这么写,就实现传递两个数字返回相加结果,怎么说?看上去好像和Java语法很像对不对?不过我发现这里没有提词,有点不太习惯。
java
// IMyAidlInterface.aidl
package com.imcys.aidl_sdk;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
int add(int a, int b);
}
接下来,我们点击Build,不Build这个AIDL文件不会变成接口类。
当我们再次检查时会发现已经生成了对应的Java文件。
这个接口文件里有一个抽象类,这个Stub的构造方法就会给我们一个Binder,后面我们就可以在服务中使用它。
实现服务端APP
我们回到最外层,创建一个新的APP项目模块。
这么做是为了我们待会方便引入写好的AIDL模块,就创建在一个大项目下面了。
不要忘记给这个新建的服务端APP模块引入前面创建的aidl-sdk,不然生成的接口这个模块看不到。
implementation(project(":aidl-sdk"))
还记得吗?我前面说这个AIDL生成的接口中有个Binder,这东西要给服务来用,所以我们直接创建一个项目奥。
下面我创建一个AIDLDemoServer 类作为Service 。
而onBind方法正好需要一个Binder ,那么我们就直接给他传递生成的Binder,让它来接管服务的通信。
这里我们继承生成接口的Stub类,实现它里边的方法,这些方法实际上就是我们刚刚在模板定义的,注意,下面的三个方法是我后来写在模板的,因此我们暂时不用关注。
我们给add进行了实现,它的功能就是相加就可以了,然后我们把Binder传递给onBind就好。
服务注册
千万别忘记注册我们的服务:
xml
<service
android:name=".AIDLDemoServer"
android:enabled="true"
android:exported="true"
tools:ignore="ForegroundServicePermission">
<intent-filter>
<action android:name="com.imcys.aidldemo.AIDLSERVICE"/>
</intent-filter>
</service>
注意我们这里加了一个action,用于绑定时过滤。
免杀扩展
让我们的服务成为前台服务,降低系统杀掉的情况,这个安卓13有一些权限调整,需要你指定前台服务的类型。
xml
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
具体看上面地址,我这里就不做这一步了。
实现客户端APP
同样的,我们给APP引入前面创建的aidl-sdk,不然生成的接口这个模块看不到。
implementation(project(":aidl-sdk"))
绑定服务
绑定服务的方法类似我们正常在APP内绑定服务,但是由于不在一个APP我们需要指定包名和这个服务的具体类。
kotlin
private fun bindAIDLServer() {
val intent = Intent()
intent.setAction("com.imcys.aidldemo.AIDLSERVICE")
intent.setComponent(
ComponentName(
"com.imcys.aidldemo.server",
"com.imcys.aidldemo.server.AIDLDemoServer"
)
)
val suc = bindService(intent, serviceConnection, BIND_AUTO_CREATE)
Log.i(TAG, "bindToService: $suc")
}
注意我们这里用了setAction,是因为服务端APP注册服务时指定了action。
这块需要传一个ServiceConnection,我们看看如何获取它。
我们看到它也返回了IBinder,很巧的是,生成的IMyAidlInterface中有一个转换的方法。
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service)
非常巧妙,我们获取到了iMyAidlInterface,现在我们调用iMyAidlInterface的方法就会和服务端APP进行通信了。
让我们运行看看,注意不要在onServiceConnected执行iMyAidlInterface,因为那是主线程,我这块只是为了演示。
运行结果
可以看到,我们成功了,返回结果是30,它将10和20加在了一起,这样我们就实现了跨进程通信。
传递Bean
这是拓展内容,有兴趣的可以看看。
创建Bean
我们在aidl-sdk模块创建bean包,在里边创建一个AIDLMessage,由于AIDL不能直接传输它,因此我们需要序列化,这里谷歌推荐的是Parcelable,这个实现大家可以在网上找一下。
创建AIDL
我们需要定义一个新的AIDL
parcelable AIDLMessage;
但是不要忘记,我们得在之前的AIDL中定义一个方法来使用它,现在假设它是返回类型。
注意,我们得导入它的包,和正常导包的写法是一样的。
服务端实现
Service的实现是简单的,我们相当于new一个AIDLMessage出去。
客户端实现
Log.i(TAG, "特别序列化${iMyAidlInterface.buildAIDLMessage(1, "备注").msg}")
我们在客户端调用一下,看看运行结果。
毫无疑问,成功了。
文末
本文内容可能并不完全正确,希望帮助到了大家,有问题可以在评论区告诉我,另外最近掘金在投年度创作者榜单,希望大家可以投我一票。