Android Framework -- Binder

App层的理解

Binder是Android提供的进程间通信的一种方式,性能相对更高效一些,适用于移动环境。我们把这个通信过程分为客户端Client进程(发送消息),服务端Server进程(接收消息),两个进程无法直接通信,当Client进程想调用Server进程的add函数并获取结果时,就通过Binder来实现。Binder会为Client提供一个Proxy(Binder也无法把真实的Server丢给Client),当Client调用Proxy.add时Binder就会去执行Server.add并把结果通过Proxy返回给Client。Binder底层是一个驱动。大致流程如下
这里写图片描述

  1. Client和Server都会向ServiceManager注册自己(或者说所有的Service都需要向SM注册自己)。
  2. 当Client进程【Server2】要向Server进程【Server1】发送消息时,他会向Binder去请求,我要访问0x1234 Server的add方法
  3. Binder根据0x1234向SM查询这个Server的信息
  4. Binder查找到这个Server并调用add方法拿到返回值
  5. 把返回值丢给Client

Binder通信篇

Binder的通信部分其实很简单,但源码中的业务封装太过复杂使得Binder看起来很复杂。抛开业务封装看下Binder通讯原理。ServiceManager就抛开了Binder的业务封装,直接使用它的通信功能。

首先我们知道,逻辑地址空间有4G,其中3G为用户空间,1G为内核空间;我们的应用进程都在用户空间中且相互隔绝无法直接互相访问。于是乎有了进程间通信的需求。Android基于Linux,Linux提供了很多进程间通信的方案:管道(Pipe)、信号(Signal)、消息队列(Message)、共享内存(Share Memory)、插口(Socket)等。而Android系统有自己独特的Binder进程间通信机制。Android源码中大量的使用Binder进行进程间通信(当然,有些地方也还是会用其他的进程间通信方式的)。Binder有很多优势,其中一个就是性能高,其他进程间通信基本上都需要进行两次的数据拷贝,而Binder只需要一次。
下面看看Binder通信的过程
这里写图片描述

1.每个进程(C端和S端都一样)在于Binder初次接触的时候,Binder都会为其建立一个物理地址映射,该进程在用户空间地址和Binder在内核空间的地址映射到一个物理内存上(如图:进程C和Binder进程K的地址映射到某物理内存上)。这样的映射在第一次接触的时候只会分配一个物理页大小(映射的最小值),后面在传输有效数据的时候会进行扩充,使用完毕立刻释放。
2.C端向S端传输有效数据的时候,C端需要向Binder驱动写入数据,数据包含Binder实体(即S端)的信息、有效数据在用户空间的地址等。Binder拿到数据后,先找到S端的信息,然后通过Binder与C端的映射(如图中的a)读取有效数据,然后拷贝到Binder与S端的映射(如图中的b)(一次数据拷贝);这个时候S端发现有数据之后就会通过映射去读取数据,完成一次进程间通信。

下面看下具体的ServiceManager注册到Binder成为Binder过程解析
init进程在启动各种服务的时候会启动SM服务,附上init.rc的相关部分
这里写图片描述
第一行:以服务的形式启动
第二行:以系统用户system的身份运行
第三行:critical表示SM是系统的关键服务(关键服务一旦退出会立刻重启,4分钟退出次数>4则系统重启)
第四、五行:如果SM重启则zygote、media也得重启
下面看下SM注册成为Binder管理的过程
这里写图片描述
第8行:打开Binder驱动,该动作同时会调用mmap分配一页的物理页来映射SM与Binder
第10行:binder_become_context_manager注册成为Binder的管理者,其实就是把自己的标记设置为0(每个服务在SM中注册都会有一个标记,客户端需要某服务会向SM查询就是查询到这个标记就可以通过Binder访问对方,而SM本身也是一个服务,它的服务标记为0)
第16行:等待客户端的请求

这个过程就是最简单的与Binder交流了。

PS:通常情况下,客户端与Binder交流略过业务逻辑部分最终会调用IPCThreadState来做通信,IPCThreadState负责对Binder的写入与读取,每个线程有且只有一个IPCThreadState。下面业务篇有具体描述

Binder业务篇

