Android--Binder机制与AIDL

关于安卓中的Binder机制,该博客很值得一看:Android Binder设计与实现 - 设计篇,其中深入到了底层原理实现,篇幅很长,需要耐心看完

Binder机制是安卓自己的一套跨进程通信机制,对此需要有进程的概念,Java--线程中有提到进程,当然Linux中自带了很多跨进程通信的方式:管道、消息队列、信号灯、信号、套接字、共享内存,其中很多都因为过时而无人问津了,Binder机制底层使用的是共享内存,共享内存也分两块,一个是虚拟内存(mmap),一个是内存共享(shm),具体可以查看该博客:mmap和shm共享内存的区别和联系

Binder使用的是内存映射(mmap),mmap可以在磁盘上开辟一块空间,作为内存,这样写操作就不需要缓存拷贝了,只需要直接拷贝到文件,用Java来说就是不需要调用write、flush方法,读取时也不需要再通过缓存拷贝到内存中

一、Binder机制简单介绍

inder机制核心点就是利用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引用存入缓存中

IBinder: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;
        }
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值