一文搞懂Android AIDL
AIDL(Android Interface Definition Language)用于在Android应用中实现进程间通信(IPC)。它允许不同应用或服务间的数据传递和方法调用,确保不同进程间能够相互访问和操作。通过AIDL,开发者可以定义一个接口,然后在服务端和客户端实现这个接口,实现跨进程的交互。背景上,AIDL主要解决了Android系统中的进程隔离问题,使得不同应用或进程能够安全有效地
介绍
AIDL
(Android Interface Definition Language)用于在Android应用中实现进程间通信(IPC)。它允许不同应用或服务间的数据传递和方法调用,确保不同进程间能够相互访问和操作。通过AIDL,开发者可以定义一个接口,然后在服务端和客户端实现这个接口,实现跨进程的交互。背景上,AIDL主要解决了Android系统中的进程隔离问题,使得不同应用或进程能够安全有效地共享数据和功能。
示例
话不多说,直接开搂!
下面示例创建了两个安卓项目,一个服务端(com.xaye.aidl_1)一个客户端(com.xaye.aidl_2)
step1
如果你的项目中没有使用过AIDL,那么当你创建AIDL文件时,会遇到下图这种情况👇
解决方法是在你使用AIDL的模块build.gradle
文件中添加以下代码
buildFeatures {
aidl true
}
然后再rebuild
一下就可以啦(●’◡’●)
step2
需要创建一个AIDL服务的接口,因为要供其他应用调用,创建AIDL文件时,需要右击main文件夹创建,因为AIDL需要和java文件夹在同级目录下,效果如下👇
此时的 libary
是作为主项目的子模块,当然 你也可以直接写在主模块中。
写之前先了解下在AIDL(Android Interface Definition Language)
中,默认支持的几种数据类型:
- 基本数据类型:
boolean
:布尔值(true 或 false)byte
:8 位有符号整数char
:16 位 Unicode 字符double
:64 位双精度浮点数float
:32 位单精度浮点数int
:32 位有符号整数long
:64 位有符号整数short
:16 位有符号整数
- Java 对象:
String
:字符串CharSequence
:字符序列
- Bundle:
Bundle
类可以用于在进程间传递一组键值对的集合,类似于一个映射表。
- List 和 Map:
List<T>
:支持基本数据类型和支持 Parcelable 接口的对象类型的列表。Map<K, V>
:支持基本数据类型和支持 Parcelable 接口的对象类型的映射表。
- Parcelable:
- 自定义的类如果实现了 Parcelable 接口,也可以在 AIDL 中使用。这是一个用来序列化对象的接口,可以将对象数据从一个进程传递到另一个进程。
- IBinder:
IBinder
接口可以在 AIDL 中作为参数传递,允许在不同的进程间传递服务对象的引用。
step3
为了上点难度,咱们自定义两种类型 VideoParams
和 VideoCallback
创建服务接口(com.xaye.aidl_1):
可以参照上图
// IRemoteService.aidl
package com.xaye.library;
// 引用 VideoParams 和 VideoCallback
import com.xaye.library.VideoParams;
import com.xaye.library.VideoCallback;
interface IRemoteService {
void processVideoClip(in VideoParams params, in VideoCallback callback);
}
VideoParams
VideoParams
是一个java bean,代码中需要对其序列化,在aidl中使用就是下面这样。
// VideoParams.aidl
package com.xaye.library;
import com.xaye.library.VideoParams;
parcelable VideoParams;
VideoCallback
一个简单的回调接口。
// VideoCallback.aidl
package com.xaye.library;
interface VideoCallback {
void onSuccess(String filePath);
void onError(String error, String throwableMessage);
}
aidl
包下要写的东西就这么些,其他应用要使用的话,需要把整个aidl
文件夹复制到main
下。
step4
如 step2 图示,在java包中 VideoParams
就是一个实现序列化后的bean,VideoService
是一个普通的安卓Service,在这个服务里处理其他应用发送的事件,代码如下,就简单处理返回了下。
public class VideoService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new IRemoteService.Stub() {
@Override
public void processVideoClip(VideoParams params, VideoCallback callback) {
try {
callback.onSuccess("aidl 1 回调数据!");
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
}
}
这个 IRemoteService
需要rebuild
后才会有,没build
会爆红。
至此… 呃,还有一点,在清单文件中注册该服务,这里主要注意要指定 action
,因为其他应用需要该 action
去找到你的服务。
<service android:name=".VideoService"
android:exported="true">
<intent-filter><!--需要接受的action,其他应用指定-->
<action android:name="com.xaye.libary.IRemoteService" />
</intent-filter>
</service>
至此,服务端代码完成。
补充: 在实际开发中其实没必要传实体类,定义和接收都比较繁琐,直接将实体类使用fastjson 转为json字符串,客户端接收的时候再解析下json就行了!
step5 :实现客户端代码(com.xaye.aidl_2)
无图言吊,上图,客户端代码结构
正如上面说的,把aidl文件夹 直接复制进来,然后,客户端调用 processVideoClip
方法,需要VideoParams
和 VideoCallback
两个类,因为 VideoCallback
类中的数据aidl
默认都支持,所以你rebuild
后就能直接导入,而VideoParams
是自定义数据类,需要把它放到java包 和 服务端同包名下即可,这样在运行时就能通过包名找到同一个VideoParams
了。
服务端代码如下👇
class TestActivity : AppCompatActivity() {
private val TAG = "TestActivity"
private lateinit var binding: ActivityTestBinding
private var remoteService: IRemoteService? = null
private var connection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
Log.i(TAG,"test aidl client onServiceConnected")
remoteService = IRemoteService.Stub.asInterface(service)
}
override fun onServiceDisconnected(name: ComponentName?) {
Log.i(TAG,"test aidl client onServiceDisconnected")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityTestBinding.inflate(layoutInflater)
setContentView(binding.root)
bindService()
binding.btnTest.setOnClickListener {
Log.i(TAG,"test aidl client click remoteService == null : ${remoteService == null}")
remoteService?.processVideoClip(VideoParams("","",""),object : VideoCallback.Stub(){
override fun onSuccess(filePath: String?) {
Log.i(TAG,"test aidl client onSuccess $filePath")
runOnUiThread {
Toast.makeText(this@TestActivity,filePath,Toast.LENGTH_LONG).show()
}
}
override fun onError(error: String?, throwableMessage: String?) {
Log.i(TAG,"test aidl client onError $error")
runOnUiThread {
Toast.makeText(this@TestActivity,"$error $throwableMessage",Toast.LENGTH_LONG).show()
}
}
})
}
}
private fun bindService() {
val intent = Intent()
intent.action = "com.xaye.libary.IRemoteService"//子模块中注册时 要求的action
intent.setPackage("com.xaye.aidl_1") //目标应用包名,注意不是子模块的
val bound = bindService(intent, connection, BIND_AUTO_CREATE)
if (bound) {
Log.i(TAG,"test aidl client bindService succeed")
} else {
Log.i(TAG,"test aidl client bindService failed")
}
}
}
首先就是bindService
去绑定远程服务,在ServiceConnection
中拿到服务端的服务,然后就可以愉快滴调用服务端方法了,这里主要注意下 intent.action
是服务注册时指定的!setPackage
是服务端所属的应用包名,即使你的服务在子模块中,也要写主应用包名!
安装上客户端 和 服务端应用,运行上面代码,效果如下图
至此,全剧终。
AIDL没什么难的,只是平时用的比较少,记得刚入行的时候听到感觉好高级,因为工作中没有需求,就一直没有机会去了解(其实就是懒😄),当亲手实操下,看到数据返回时,内心还是有点兴奋的,又get到一个技能,哈哈。
源码已上传csdn资源,0积分:AIDL客户端服务端源码
更多推荐
所有评论(0)