Binder→AIDL→Messenger
Binder是什么?
- Binder是Android提供的一种进程间通信(IPC)方式
- C/S架构,稳定性好,优于共享内存方式
(共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问) - 性能较好,数据拷贝一次,拷贝次数优于管道(管道的通信介质是文件)、消息队列、socket
- 安全性好,UID/PID可见
- 内核空间(Kernel)是系统内核运行的空间,用户空间(User Space)是用户程序运行的空间。为了保证安全性,它们之间是隔离的。
Binder通信流程
- 首先 Binder 驱动在内核空间创建一个数据接收缓存区;
- 接着在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系;
- 发送方进程通过系统调用 copyfromuser() 将数据 copy 到内核中的内核缓存区,由于内核缓存区和接收进程的用户空间存在内存映射,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。
AIDL是什么?
- AIDL( Android Interface Definition Language),译为:android接口定义语言,是一种IDL语言,用于生成可以在Android设备上两个进程之间进行 IPC的代码。
- AIDL是基于Binder实现的,为了让开发者更简单的使用Binder完成进程间通信
- AIDL在Android中的应用
Intent、Messenger、ContentProvider - AIDL文件在编译过程中生成接口的实现类,用于IPC通信
- AIDL支持基本数据类型,实现Parcelable接口的对象,和List、Map等
AIDL的使用
- in、out、inout、oneway关键字的作用
- 需求
在MyService中有一个计时器,每秒数据+1,将数据回调到主进程中的MainActivity - 接口回调的AIDL
在项目目录中右键→New→AIDL(创建AIDL文件)
package com.vision.myaidl;
interface IListener {
void onDataChange(int value);
}
- 建立连接的AIDL
package com.vision.myaidl;
import com.vision.myaidl.IListener;
interface IConnection {
void setCallBackListener(IListener listener);
}
Make Project 编译一下,会自动生成接口类
MyService
private const val TAG = "MyService"
class MyService : Service() {
var listenerProxy: IListener? = null
private var iConnection = object : IConnection.Stub() {
override fun setCallBackListener(listener: IListener?) {
listenerProxy = listener
counting()
}
}
override fun onBind(intent: Intent): IBinder {
return iConnection.asBinder()
}
//使用线程池做一个计时器,每秒钟回调数据
private var threadPool = ScheduledThreadPoolExecutor(1)
fun counting(){
var i = 0
threadPool.scheduleAtFixedRate({
Log.d(TAG, "onBind: ${++i}")
listenerProxy?.onDataChange(i)
}, 0, 1000, TimeUnit.MILLISECONDS)
}
override fun onDestroy() {
super.onDestroy()
threadPool.shutdownNow()
}
}
- MainActivity
private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bindService(Intent(this, MyService::class.java), object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val connection = IConnection.Stub.asInterface(service)
connection.setCallBackListener(object : IListener.Stub() {
override fun onDataChange(value: Int) {
Log.d(TAG, "onDataChange: $value")
}
})
}
override fun onServiceDisconnected(name: ComponentName?) {
}
}, BIND_AUTO_CREATE)
}
}
- 打印信息
2021-04-06 15:11:41.974 26110-26128/com.vision.myaidl D/MainActivity: onDataChange: 1
2021-04-06 15:11:42.974 26110-26128/com.vision.myaidl D/MainActivity: onDataChange: 2
2021-04-06 15:11:43.978 26110-26128/com.vision.myaidl D/MainActivity: onDataChange: 3
2021-04-06 15:11:44.977 26110-26128/com.vision.myaidl D/MainActivity: onDataChange: 4
2021-04-06 15:11:45.974 26110-26129/com.vision.myaidl D/MainActivity: onDataChange: 5
2021-04-06 15:11:41.974 26149-26187/com.vision.myaidl D/MyService: onBind: 1
2021-04-06 15:11:42.974 26149-26187/com.vision.myaidl D/MyService: onBind: 2
2021-04-06 15:11:43.975 26149-26187/com.vision.myaidl D/MyService: onBind: 3
2021-04-06 15:11:44.975 26149-26187/com.vision.myaidl D/MyService: onBind: 4
2021-04-06 15:11:45.974 26149-26187/com.vision.myaidl D/MyService: onBind: 5
关键字(oneway)
- 如果子进程中阻塞,例如sleep(5000),则会发现主进程中也跟着阻塞,为了避免这种情况,可以使用oneway关键字标记AIDL中的方法,被oneway标记后不会阻塞主进程,同时该方法不能有返回值
private var iConnection = object : IConnection.Stub() {
override fun setCallBackListener(listener: IListener?) {
listenerProxy = listener
sleep(5000)
counting()
}
}
- 使用oneway标记阻塞的方法后,不会阻塞主进程
package com.vision.myaidl;
import com.vision.myaidl.IListener;
interface IConnection {
oneway void setCallBackListener(IListener listener);
}
关键字(in、out、inout)
- in:纯粹的输入参,这意味着是从客户端到服务器的数据;
- out:参数是纯粹的输出,这意味它的数据是通过服务器到客户端的;
- inout:参数是输入也是输出,客户端的值在服务器可能会被修改;
参考资源
版权声明:本文为yu540135101原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。