美文网首页Android面试
手写AIDL 了解binder在java层的实现

手写AIDL 了解binder在java层的实现

作者: wildeyess | 来源:发表于2020-06-09 17:47 被阅读0次

    AIDL是什么

    Android 接口定义语言 (AIDL) 与您可能使用过的其他接口语言 (IDL) 类似。您可以利用它定义客户端与服务均认可的编程接口,以便二者使用进程间通信 (IPC) 进行相互通信。在 Android 中,一个进程通常无法访问另一个进程的内存。因此,为进行通信,进程需将其对象分解成可供操作系统理解的原语,并将其编组为可供您操作的对象。编写执行该编组操作的代码较为繁琐,因此 Android 会使用 AIDL 为您处理此问题。

    AIDL的用处

    Android 接口定义语言 (AIDL) 会将对象分解成原语,操作系统可通过识别这些原语并将其编组到各进程中来执行 IPC。对于之前采用 Messenger 的方法而言,其实际上是以 AIDL 作为其底层结构。如上所述,Messenger 会在单个线程中创建包含所有客户端请求的队列,以便服务一次接收一个请求。不过,如果您想让服务同时处理多个请求,则可直接使用 AIDL。在此情况下,您的服务必须达到线程安全的要求,并且能够进行多线程处理。

    手写AIDL

    1.定义binder服务端所提供的功能接口。定义服务端的功能为提供人员的增加与删除,并提供所有人员列表()。

    /**
     * 这个类用来定义服务端具有什么样的能力,继承自IInterface才就有跨进程传输的基础能力
    /**
     * Base class for Binder interfaces.  When defining a new interface,
     * you must derive it from IInterface.  Iinterface的说明
     */
     */
    public interface PersonManager extends IInterface {
        /**
         * 添加人数
         *
         * @throws RemoteException
         */
        void addPerson(PersonBean personBean) throws RemoteException;
    
        /**
         * 删除人数
         *
         * @throws RemoteException
         */
        void deletePerson(PersonBean personBean) throws RemoteException;
    
        /**
         * 获取人数
         *
         * @throws RemoteException
         */
        List<PersonBean> getPersons() throws RemoteException;
    }
    

    PersonBean 是人员信息的model,我只写了一个name属性,跨进程传输则必须实现Parcelable相关接口。为了后面的删除功能还需要重写equle和hashcode方法。

    /**
     * 实现了parcelable接口的实体类,可用于跨进程传输
     */
    class PersonBean() : Parcelable {
        var name: String? = null;
    
        constructor(parcel: Parcel) : this() {
            name = parcel.readString()
        }
    
        override fun writeToParcel(parcel: Parcel, flags: Int) {
            parcel.writeString(name)
        }
    
        override fun describeContents(): Int {
            return 0
        }
    
        override fun toString(): String {
            return "PersonBean(name=$name)"
        }
    
        override fun equals(other: Any?): Boolean {
            if (this === other) return true
            if (javaClass != other?.javaClass) return false
    
            other as PersonBean
    
            if (name != other.name) return false
    
            return true
        }
    
        override fun hashCode(): Int {
            return name?.hashCode() ?: 0
        }
    
        companion object CREATOR : Parcelable.Creator<PersonBean> {
            override fun createFromParcel(parcel: Parcel): PersonBean {
                return PersonBean(parcel)
            }
    
            override fun newArray(size: Int): Array<PersonBean?> {
                return arrayOfNulls(size)
            }
        }
    
    }
    

    2.添加proxy代理类。我的理解是模拟服务端的所有功能,让客户端感受不到调用的是远程还是本地的服务功能。可以看见PersonManagerProxy实现了PersonManager
    接口,所以就实现了相应的方法,而在构造方法中传入了IBinder,实际上的跨进程传输的实体。addPerson,deletePerson,getPersons,三个方法中可以看见实际调用的方法是IBinder的transact方法。PersonManagerProxy只是进行了一些参数方法的封装。

    /**
     * personmanager/binder在本地的远程代理类。
     */
    public class PersonManagerProxy implements PersonManager {
        /**
         * 远程binder对象
         */
        IBinder remote;
        private static final String DESCRIPTOR = "com.study.stydyfirst.server.PersonManager";
    
        /**
         * 构造方法传入ibinder
         *
         * @param remote
         */
        public PersonManagerProxy(IBinder remote) {
            this.remote = remote;
        }
    
        public String getInterfaceDescriptor() {
            return DESCRIPTOR;
        }
    
        @Override
        public void addPerson(PersonBean personBean) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel replay = Parcel.obtain();
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                if (personBean != null) {
                    data.writeInt(1);
                    personBean.writeToParcel(data, 0);
                } else {
                    data.writeInt(0);
                }
                remote.transact(PersonManagerStub.TRANSAVTION_addperson, data, replay, 0);
                replay.readException();
            } finally {
                replay.recycle();
                data.recycle();
            }
        }
    
        @Override
        public void deletePerson(PersonBean personBean) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel replay = Parcel.obtain();
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                if (personBean != null) {
                    data.writeInt(1);
                    personBean.writeToParcel(data, 0);
                } else {
                    data.writeInt(0);
                }
                remote.transact(PersonManagerStub.TRANSAVTION_deleteperson, data, replay, 0);
                replay.readException();
            } finally {
                replay.recycle();
                data.recycle();
            }
        }
    
        @Override
        public List<PersonBean> getPersons() throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel replay = Parcel.obtain();
            List<PersonBean> result;
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                remote.transact(PersonManagerStub.TRANSAVTION_getpersons, data, replay, 0);
                replay.readException();
                result = replay.createTypedArrayList(PersonBean.CREATOR);
            } finally {
                replay.recycle();
                data.recycle();
            }
            return result;
        }
    
        @Override
        public IBinder asBinder() {
            return remote;
        }
    }
    

    3.添加PersonManagerStub类。跨进程传输的Binder对象。从代码中可以看见PersonManagerStub 继承自Binder,实现了PersonManager 接口的方法。说明:

    • 说明他就是一个binder类,具有跨进程传输的能力
    • 他的实现类必须实现我们定义的远程服务端具备的添加,删除,列表获取等能力
      继续分析下PersonManagerStub 的其他代码,
    • 构造方法中 this.attachInterface(this, DESCRIPTOR);的作用就是向BinderService注册Binder服务。只有注册了binder,客户端才能查询到有这个binder对象,并使用它。
    • asInterface方法,将一个binder对象转换为PersonManager 这个我们定义的接口。通过binder.queryLocalInterface(DESCRIPTOR);方法,判断是否返回代理对象。
    • onTransact方法。就是在刚才PersonManagerProxy类中调用的方法就到这了。将客户端传输的数据在这里进行实际处理并通过跨进程传输到远程service那里
    /**
     * 继承自binder实现了personmanager的方法,说明它可以跨进程传输,并可进行服务端相关的数据操作
     */
    public abstract class PersonManagerStub extends Binder implements PersonManager {
        private static final String DESCRIPTOR = "com.study.stydyfirst.server.PersonManager";
    
        public PersonManagerStub() {
            this.attachInterface(this, DESCRIPTOR);
        }
    
        public static PersonManager asInterface(IBinder binder) {
            if (binder == null)
                return null;
            IInterface iin = binder.queryLocalInterface(DESCRIPTOR);//通过DESCRIPTOR查询本地binder,如果存在则说明调用方和service在同一进程间,直接本地调用
            if (iin != null && iin instanceof PersonManager)
                return (PersonManager) iin;
            return new PersonManagerProxy(binder);//本地没有,返回一个远程代理对象
        }
        @Override
        public IBinder asBinder() {
            return this;
        }
    
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION:
                    reply.writeString(DESCRIPTOR);
                    return true;
    
                case TRANSAVTION_getpersons:
                    data.enforceInterface(DESCRIPTOR);
                    List<PersonBean> result = this.getPersons();
                    reply.writeNoException();
                    reply.writeTypedList(result);
                    return true;
    
                case TRANSAVTION_addperson:
                    data.enforceInterface(DESCRIPTOR);
                    PersonBean arg0 = null;
                    if (data.readInt() != 0) {
                        arg0 = PersonBean.CREATOR.createFromParcel(data);
                    }
                    this.addPerson(arg0);
                    reply.writeNoException();
                    return true;
                case TRANSAVTION_deleteperson:
                    data.enforceInterface(DESCRIPTOR);
                    PersonBean personBean = null;
                    if (data.readInt() != 0) {
                        personBean = PersonBean.CREATOR.createFromParcel(data);
                    }
                    this.deletePerson(personBean);
                    reply.writeNoException();
                    return true;
    
            }
            return super.onTransact(code, data, reply, flags);
        }
    
        public static final int TRANSAVTION_getpersons = IBinder.FIRST_CALL_TRANSACTION;
        public static final int TRANSAVTION_addperson = IBinder.FIRST_CALL_TRANSACTION + 1;
        public static final int TRANSAVTION_deleteperson = IBinder.FIRST_CALL_TRANSACTION + 2;
    }
    
    

    AIDL的相关类已经编写完毕,下面只需要远程service实现相应方法,客户端调用,就能测试该手写AIDL的跨进程传输能力了
    远程进程service类,这里使用前台服务通知来显示人员信息,便于直观查看跨进程传输结果,可以看见我们在service中实例化了一个PersonManagerStub对象,并实现了相关方法。并通过notification显示personss的数据信息,并通过service的onBind方法将binder对象传输给客户端

    /**
     * 远程service
     */
    class PersonManagerService : Service() {
        /**
         * 创建一个数组用于管理人员  var 声明可变变量,val声明不可变变量 相当于final
         */
        private var personss: ArrayList<PersonBean> = ArrayList();
        val CHANNEL_ID = "personmanager";
        val notificationId = 1;
        override fun onCreate() {
            super.onCreate()
            createNotificationChannel()
            //初始化通知栏,
            showNotification()
    
        }
    
        /**
         * 将当前人数显示通过通知栏显示出来
         */
        fun showNotification() {
            var builder = NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("0fdg")
                .setStyle(
                    NotificationCompat.BigTextStyle()
                        .bigText("当前的人数是:" + personss.size + "人员信息:" + personss.toString())
                )
                .setSmallIcon(R.mipmap.ic_launcher)//不设置smallicon,文字信息不生效,我服了
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
            with(NotificationManagerCompat.from(this)) {
                // notificationId is a unique int for each notification that you must define
                //notify(notificationId, builder.build())
                startForeground(notificationId, builder.build())
            }
        }
    
        /**
         * 首先创建通知渠道,再进行通知显示
         */
        private fun createNotificationChannel() {
            // Create the NotificationChannel, but only on API 26+ because
            // the NotificationChannel class is new and not in the support library
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                val name = getString(R.string.channel_name)
                val descriptionText = getString(R.string.channel_description)
                val importance = NotificationManager.IMPORTANCE_DEFAULT
                val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
                    description = descriptionText
                }
                // Register the channel with the system
                val notificationManager: NotificationManager =
                    getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
                notificationManager.createNotificationChannel(channel)
            }
        }
    
        override fun onBind(intent: Intent): IBinder {
    //        TODO("Return the communication channel to the service.")
    
            return binder
        }
    
        private val binder = object : PersonManagerStub() {
            override fun addPerson(personBean: PersonBean?) {
                if (personBean != null) {
                    personss.add(personBean)
                    Log.d("Server", "添加" + personBean.name)
                    showNotification()
                };
            }
    
            override fun deletePerson(personBean: PersonBean?) {
                personss.remove(personBean)
                Log.d("Server", "删除");
    
                showNotification()
            }
    
            override fun getPersons(): List<PersonBean> {
                Log.d("Server", "删除")
    
                return persons;
            }
        }
    }
    

    本地clientActivity,简单service启动与远程建立链接,并传输数据,本地通过PersonManagerStub.asInterface(service)方法直接将binder对象转换为PersonManager即可直接调用远程方法进行数据传输

    /**
     * 本地client
     */
    class ClientActivity : AppCompatActivity() {
    
        /** The primary interface we will be calling on the service.  */
        private var mService: PersonManager? = null
    
        /**
         * 是否绑定的标志
         */
        private var isBound = false
    
        /**
         * Class for interacting with the main interface of the service.
         */
        private val mConnection = object : ServiceConnection {
            override fun onServiceConnected(className: ComponentName, service: IBinder) {
                // This is called when the connection with the service has been
                // established, giving us the service object we can use to
                // interact with the service.  We are communicating with our
                // service through an IDL interface, so get a client-side
                // representation of that from the raw service object.
                Log.d("client", "链接成功====");
                mService = PersonManagerStub.asInterface(service)//将service转成远程服务代理对象,并调用他的方法
                isBound = true
            }
    
            override fun onServiceDisconnected(className: ComponentName) {
                // This is called when the connection with the service has been
                // unexpectedly disconnected -- that is, its process crashed.
                mService = null
                isBound = false
            }
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_client)
            // Bind to LocalService
            addListener()
        }
    
        override fun onDestroy() {
            super.onDestroy()
            if (isBound) {
                unbindService(mConnection)
            }
        }
    
        private fun addListener() {
            btn_add.setOnClickListener {
                var person = PersonBean()
                person.name = "刘德华"
                mService?.addPerson(person)
            }
            btn_delete.setOnClickListener {
                var person = PersonBean()
                person.name = "刘德华"
                mService?.deletePerson(person)
            }
            btn_bind.setOnClickListener {
                Toast.makeText(baseContext, "绑定服务", Toast.LENGTH_SHORT).show();
                Intent(this, PersonManagerService::class.java).also { intent ->
                    bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
                    //startService(intent)
                }
            }
        }
    }
    

    以上就是java层面的client-binder-servi,跨进程的一个传输流程。并通过手写方式实现了AIDL的所有功能。 项目demo地址https://github.com/92123748/AIDLTest

    相关文章

      网友评论

        本文标题:手写AIDL 了解binder在java层的实现

        本文链接:https://www.haomeiwen.com/subject/dxtjwhtx.html