美文网首页Android Review
Android Review - Binder机制(二)

Android Review - Binder机制(二)

作者: 你需要一台永动机 | 来源:发表于2019-10-17 18:20 被阅读0次

    前一篇我们从理论上来了解Android的Binder机制,本篇从实战来深入了解Android的Binder机制。

    Binder的使用

    Binder依赖于Service,在组件(Activity)中通过bindService(),就可以获取Service中的Binder对象,实现Service与Activity的通信。

    服务分为本地服务和远程服务,都可以使用Binder。

    一:本地服务
    在本地服务中使用Binder,只需要两步:

    1. 声明一个Service,重写onBind(),返回一个继承Binder的自定义对象;
    2. 声明一个ServiceConnection,重写onServiceConnected(),获取IBinder对象,然后调用Activity的bindService();

    声明Service:

    class ServiceWithBinder : Service() {
    
        private val TAG = "ServiceWithBinder"
    
        class InnerBinder : Binder() {
    
            @Throws(RemoteException::class)
            fun multiply(x: Int, y: Int): Int {
                return x * y
            }
    
            @Throws(RemoteException::class)
            fun divide(x: Int, y: Int): Int {
                return x / y
            }
        }
    
        override fun onBind(t: Intent): IBinder? {
            Log.e(TAG, "onBind")
            return InnerBinder()
        }
    }
    

    在Activity中绑定服务:

    fun myBindService() {
       val mServiceConnBinder = object : ServiceConnection {
            override fun onServiceDisconnected(name: ComponentName) {
                Log.e(TAG, "onServiceDisconnected Binder")
                binderInterface = null
            }
    
            override fun onServiceConnected(name: ComponentName, service: IBinder) {
                Log.e(TAG, "onServiceConnected Binder")
                binderInterface = service as ServiceWithBinder.InnerBinder
                //调用服务的方法
                binderInterface?.multiply(3,2)
            }
        }
        val intentBinder = Intent(this, ServiceWithBinder::class.java)
        //此时使用bindService开启服务
       bindService(intentBinder, mServiceConnBinder, Context.BIND_AUTO_CREATE)
        //销毁服务
       unbindService(mServiceConnBinder)
    }
    

    二:远程服务
    在远程服务中使用Binder,需要三步:创建aidl、声明Service、调用bindService。

    1.创建aidl
    aidl是进程间通信接口,需要在客户端(声明Service的应用)和服务端(调用Service的应用)同时声明,并且要完全一致。

    首先,在服务端端创建aidl:选中项目目录->右键选中new->然后选中AIDL->填写文件名(比如:ICalcAIDL)->修改aidl的代码。

    // ICalcAIDL.aidl
    package com.pmm.demo.advanced;
    
    // Declare any non-default types here with import statements
    interface ICalcAIDL {
        int add(int x , int y);
        int min(int x , int y );
    }
    

    然后,在客户端创建aidl,步骤同上。客户端的aidl要与服务端的一模一样,包括包名、类名、方法。

    然后,选中Build->Make Project,编译整个工程,就会在build/generated/source/aidl/debug目录下生产一个名为ICalcAIDL的接口。

    2.声明Service
    在服务端,声明一个类(比如:MyStub)继承ICalcAIDL.Stub(ICalcAIDL的代理类,系统帮我们生成的),然后声明一个继承Service的类,在onBind()中返回MyStub对象。

    class ServiceWithAIDL : Service() {
    
        private val TAG = "ServiceWithAIDL"
    
        private val mBinder = object : ICalcAIDL.Stub() {
    
            @Throws(RemoteException::class)
            override fun add(x: Int, y: Int): Int {
                return x + y
            }
    
            @Throws(RemoteException::class)
            override fun min(x: Int, y: Int): Int {
                return x - y
            }
        }
    
        override fun onBind(t: Intent): IBinder? {
            Log.e(TAG, "onBind")
            return mBinder
        }
    }
    

    3.调用bindService
    在客户端,声明一个匿名内部类ServiceConnection,重写onServiceConnected(),通过ICalcAIDL.Stub.asInterface(iBinder)获取服务端的ICalcAIDL对象。

    fun myBindService() {
       val mServiceConnAIDL = object : ServiceConnection {
            override fun onServiceDisconnected(name: ComponentName) {
                Log.e(TAG, "onServiceDisconnected AIDL")
                mCalcAidl = null
            }
    
            override fun onServiceConnected(name: ComponentName, service: IBinder) {
                Log.e(TAG, "onServiceConnected AIDL")
                mCalcAidl = ICalcAIDL.Stub.asInterface(service)
               //调用服务的方法
                mCalcAidl?.add(3, 2)
            }
        }
        val intentBinder = Intent(this, ServiceWithAIDL::class.java)
        //此时使用bindService开启服务
       bindService(intentBinder, mServiceConnAIDL, Context.BIND_AUTO_CREATE)
        //销毁服务
        unbindService(mServiceConnAIDL)
    }
    

    AIDL机制

    aidl是进程间通信接口,它不是Java的类,更像一种协议。它的作用是让AS自动生成Binder类,供客户端调用。

    aidl文件编译后,会生成一个Java接口,分为3层:ICalcAIDL、Stub、Proxy。

    package com.pmm.demo.advanced;
    
    public interface ICalcAIDL extends android.os.IInterface {
       //具体的Binder类
        public static abstract class Stub extends android.os.Binder implements com.pmm.demo.advanced.ICalcAIDL {
            ·······
               //代理类
            private static class Proxy implements com.pmm.demo.advanced.ICalcAIDL {
                ·······
            }
        }
    
        //加
        public int add(int x, int y) throws android.os.RemoteException;
        //减
        public int min(int x, int y) throws android.os.RemoteException;
    }
    

    下面是生成类ICalcAIDL的结构图

    ICalcAIDL的结构图

    1.ICalcAIDL
    ICalcAIDL就是aidl文件中声明的接口,也就是我们要给客户端调用的功能。

    2.Sub
    Stub是一个使用ICalcAIDL装饰的Binder类,是我们实际传递给客户端的对象。通过Stub,客户端就可以调用ICalcAIDL的方法。

    首先是DESCRIPTOR,默认值是包名+类名,作用是在IBinder中查找ICalcAIDL接口。

    private static final java.lang.String DESCRIPTOR = "com.pmm.demo.advanced.ICalcAIDL";
    

    然后是asInterface(),作用是返回Proxy对象,也就是把ICalcAIDL对象返回给客户端。我们注意到一般obj都是对应服务的指引,然后传递给Proxy对象。

    public static com.pmm.demo.advanced.ICalcAIDL asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof com.pmm.demo.advanced.ICalcAIDL))) {
            return ((com.pmm.demo.advanced.ICalcAIDL) iin);
        }
        //返回Proxy对象
        return new com.pmm.demo.advanced.ICalcAIDL.Stub.Proxy(obj);
    }
    

    然后是onTransact(),作用是处理客户端的请求,并将处理结果返回给客户端。

    @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) {
            //用来指定DESCRIPTOR,从而确定需要通讯的接口
            case INTERFACE_TRANSACTION: {
                reply.writeString(descriptor);
                return true;
            }
            //用来处理ICalcAIDL的add()
            case TRANSACTION_add: {
                //说明data正准备给指定DESCRIPTOR的接口使用
                data.enforceInterface(descriptor);
                int _arg0;
                _arg0 = data.readInt();
                int _arg1;
                _arg1 = data.readInt();
                int _result = this.add(_arg0, _arg1);
                //说明当前操作没有出现异常
                reply.writeNoException();
                reply.writeInt(_result);
                return true;
            }
             //用来处理ICalcAIDL的min()
            case TRANSACTION_min: {
                data.enforceInterface(descriptor);
                int _arg0;
                _arg0 = data.readInt();
                int _arg1;
                _arg1 = data.readInt();
                int _result = this.min(_arg0, _arg1);
                reply.writeNoException();
                reply.writeInt(_result);
                return true;
            }
            default: {
                return super.onTransact(code, data, reply, flags);
            }
        }
    }
    

    3.Proxy
    Proxy,顾名思义,就是ICalcAIDL的代理类,用来实现ICalcAIDL的方法。Proxy的作用是将需要传递的参数转化为Parcel,从而跨进程传输。

    我们来看下核心代码,只显示add(),不显示min方法。

    private static class Proxy implements com.pmm.demo.advanced.ICalcAIDL {
        private android.os.IBinder mRemote;
    
        Proxy(android.os.IBinder remote) {
            mRemote = remote;
        }
    
        @Override
        public int add(int x, int y) throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            int _result;
            try {
                //用来标识_data是给含有DESCRIPTOR标志的Binder接口的参数
                _data.writeInterfaceToken(DESCRIPTOR);
                _data.writeInt(x);
                _data.writeInt(y);
                //调用Stub的transact,处理add请求
                mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                _reply.readException();
                //获取Stub的处理结果
                _result = _reply.readInt();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }
    }
    

    我们可以看到,前面asInterface(obj)传递进来的对象,是我们代理里真正的执行对象。比如:mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);

    AIDL其实通过我们写的aidl文件,帮助我们生成了一个接口,一个Stub类用于服务端,一个Proxy类用于客户端调用。

    客户端使用AIDL接口的asInterface()获取对应的代理,代理调用对应的方法,方法里都有mRemote.transact()进行具体的调用发消息给服务器,最后服务端对应Binder对象中的onTransact()来处理客户端的请求,并将处理结果返回给客户端。

    PS:本文整理自以下博客
    安卓移动架构07-Binder核心机
    Android aidl Binder框架浅析

    若有发现问题请致邮 caoyanglee92@gmail.com

    相关文章

      网友评论

        本文标题:Android Review - Binder机制(二)

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