栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Binder 机制简析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Binder 机制简析

Binder概念

1.从机制的角度上说,Binder是一种Android中实现的跨进程通信(IPC)的方式。

2.从组成上,Binder是一种虚拟的物理设备驱动,存放在/dev/binder  路径下面

3.从代码层角度,Binder是一个类,实现了IBinder接口(java层)

Binder的作用

1.在Android中实现跨进程通信。

2.连接Service进程,Client进程和ServiceManager进程。

3.将Binder机制模型以代码的形式实现在Android系统中。

为什么采取Binder通信机制呢?

我们先来看看传统的linux跨进程通信机制都有哪些

1.无名pipe和消息队列,这个采用的是存储转发的方式,至少需要拷贝两次才行,效率低

2.共享内存,传输过程中不需要拷贝,但是控制机制复杂(通信的过程中,需要获取对方的pid,需要用到多个机制才能实现)

3.socket,是一个通用的接口,导致其传输效率低,开销大

由于上面这些通信机制的弊端,Android对上面的机制进行借鉴和优化,研发出来一个新的机制Binder,这是一种C/S的通信模型,在内核中添加了身份标识PID和UID,安全性比较高,Binder还创建了私有管道,而Linux的访问接入点是开放的。

Binder通信的原理:

Binder机制进程间通信:是将数据写入驱动层这里会进行一次数据拷贝,这里是客户端完成的,在服务端需要创建一个服务,并进行注册,在注册的过程中会在驱动层建立映射关系,服务层跟驱动层可以直接相互获取数据,这里采用的是内存共享的方式进行。在交互的过程中,绑定了PID并通过方法ID的映射来进行调用,保证了安全性和更高的性能。

跨进程通信例子

下面借助AIDL来实现,AIDL是一个模板文件,协助生成一些重复的代码,创建一个MyAIDL文件

触发构建,Android Studio 自动生成一个MyAIDL java文件,如下图:

AIDL文件

interface MyAIDL {

    void setAddress(String address);

    String getAddress();

}

构建之后帮我们生成的AIDL java 文件,这里边重要的有两部分一个Stub一个是Proxt,其中Stub属于接收方,Proxy 属于发送方,下面把重点的内容进行解析,先来看Stub部分

 
    public static abstract class Stub extends android.os.Binder implements MyAIDL {
        private static final String DEscriptOR = "com.example.testbinder.MyAIDL";

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

        
        public static MyAIDL asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DEscriptOR);
            if (((iin != null) && (iin instanceof MyAIDL))) {
                return ((MyAIDL) iin);
            }
            return new 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 {
            String descriptor = DEscriptOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_setAddress: {
                    data.enforceInterface(descriptor);
                    String _arg0;
                    _arg0 = data.readString();
                    this.setAddress(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getAddress: {
                    data.enforceInterface(descriptor);
                    String _result = this.getAddress();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

在Binder机制里边,必然包含Stub部分,这个Stub名称可以随意更改,但是必须要继承Binder并实现MyAIDL这个接口

 this.attachInterface(this, DEscriptOR); 构造方法使用全类名字符串进行绑定

 android.os.IInterface iin = obj.queryLocalInterface(DEscriptOR);这里先从本地查找,如果

 能找到说明不是跨进程通信,直接将本地的Binder返回,如果没有找到说明是跨进程,直接创建

 一个。

 onTransact 这个是接收方,当调用transact之后触发这个方法。

下面来看Proxy部分

  private static class Proxy implements MyAIDL {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

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

            public String getInterfaceDescriptor() {
                return DEscriptOR;
            }

            @Override
            public void setAddress(String address) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DEscriptOR);
                    _data.writeString(address);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_setAddress, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().setAddress(address);
                        return;
                    }
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public String getAddress() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                String _result;
                try {
                    _data.writeInterfaceToken(DEscriptOR);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_getAddress, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        return getDefaultImpl().getAddress();
                    }
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            public static MyAIDL sDefaultImpl;
        }

这个里边分别是setAddress和getAddress的实现,这里边实现类似,得到两个普通缓存区_data和_reply ,设置DIP,并执行transact方法,这个方法的第一个参数是方法映射,这里的调用为了效率更高不是传输的方法名,是传的方法整形映射。在另外一段也有同样的方法映射,这样就能找到需要调用的方法。

另外进程间通信,必须依托于服务,创建一个Service

public class IPCService extends Service {
    public IPCService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.

        return new IPCBinder();
    }


    private class IPCBinder  extends MyAIDL.Stub {

        private String address;

        @Override
        public void setAddress(String address) throws RemoteException {
            this.address = address;
        }

        @Override
        public String getAddress() throws RemoteException {
            return address;
        }


    }
}

 主意声明IPCBinder 继承的是MyAIDL.Stub,这里是服务的提供方,这里提供的是什么,在客户端在绑定的时候得到的就是什么,如果不能直接继承Binder

创建另外一个App,来作为客户端

public class MainActivity extends AppCompatActivity {

    private MyAIDL mMyAIDL;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent = new Intent();
        intent.setAction("com.example.testbinder.IPCService");
        intent.setPackage("com.example.testbinder");

        bindService(intent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mMyAIDL = MyAIDL.Stub.asInterface(service);
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        }, Context.BIND_AUTO_CREATE);
    }


    public void startIPC(View view) {
        if (mMyAIDL != null) {
            try {
                mMyAIDL.setAddress("beijing");
                Toast.makeText(this, "" + mMyAIDL.getAddress(), Toast.LENGTH_SHORT).show();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
}

这样便可以模拟IPC进程间通信了,注意一定要将AIDL部分拷贝过来。

总结:Binder就是通过DIP来标识,并通过调用方法映射进行方法调用,transact是将数据写到驱动(此时还处于客户端段进程),并触发onTransact方法(此时就到了远端进程)。transact将数据写到驱动,进行了一次数据拷贝,注册的服务跟驱动层有映射关系可以相互获取数据,这里是通过共享内存的方式来实现不需要数据拷贝。这样整个Binder的IPC过程只进行了一次数据拷贝,并使用了内存共享的方式来进行通信,相对于传统的Linux通信方式高效而且安全。
 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/328925.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号