关于安卓中的Binder机制,该博客很值得一看:Android Binder设计与实现 - 设计篇,其中深入到了底层原理实现,篇幅很长,需要耐心看完
Binder机制是安卓自己的一套跨进程通信机制,对此需要有进程的概念,Java--线程中有提到进程,当然Linux中自带了很多跨进程通信的方式:管道、消息队列、信号灯、信号、套接字、共享内存,其中很多都因为过时而无人问津了,Binder机制底层使用的是共享内存,共享内存也分两块,一个是虚拟内存(mmap),一个是内存共享(shm),具体可以查看该博客:mmap和shm共享内存的区别和联系
Binder使用的是虚拟内存(mmap),即在磁盘上开辟一块空间,作为内存,这样写操作就不需要缓存拷贝了,用Java来说就是不需要调用write、flush方法,读取时也不需要再一次拷贝到内存中。在早年玩一些大型电脑游戏时,系统会给出提示:"虚拟内存不足",看起来游戏中也大量的运用了这个技术
一、Binder机制简单介绍
Binder机制核心点就是利用mmap开辟一块空间,使得多个进程可以访问,由于相当于内存,所以拷贝只需要一次:从线程的工作内存到主内存,想要深入理解可以查看Java--深入理解JMM模型、Java并发特性
1.ServiceManager
跨进程通信需要一个管理者,而ServiceManager就是这个管理者,它存有一份Binder的映射表,一般我们创建的Binder实体后,都会在这个表创建映射关系
ServiceManager在开机时就启动了,ServiceManager自己也会创建一份Binder实体,每个App都可以获取到ServiceManager,因为ServiceManager的Binder实体对应的引用号是固定的,通过context.getSystemService最终拿到该Binder引用,来获取映射表中的一些其他系统服务,如LOCATION_SERVICE(定位服务)、NOTIFICATION_SERVICE(通知服务)等等,代码体现在SystemServiceRegistry类中,它会预先将系统服务的Binder引用存入缓存中
2.Binder的创建
首先服务端想要向外部进程开放通信,那么它会创建Binder实体,并将Binder引用注册到SystemService的映射表中,利用上面的模板,Binder的创建如下:
3.Client获取Binder引用进行通信
Service端搞定后,Client进程就可以通过ServiceManager获取Binder引用进行通信了,这里不是直接使用映射表的引用,而是新建一个引用来指向Binder
二、ADIL
ADIL是一个自动生成代码的工具,它能够帮助我们快速建立Binder相关代码,实现Binder机制的跨进程通信
1.service端
1.1 首先创建AIDL文件
我这边已经创建好了
内容如下
- RequestData.aidl:
package com.aruba.serviceapplication;
// 发送数据的自定义实体类
parcelable RequestData;
- ResponseData.aidl:
package com.aruba.serviceapplication;
// 返回数据的自定义实体类
parcelable ResponseData;
- IMyAidlInterface.aidl:
package com.aruba.serviceapplication;
// 注意需要手动导入自定义的类
import com.aruba.serviceapplication.RequestData;
import com.aruba.serviceapplication.ResponseData;
interface IMyAidlInterface {
ResponseData send(in RequestData request);
}
1.2 创建对应的实体类
需要和aidl定义的同一个包名下创建,否则会找不到
- RequestData:
package com.aruba.serviceapplication
import android.os.Parcel
import android.os.Parcelable
/**
* 发送数据
* Created by aruba on 2021/11/17.
*/
class RequestData(val data: String?) : Parcelable {
constructor(parcel: Parcel) : this(parcel.readString()) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(data)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<RequestData> {
override fun createFromParcel(parcel: Parcel): RequestData {
return RequestData(parcel)
}
override fun newArray(size: Int): Array<RequestData?> {
return arrayOfNulls(size)
}
}
}
- ResponseData:
package com.aruba.serviceapplication
import android.os.Parcel
import android.os.Parcelable
/**
* 返回数据
* Created by aruba on 2021/11/17.
*/
class ResponseData(val data: String?) : Parcelable {
constructor(parcel: Parcel) : this(parcel.readString()) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(data)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<ResponseData> {
override fun createFromParcel(parcel: Parcel): ResponseData {
return ResponseData(parcel)
}
override fun newArray(size: Int): Array<ResponseData?> {
return arrayOfNulls(size)
}
}
}
非常简单,都是一个字符串成员变量
1.3 创建Service
Service在onBind方法返回Binder实体,最后会注册到ServiceManager的映射表,而aidl已经帮助我们自动生成创建Binder的代码,通过IMyAidlInterface.Stub对象的asBinder方法
IMyAidlInterface就是我们定义的aidl文件中的接口,编译后会自动生成IMyAidlInterface.java类
/**
* Created by aruba on 2021/11/17.
*/
class MyService : Service() {
private val binder by lazy {
object : IMyAidlInterface.Stub() {
override fun send(request: RequestData?): ResponseData {
Log.i("service", "receive:${request?.data}")
return ResponseData("i'm service message")
}
}.asBinder()
}
override fun onBind(intent: Intent?): IBinder? {
return binder
}
}
别忘了在manifest.xml中注册,并且给其他进程提供隐式调用
<service
android:name=".MyService"
android:exported="true">
<intent-filter>
<action android:name="com.aruba.service" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
1.4 Activity中启动服务
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//启动服务,开启通信通道
startService(Intent(this, MyService::class.java))
}
}
2.Client端
2.1 复制aidl相关文件
Client端需要将aidl文件复制一份到项目中,并且自定义实体类的Java文件也需要复制,包名还需要和Service端一致
2.2 bindService
Activity中通过bindService,隐式调用Service,获取Service端的Binder引用,该Binder引用就是我们在aidl中定义的IMyAidlInterface接口,通过IMyAidlInterface.Stub.asInterface方法获取
// 隐式调用
val intent = Intent("com.aruba.service").apply {
`package` = "com.aruba.serviceapplication"//设置服务端的包名
}
bindService(intent, object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
myAidlInterface = IMyAidlInterface.Stub.asInterface(service)
}
override fun onServiceDisconnected(name: ComponentName?) {
myAidlInterface = null
}
}, 0)
最后写一个按钮,点击调用下send方法,发送消息到Service端
Actvity完整代码:
class MainActivity : AppCompatActivity() {
var myAidlInterface: IMyAidlInterface? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 隐式调用
val intent = Intent("com.aruba.service").apply {
`package` = "com.aruba.serviceapplication"//设置服务端的包名
}
bindService(intent, object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
myAidlInterface = IMyAidlInterface.Stub.asInterface(service)
}
override fun onServiceDisconnected(name: ComponentName?) {
myAidlInterface = null
}
}, 0)
}
/**
* 发送消息
*/
fun send(view: android.view.View) {
// 调用Ibinder的send方法
myAidlInterface?.send(RequestData("hello i'm client")).let {
(findViewById(R.id.textView) as TextView).text = it?.data
}
}
}
效果:
Service端也打印了日志:
三、AIDL源码分析
首先附上一张图,展示Binder机制发送消息调用流程
查看生成的IMyAidlInterface.java文件,其源码如下,过下即可:
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.aruba.serviceapplication;
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Default implementation for IMyAidlInterface.
*/
public static class Default implements com.aruba.serviceapplication.IMyAidlInterface {
@Override
public com.aruba.serviceapplication.ResponseData send(com.aruba.serviceapplication.RequestData request) throws android.os.RemoteException {
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.aruba.serviceapplication.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.aruba.serviceapplication.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.aruba.serviceapplication.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.aruba.serviceapplication.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.aruba.serviceapplication.IMyAidlInterface))) {
return ((com.aruba.serviceapplication.IMyAidlInterface) iin);
}
return new com.aruba.serviceapplication.IMyAidlInterface.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_send: {
data.enforceInterface(descriptor);
com.aruba.serviceapplication.RequestData _arg0;
if ((0 != data.readInt())) {
_arg0 = com.aruba.serviceapplication.RequestData.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
com.aruba.serviceapplication.ResponseData _result = this.send(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.aruba.serviceapplication.IMyAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public com.aruba.serviceapplication.ResponseData send(com.aruba.serviceapplication.RequestData request) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.aruba.serviceapplication.ResponseData _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((request != null)) {
_data.writeInt(1);
request.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_send, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().send(request);
}
_reply.readException();
if ((0 != _reply.readInt())) {
_result = com.aruba.serviceapplication.ResponseData.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.aruba.serviceapplication.IMyAidlInterface sDefaultImpl;
}
static final int TRANSACTION_send = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public static boolean setDefaultImpl(com.aruba.serviceapplication.IMyAidlInterface impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Stub.Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.aruba.serviceapplication.IMyAidlInterface getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public com.aruba.serviceapplication.ResponseData send(com.aruba.serviceapplication.RequestData request) throws android.os.RemoteException;
}
1.Stub类
把多余的代码去掉,首先看Stub类,Stub继承至Binder ,是真正的Binder实现类,我们在Service中也是实例化了一个Stub,其中asInterface方法是给Client端调用的,asBinder方法就是返回了自己,Service端不调用也可以,Binder类的核心方法是onTransact方法,Client调用aidl接口的方法后,最终这个方法会接收到消息,并调用代理相应的方法,也就是我们在Service中实例化Stub时重写的方法,最后底层通过mmap写回数据
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.aruba.serviceapplication.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.aruba.serviceapplication.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.aruba.serviceapplication.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.aruba.serviceapplication.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.aruba.serviceapplication.IMyAidlInterface))) {
return ((com.aruba.serviceapplication.IMyAidlInterface) iin);
}
return new com.aruba.serviceapplication.IMyAidlInterface.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_send: {
data.enforceInterface(descriptor);
com.aruba.serviceapplication.RequestData _arg0;
if ((0 != data.readInt())) {
_arg0 = com.aruba.serviceapplication.RequestData.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
// 调用Service本地代理的send方法,返回结果
// 就是我们在Service中实例化Stub时写的回调函数
com.aruba.serviceapplication.ResponseData _result = this.send(_arg0);
reply.writeNoException();
if ((_result != null)) {
reply.writeInt(1);
// 把数据写回Binder中
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
static final int TRANSACTION_send = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
2.Proxy类
除了Service端本地有一份默认的Proxy实例,用来调用回调方法外,其他的都是给Client生成的,其中mRemote为Client端bindService时获取到的IBinder,即Binder引用
private static class Proxy implements com.aruba.serviceapplication.IMyAidlInterface {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public com.aruba.serviceapplication.ResponseData send(com.aruba.serviceapplication.RequestData request) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.aruba.serviceapplication.ResponseData _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((request != null)) {
_data.writeInt(1);
request.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
// 发送消息,最终调用Binder的onTransact方法,该方法为阻塞方法
boolean _status = mRemote.transact(Stub.TRANSACTION_send, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {// 说明在一个进程中
return getDefaultImpl().send(request);
}
_reply.readException();
if ((0 != _reply.readInt())) {
// client端读取数据,反序列化
_result = com.aruba.serviceapplication.ResponseData.CREATOR.createFromParcel(_reply);
} else {
_result = null;
}
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.aruba.serviceapplication.IMyAidlInterface sDefaultImpl;
}