IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Binder 机制简析 -> 正文阅读

[移动开发]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部分

 /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements MyAIDL {
        private static final String DESCRIPTOR = "com.example.testbinder.MyAIDL";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.example.testbinder.MyAIDL interface,
         * generating a proxy if needed.
         */
        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通信方式高效而且安全。
?

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-10-16 19:45:19  更:2021-10-16 19:46:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 23:37:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码