安卓复习之旅—Binder工作机制

Binder是Android中的一个类,实现了IBinder接口。从IPC角度Binder是Android中的一种跨进程通信方式;从Android framework角度来说,Binder是servicemanager连接各种manager(activitymanager、windowmanager…)的桥梁;从Android应用层来说,Binder是客户端和服务器进行通信的媒介,当bindservice的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据;Android开发中,Binder主要用在service中,包括AIDL和messenger,其中messenger底层也是AIDL,所以本篇文章通过AIDL来讲解Binder工作机制;

首先在工程中新建一个aidl文件:

package com.aidl.test;
interface IMath{
    int add(int a,int b);
    int sub(int a,int b);
}

然后在gen目录系统会自动生成IMath.java文件,这就是我们要分析的重点文件:


package com.aidl.test;

public interface IMath extends android.os.IInterface {
    /** Local-side IPC implementation stub class. */
    //Stub()就是一个binder类
    public static abstract class Stub extends android.os.Binder implements com.aidl.test.IMath {
        private static final java.lang.String DESCRIPTOR = "com.aidl.test.IMath";

        /** Construct the stub at attach it to the interface. */

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.aidl.test.IMath interface,
         * generating a proxy if needed.
         * 将服务器端的binder对象转换成客户端所需要的aidl接口类型的对象
         */
        public static com.aidl.test.IMath asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            //同进程返回本身
            if (((iin != null) && (iin instanceof com.aidl.test.IMath))) {
                return ((com.aidl.test.IMath) iin);
            }
            //不同进程,返回stub.Proxy对象
            return new com.aidl.test.IMath.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        //onTransact运行在服务器端的binder线程池中,处理客户端发送的跨进程请求
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
                throws android.os.RemoteException {
            //确定客户端发送的哪种请求,从data中取出目标方法所需的参数,当方法执行完之后,想reply写入返回值
            switch (code) {
            case INTERFACE_TRANSACTION: {
                reply.writeString(DESCRIPTOR);
                return true;
            }
            case TRANSACTION_add: {
                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;
            }
            case TRANSACTION_sub: {
                data.enforceInterface(DESCRIPTOR);
                int _arg0;
                _arg0 = data.readInt();
                int _arg1;
                _arg1 = data.readInt();
                int _result = this.sub(_arg0, _arg1);
                reply.writeNoException();
                reply.writeInt(_result);
                return true;
            }
            }
            return super.onTransact(code, data, reply, flags);
        }
//运行在客户端
        private static class Proxy implements com.aidl.test.IMath {
            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 int add(int a, int b) throws android.os.RemoteException {
                //创建输入和输出参数和返回值
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    //写入参数
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(a);
                    _data.writeInt(b);
                    //调用transact发起请求,此时当前线程被挂起,服务端的ontransact方法被调用
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                    _reply.readException();
                    //写入返回值
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public int sub(int a, int b) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(a);
                    _data.writeInt(b);
                    mRemote.transact(Stub.TRANSACTION_sub, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
//标明在transact过程中客户端所请求的是哪个方法
        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_sub = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }
//aidl文件中声明的方法
    public int add(int a, int b) throws android.os.RemoteException;

    public int sub(int a, int b) throws android.os.RemoteException;
}

代码中已经写了相应的注释了,就不啰嗦了,需要注意的是客户端发起请求后,当前线程会被挂起,直至服务端进程返回数据,如果一个远程方法是很耗时的,就不能在主线程发起此远程请求,由于服务端的binder方法运行在binder的线程池中,所以binder方法不管是否耗时都应该采用同步的方式去实现,因为它已经运行在一个线程中了。

这里写图片描述
Binder工作机制到此就差不多了,接下来看一下binder连接池:

binder连接池
先回顾一下AIDL的使用流程:首先创建一个service和一个AIDL接口,接着创建一个类继承AIDL的stub类,并实现其抽象方法,在onBind方法中返回这个类的对象,最后客户端通过bindservice绑定服务端的service,建立连接后就可以访问远程服务端的方法了;

import com.aidl.test.IMath.Stub;

public class MyService extends Service{

    private  Stub stub = new Stub() {
        @Override
        public int sub(int a, int b) throws RemoteException {
            return a - b;
        }
        @Override
        public int add(int a, int b) throws RemoteException {
            return  a + b;
        }
    };
    @Override
    public IBinder onBind(Intent intent) {
        return stub;
    }
}
public class MainActivity extends Activity implements OnClickListener {
    private View mBind,mUnbind,mAdd,mSub;
    private IMath math;
    private ServiceConnection conn = new ServiceConnection() {


        @Override
        public void onServiceDisconnected(ComponentName name) {

        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            math = IMath.Stub.asInterface(service);
            Toast.makeText(getApplicationContext(), "绑定成功", 0).show();
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBind = findViewById(R.id.bind);
        mUnbind = findViewById(R.id.unbind);
        mAdd = findViewById(R.id.add);
        mSub = findViewById(R.id.sub);
        mBind.setOnClickListener(this);
        mUnbind.setOnClickListener(this);
        mAdd.setOnClickListener(this);
        mSub.setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {
        if(v == mBind){//绑定服务
            Intent service = new Intent("com.aidl.test");
            bindService(service , conn , BIND_AUTO_CREATE);
        }else if(v == mUnbind){
            unbindService(conn);//解绑
        }else if(v == mAdd){
            try {
                int addResult = math.add(11, 99);
                Toast.makeText(getApplicationContext(), addResult+"", 0).show();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }else if(v == mSub){
            try {
                int subResult = math.sub(99, 11);
                Toast.makeText(getApplicationContext(), subResult+"", 0).show();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

}

当项目中有很多业务需要使用aidl来进行跨进程通信的时候,我们就按照基本流程一个一个实现吗?方法肯定是可以的,但是我们会创建很多service,势必会占用很多的资源,针对这个问题,我们要减少service的数量,将所有的aidl放在一个service中去管理,即binder连接池:

每个业务模块创建创建自己的aidl接口,然后向服务端提供自己的唯一标识和其对应的binder对象,对于服务端,只需要一个service就可以了,服务端提供一个querybinder接口,这个接口能够根据业务模块的特征来返回相应的binder对象,不同的业务模块拿到所需要的binder对象后就可以进行远程方法的调用了。

这里写图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值