Binder对于业务层的封装非常繁杂,而对于应用程序来说接触的比较多的应该就是AIDL。
Binder的业务层从上至下包含诸多对象,Java层Binder、BinderProxy、BinderInternal;Native层BpBinder、BBinder、Bpxxx等等等…其实Java层基本上是Native的一个映射。在Java世界初创之时,会注册一些JNI函数,其中register_android_os_Binder函数会把Java层的Binder进行初始化工作。主要就是通过JNI把Binder、BinderProxy、BinderInternal分别保存在Native层的全局变量gBinderOffsets、gBinderProxyOffsets、gBinderIntenalOffsets中,并将三个类的Native初始化好。
这里写图片描述
接下来我们看一个小例子:AMS将自己注册到SM中
以及AMS如何响应请求
这里写图片描述
上图,蓝色部分表示Java层,黑色部分表示Native层。左侧是AMS将自身注册到SM,SM上面介绍过,直接读取Binder驱动数据,上图没画出来。右侧则是客户端向AMS发起请求后从Binder到S
端的过程,其他服务也差不多。另外,还有一些服务
直接在Native层发起请求或处理请求与Binder交互的。其过程其实类似,这边做下简单笔记,可跳过。

//MediaServer的注册
//①这里会做两件事:打开Binder驱动;mmap 分配一个物理页
sp<ProcessState> proce(self())
//②defaultServiceManager
sp<IServiceManager> sm = defaultServiceManager()
//③instantiate
instantiate()

defaultServiceManager做的事就是获得一个与SM通信的Bp代理
这里写图片描述
instantiate则利用sm进行注册
这里写图片描述

AIDL: Android:学习AIDL,这一篇文章就够了
步骤
编写Bean

public class Book implements Parcelable{
    private String name;
    private int price;
}

编写aidl文件(aidl代码客户端和服务端都需要一份)
Book.aidl

package com.lypeer.i[cclient
parcelable Book

BookManager.aidl

import com.lypeer.ipcclient.Book
interface BookManager{
    List< Book > getBooks();
    void addBook(in Book book); 
}

Make后生成Java代码
服务端代码

public class AIDLService extends Service {
    //包含Book对象的list
    private List<Book> mBooks = new ArrayList<>();

    //由AIDL文件生成的BookManager
    private final BookManager.Stub mBookManager = new BookManager.Stub() {
        @Override
        public List<Book> getBooks() throws RemoteException {
            synchronized (this) {
                    return mBooks;
            }
        }


        @Override
        public void addBook(Book book) throws RemoteException {
            synchronized (this) {
                if (!mBooks.contains(book)) {
                    mBooks.add(book);
                }
            }
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        Book book = new Book();
        book.setName("Android开发艺术探索");
        book.setPrice(28);
        mBooks.add(book);   
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBookManager;
    }
}

注册Service

<service
    android:name=".service.AIDLService"
    android:exported="true">
        <intent-filter>
            <action android:name="com.lypeer.aidl"/>
            <category android:name="android.intent.category.DEFAULT"/>
        </intent-filter>
</service>

客户端代码
连接服务端

private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBookManager = BookManager.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

然后

Intent intent = new Intent();
intent.setAction("com.lypeer.binder");
intent.setPackage("com.lypeer.ipcserver");
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);

最后通过调用mBookManager的方法来发起通信请求。


这里比较关键的就是BookManager.aidl生成的文件,它将生成一个文件但关系复杂大致是这样的(省去诸多东西,请当做伪代码来看)

public interface BookManager extends IInterface{

    public static abstract class Stub extends Binder implements BookManager{
        public static BookManager asInterface(IBinder obj){...}
        public static IBinder asBinder(){...}
        public boolean onTransact(int code,Parcel data,Parcel reply,int flags){...}
        private static class Proxy implements BookManager{
            private IBinder mRemote;
            public IBinder asBinder(){return mRemote;}
            public List<Book> getBooks(){
                ...
                mRemote.transact(...);
                ...
            }
            public void addBook(Book book){
                ...
                mRemote.transact(...);
                ...
            }
        }
    }
}

可以看到 主要的类有三个我们自己的BookManager接口 内部类Stub和Proxy
这里写图片描述
大致流程如上图所示,【Client进程视角】我们调用Stub的方法,Stub会先看是否在同一进程,在则直接转换,就没有Binder啥事了;不在则调用Proxy进行通信,Proxy的addBook和getBooks最终调用mRemote.transact()向Binder写数据(这部分前面详细解释过了)。接下来【Service进程视线】,Service也有一份一样的aidl代码自然也生成了相同的java代码,Service中主要是Stub这边会用onTransact()来接收Binder写给自己的数据调用addBook和getBooks并返回结果。

最后附两图
这里写图片描述
这里写图片描述

荐:Android跨进程通信:图文详解 Binder机制 原理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值