乐趣区

关于android:阿里面试官Android中binder机制的实现原理及过程

Binder 是 Android 零碎中十分重要的组成部分。Android 零碎中的许多性能建设在 Binder 机制之上。在这篇文章中,咱们会对 Android 中的 Binder 在零碎架构中的作用进行剖析;而后,咱们会从底层的实现角度简要阐明为什么 Android 要开发出一套独立的跨过程通信机制;最初,咱们会给出一个 AIDL 的应用示例来阐明如何应用 Binder 来进行通信。

1、什么是 Binder?为什么说它对 Android 零碎至关重要?

“什么是 Binder?为什么说它对 Android 零碎至关重要?”在答复这个问题之前,咱们先来说下其余的货色。

不晓得你有没有思考过这么一个问题:为什么当咱们在 Android 中启动一个页面的时候须要调用 startActivity() 办法,而后还要传入一个 Intent?如果咱们不应用这种传递值的形式,间接写成动态的变量有没有问题?这也是之前有人问过我的一个问题。

对下面的两个问题,咱们先答复第二个。应用动态的变量传递值在大部分状况下是能够的,当然要留神在应用完了值之后要及时开释资源,不然会占用太多内存,甚至 OOM. 然而,在非凡的状况下它是无奈实用的,即跨过程的状况下。这是因为,动态的变量的作用范畴只是其所在的过程,在其余过程拜访的时候属于跨过程拜访,当然拜访不到了。对于第一个问题,Android 中的一个 Activity 的启动过程远比咱们设想的简单,其中就波及跨过程的通信过程。当咱们调用 startActivity() 办法之后,咱们的所有的“用意”会通过层层过滤,直到一个称之为 AMS 的中央被解决。解决完之后,再跨过程调用你启动页面时的过程进行后续解决,即回调 onCreate() 等生命周期办法。

一个 Activity 的启动过程波及 Android 中两种重要的通信机制,Binder 和 Handler,咱们会在当前的文章中对此进行剖析。

上面咱们通过一个简略的图来阐明一下 Activity 的启动过程:

当咱们调用 startActivity() 办法的时候,首先会从 ServiceManager 中获取到 ActivityManagerService(就是 AMS),而后将 ApplicationThread 作为参数传递给 AMS,而后执行 AMS 的办法来启动 Activity.(在咱们的利用过程中执行另一个过程的办法。)

AMS 是全局的,在系统启动的时候被启动。当咱们应用它的时候从 ServiceManager 中获取这个全局的变量即可。当咱们调用它的办法的时候,办法具体的执行逻辑将在零碎的过程中执行。咱们传入的 ApplicationThread 就像一个信使一样。当 AMS 处理完毕,决定回调 Activity 的生命周期办法的时候,就间接调用 ApplicationThread 的办法(这是在另一个过程中调用咱们的利用过程)。这样就实现了咱们的 Activity 的生命周期的回调。

看了下面的过程,兴许有的同学会感觉。Binder 对 Android 零碎至关重要,然而咱们并没有用到 Binder 啊。实际上,咱们只是没有间接应用 Binder. 以下图为例,咱们说下咱们理论开发过程中是如何应用 Binder 的。

在大多数状况下,咱们都在与各个 Manager 进行交互,而实际上这些 Manager 外部是应用 Binder 来进行跨过程通信的。如上所示,当咱们调用 Manager 的时候,Manager 会通过代理类来从 Binder 驱动中失去另一个过程的 Stub 对象,而后咱们应用该 Stub 对象,近程调用另一个过程的办法。只是这个过程被封装了,咱们没有感知到而已,而这个跨过程通信 (IPC) 的机制就是 Binder 机制。

至于什么是 Stub 呢?Stub 是 AIDL 标准中的一部分。AIDL 为咱们应用 Binder 提供了一套模板。在 Android 零碎中大量应用了这种定义来实现跨过程通信。稍后咱们介绍 AIDL 的时候,你将看到它是如何作用的。

2、为什么是 Binder 而不是其余通信机制?

Android 是基于 Linux 的,Linux 自身曾经具备了许多的 IPC 机制,比方:管道(Pipe)、信号(Signal)和跟踪(Trace)、插口(Socket)、音讯队列(Message)、共享内存(Share Memory)和信号量(Semaphore)。那么,为什么 Android 要特立独行地搞出一套 IPC 机制呢?这当然是有起因的:

  1. 效率上:Socket 作为一款通用接口,其传输效率低,开销大,次要用在跨网络的过程间通信和本机上过程间的低速通信。音讯队列和管道采纳存储 - 转发形式,即数据先从发送方缓存区拷贝到内核开拓的缓存区中,而后再从内核缓存区拷贝到接管方缓存区,至多有两次拷贝过程。共享内存尽管无需拷贝,但管制简单,难以使用。Binder 只须要一次数据拷贝,性能上仅次于共享内存
  2. 稳定性:Binder 基于 C|S 架构,客户端(Client)有什么需要就丢给服务端(Server)去实现,架构清晰、职责明确又互相独立,天然稳定性更好。 共享内存尽管无需拷贝,然而管制负责,难以使用。从稳定性的角度讲,Binder 机制是优于内存共享的。
  3. 安全性:Binder 通过在内核层为客户端增加身份标记 UID|PID,来作为身份校验的标记,保障了通信的安全性。 传统 IPC 拜访接入点是凋谢的,无奈建设公有通道。比方,命名管道的名称,SystemV 的键值,Socket 的 ip 地址或文件名都是凋谢的,只有晓得这些接入点的程序都能够和对端建设连贯,不管怎样都无奈阻止恶意程序通过猜想接管方地址取得连贯。

除了下面的起因之外,Binder 还领有许多其余的个性,比方:1). 采纳援用计数,当某个 Binder 不再被任何客户端援用的时候,会告诉它的持有者能够将其开释,这实用于 Android 这种经常因为资源有余而回收资源的利用场景。2). 它外部保护了一个线程池;3). 能够像触发本地办法一样触发近程的办法。4). 反对同步和异步 (oneway) 的触发模型;5). 能够应用 AIDL 模板进行形容和开发。

3、Binder 模型,Binder 中的 4 个次要角色

在 Binder 模型中共有 4 个次要角色,它们别离是:Client、Server、Binder 驱动和 ServiceManager. Binder 的整体构造是基于 C|S 构造的,以咱们启动 Activity 的过程为例,每个利用都会与 AMS 进行交互,当它们拿到了 AMS 的 Binder 之后就像是拿到了网络接口一样能够进行拜访。如果咱们将 Binder 和网络的拜访过程进行类比,那么 Server 就是服务器,Client 是客户终端,ServiceManager 是域名服务器(DNS),驱动是路由器。其中 Server、Client 和 ServiceManager 运行于用户空间,驱动运行于内核空间

当咱们的系统启动的时候,会在启动 SystemServer 过程的时候启动各个服务,也包含下面的 AMS. 它们会被放进一个哈希表中,并且哈希表的键是字符串。这样咱们就能够通过服务的字符串名称来找到对应的服务。这些服务就是一个个的 Binder 实体,对于 AMS 而言,也就是 IActivityManager.Stub 实例。这些服务被启动的之后就像网络中的服务器一样始终期待用户的拜访。

对于这里的 ServiceManager,它也是一种服务,然而它比拟非凡,它会在所有其余的服务之前被注册,并且只被注册一次。它的作用是用来依据字符串的名称从哈希表中查找服务,以及在系统启动的时候向哈希表中注册服务。

所以,咱们能够应用下面的这张图来形容整个 Binder 模型:首先,在零碎会将应用程序所需的各种服务通过 Binder 驱动注册到零碎中(ServiceManager 先被注册,之后其余服务再通过 ServiceManager 进行注册),而后当某个客户端须要应用某个服务的时候,也须要与 Binder 驱动进行交互,Binder 会通过服务的名称到 ServiceManager 中查找指定的服务,并将其返回给客户端程序进行应用。

4、Binder 的原理

下面咱们梳理了 Binder 的模型,以及为什么零碎设计一套通信机制的起因。那么你是否也好奇神乎其神的 Binder 到底是怎么实现的呢?这里咱们来梳理下 Binder 外部实现的原理。

首先,Binder 的实现过程是非常复杂的,在《Android 零碎源码情景剖析》一书中有 200 页的篇幅都在讲 Binder. 在这里咱们不算具体地解说它的具体的实现原理,咱们只对其中局部内容做简略的剖析,并且不心愿波及大量的代码。

4.1 inder 相干的零碎源码的构造

而后,咱们须要介绍下 Binder 相干的外围类在源码中的地位,

-framework
    |--base
        |--core
            |--java--android--os  
                              |--IInterface.java
                              |--IBinder.java
                              |--Parcel.java
                              |-- IServiceManager.java
                              |--ServiceManager.java
                              |--ServiceManagerNative.java
                              |--Binder.java  
            |--jni
                |--android_os_Parcel.cpp
                |--AndroidRuntime.cpp
                |--android_util_Binder.cpp
    |--native
        |--libs--binder         
                  |--IServiceManager.cpp
                  |--BpBinder.cpp
                  |--Binder.cpp             // Binder 的具体实现
                  |--IPCThreadState.cpp
                  |--ProcessState.cpp
        |--include--binder                  // 次要是一些头文件
                      |--IServiceManager.h
                      |--IInterface.h
        |--cmds--servicemanager
                    |--service_manager.c    // 用来注册服务的 ServiceManager
                    |--binder.c
-kernel-drivers-staging-android
                         |--binder.c        
                         |--uapi-binder.h

4.2 Binder 实现过程中至关重要的几个函数

当咱们查看 binder.c 的源码的时候,或者查看与 Binder 相干的操作的时候,常常看到几个操作 ioctl, mmap 和 open. 那么这几个操作符是什么含意呢?

首先,open 函数用来关上文件的操作符,在应用的时候须要引入头文件,#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>,其函数定义如下,

int open(const char * pathname, int flags);
int open(const char * pathname, int flags, mode_t mode);

这里的 pathname 示意文件门路;flag 示意打开方式;mode 示意关上的模式和权限等;若所有欲核查的权限都通过了查看则返回 0, 示意胜利, 只有有一个权限被禁止则返回 -1.

而后是 ioctl 指令,应用的时候须要引入 #include <sys/ioctl.h> 头文件,ioctl 是设施驱动程序中对设施的 I/O 通道进行治理的函数,用于向设施发管制和配置命令。其函数定义如下:

int ioctl(int fd, ind cmd, …);

其中 fd 是用户程序关上设施时应用 open 函数返回的文件标示符,cmd 是用户程序对设施的管制命令,至于前面的省略号,那是一些补充参数,个别最多一个,这个参数的有无和 cmd 的意义相干。

最初是 mmap 函数,它用来实现内存映射。应用的时候须要引入头文件 #include <sys/mman.h>. 与之对应的还有 munmap 函数。它们的函数定义如下,

void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);
int munmap(void* start,size_t length);

这里的参数的含意是:

  1. start:映射区的开始地址,设置为 0 时示意由零碎决定映射区的起始地址;
  2. length:映射区的长度。长度单位是以字节为单位,有余一内存页按一内存页解决;
  3. prot:冀望的内存保护标记,不能与文件的关上模式抵触。是以下的某个值,能够通过 o r 运算正当地组合在一起;
  4. flags:指定映射对象的类型,映射选项和映射页是否能够共享。它的值能够是一个或者多个以下位的组合体;
  5. fd:无效的文件形容词。个别是由 open() 函数返回,其值也能够设置为 -1,此时须要指定 flags 参数中的 MAP_ANON, 表明进行的是匿名映射;
  6. off_toffset:被映射对象内容的终点。

胜利执行时,mmap() 返回被映射区的指针,munmap() 返回 0。失败时,mmap() 返回 MAP_FAILED[其值为(void *)-1],munmap() 返回 -1.

4.3 ServiceManger 启动

Binder 中的 ServiceManager 并非 Java 层的 ServiceManager,而是 Native 层的。启动 ServiceManager 由 init 过程通过解析 init.rc 文件而创立。启动的时候会找到上述源码目录中的 service_manager.c 文件中,并调用它的 main() 办法,

// platform/framework/native/cmds/servicemanager.c
int main(int argc, char** argv)
{
    struct binder_state *bs;
    char *driver;

    if (argc > 1) {driver = argv[1];
    } else {driver = "/dev/binder";}
    // 1\. 关上 binder 驱动
    bs = binder_open(driver, 128*1024);
    // ...
    // 2\. 将以后的 ServiceManger 设置成上下文
    if (binder_become_context_manager(bs)) {return -1;}
    // ...
    // 3\. 启动 binder 循环,进入一直监听状态
    binder_loop(bs, svcmgr_handler);
    return 0;
}

ServcieManager 启动的过程就是下面三个步骤,无需过多阐明。上面咱们给出这三个办法具体实现的。在上面的代码中你将看到咱们之前介绍的三个函数的理论利用。相应有了后面的铺垫之后你了解起来不成问题 :)

// platform/framework/native/cmds/servicemanager.c
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;
    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;
        return NULL;
    }
    // 关上设施驱动
    bs->fd = open(driver, O_RDWR | O_CLOEXEC);
    if (bs->fd < 0) {goto fail_open;}
    // 向驱动发送指令,获取 binder 版本信息
    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {goto fail_open;}
    bs->mapsize = mapsize;
    // 通过零碎调用,mmap 内存映射,mmap 必须是 page 的整数倍
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    if (bs->mapped == MAP_FAILED) {goto fail_map;}
    return bs;
fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return NULL;
}

在下面的代码中,先应用 open() 函数关上设施驱动(就是一个关上文件的操作),而后应用 ioctl() 函数向下面的设施驱动发送指令以获取设施信息。最初,通过 mmap() 函数实现内存映射,并将上述的文件描述符传入。这里的 binder_state 是一个构造体,定义如下。其实就是用来形容 binder 的状态。从下面咱们也能看到它的三个变量的赋值过程。

// platform/framework/native/cmds/servicemanager.c
struct binder_state
{
    int fd;
    void *mapped;
    size_t mapsize;
};

当然,在下面的代码中,咱们又见到了久违的 goto 指令。它们次要用来解决产生一些异样的状况。

关上了驱动之后,注册为上下文的办法更加简略,

// platform/framework/native/cmds/servicemanager.c
int binder_become_context_manager(struct binder_state *bs)
{return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
复制代码

就是一个 ioctl 函数,应用指令 BINDER_SET_CONTEXT_MGR 将以后的 ServiceManager 注册为上下文。

最初就是启动 Binder 循环了。它的逻辑也没有设想中得简单,就是启动了 for 循环,

// platform/framework/native/cmds/servicemanager.c
void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;
    uint32_t readbuf[32];

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;

    readbuf[0] = BC_ENTER_LOOPER;
    // 将 BC_ENTER_LOOPER 命令发送给 binder 驱动,外部调用 ioctl 函数
    binder_write(bs, readbuf, sizeof(uint32_t));

    for (;;) {bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;
        // 应用 iotcl 函数读取
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
        if (res < 0) {break;}
        // 解析
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
        if (res == 0) {break;}
        if (res < 0) {break;}
    }
}

从下面看出,函数将会在 binder_write() 中将命令发送给 Binder 驱动,以启动循环。其实外部也是调用 ioctl 函数实现的。而后程序会启动一个循环来一直读取、解析。这是服务器很典型的操作了。

当然,咱们下面剖析的是 ServiceManager 中向 Binder 写命令的过程,而驱动如何解析呢?当然是在驱动中实现了,具体的过程能够查看 Binder 驱动局部的源码。

4.4 Binder 的跨过程通信过程

上面咱们以 AMS 作为例子来解说下 Binder 跨过程通信的实现过程。首先,当咱们调用 startActivity() 办法的时候,最终将会进入 ActivityManager 以获取 AMS,

    // platform/framework/base/core/java/android/app/ActivityManager.java
    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
    final IActivityManager am = IActivityManager.Stub.asInterface(b);
    return am;

这里会应用 ServiceManger 来按名称查找 AMS,查找到 Binder 对象之后将其转换成 AMS 就能够应用了。之前,咱们也说过用来查找 AMS 的 SeerviceManager 自身也是一种服务。所以,它这里的办法也是通过 Binder 来实现的。那么,咱们就从这里的 getService() 办法动手。

    // platform/framework/base/core/java/android/os/ServiceManager.java
    public static IBinder getService(String name) {
        try {IBinder service = sCache.get(name);
            if (service != null) {return service;} else {return Binder.allowBlocking(rawGetService(name));
            }
        } catch (RemoteException e) {/* ... */}
        return null;
    }

这里会先尝试从缓存当中取 Binder,取不到的话就从近程进行获取。这里应用 rawGetService() 办法来从近程获取 Binder,代码如下,

    // platform/framework/base/core/java/android/os/ServiceManager.java
    private static IBinder rawGetService(String name) throws RemoteException {final IBinder binder = getIServiceManager().getService(name);
        // ...        
        return binder;
    }

    // platform/framework/base/core/java/android/os/ServiceManager.java
    private static IServiceManager getIServiceManager() {if (sServiceManager != null) {return sServiceManager;}
        sServiceManager = ServiceManagerNative
                .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
        return sServiceManager;
    }

rawGetService() 办法中会应用 ServiceManagerNativegetService() 办法从近程获取 Binder. 这里的 ServiceManagerNative 实质上只是一个代理类,它理论的逻辑是由 BinderInternal.getContextObject() 返回的 Binder 实现的。

兴许你曾经晕了,怎么那么多 Binder……我来阐明下。当要查找 AMS 的时候实际上是一个跨过程的调用过程,也就是理论的查找的逻辑是在另一个过程实现,因而须要 Binder 来通信。而查找 AMS 的近程对象实际上就是咱们下面所说的 ServiceManager (Native 层的而不是 Java 层的,Java 层的 ServiceManager 是一个代理类,是用来从近程获取服务的)。

因而,依照下面的形容,BinderInternal.getContextObject() 返回的就应该是近程的 Binder 对象。于是办法进入 Native 层,

// platform/framework/base/core/jni/android_util_Binder.cpp
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    return javaObjectForIBinder(env, b);
}

这里的 ProcessState::self() 是否相熟呢?你是否还记得在上一篇文章中,咱们介绍 Android 系统启动过程的时候介绍过它。咱们已经应用它来开启 Binder 的线程池。这里的 self() 办法其实是用来获取一个单例对象的。咱们能够间接由 getContextObject() 进入 getStrongProxyForHandle() 办法。从上面的办法中咱们能够看出,这里调用了 BpBindercreate() 办法创立了一个 BpBinder 实例并返回,也就是咱们的 ServiceManager.

// plaftorm/framework/native/libs/binder/ProcessState.cpp
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;
    AutoMutex _l(mLock);
    handle_entry* e = lookupHandleLocked(handle);
    if (e != nullptr) {
        IBinder* b = e->binder;
        if (b == nullptr || !e->refs->attemptIncWeak(this)) {
            // ...
            // 调用 BpBinder
            b = BpBinder::create(handle);
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {result.force_set(b);
            e->refs->decWeak(this);
        }
    }

当咱们拿到了 ServiceManager 的 Binder 之后就能够调用它的 getService() 办法来获取服务了,

    // platform/framework/base/core/java/android/os/ServiceManagerNative.java
    public IBinder getService(String name) throws RemoteException {Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IServiceManager.descriptor);
        data.writeString(name);
        mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
        IBinder binder = reply.readStrongBinder();
        reply.recycle();
        data.recycle();
        return binder;
    }

这里的 mRemote 就是之前返回的 BpBinder,这里调用它的 transact() 办法,并传入了一个办法标记 GET_SERVICE_TRANSACTION.

// platform/framework/native/libs/binder/BpBinder.cpp
status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{if (mAlive) {status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }
    return DEAD_OBJECT;
}

显然这里会调用 IPCThreadState 的 self() 办法先获取一个单例的对象,而后调用它的 transact() 办法持续办法的执行。

// platform/framework/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::transact(int32_t handle, uint32_t code, 
    const Parcel& data, Parcel* reply, uint32_t flags)
{
    status_t err;
    // ...
    err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);
    // ...
    if ((flags & TF_ONE_WAY) == 0) { // OneWay 类型的调用,同步的
        // ...
        if (reply) {
            // 期待相应
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        IF_LOG_TRANSACTIONS() {TextOutput::Bundle _b(alog);
            if (reply) alog << indent << *reply << dedent << endl;
            else alog << "(none requested)" << endl;
        }
    } else { // 异步的
        err = waitForResponse(nullptr, nullptr);
    }
    return err;
}

下面会调用 writeTransactionData() 办法用来将数据写入到 Parcel 中。而后将会进入 waitForResponse() 办法解决与 ServiceManager 交互的后果。而实在的交互产生的中央位于 talkWithDriver() 办法,

// platform/framework/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::talkWithDriver(bool doReceive)
{if (mProcess->mDriverFD <= 0) {return -EBADF;}

    binder_write_read bwr;
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;

    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data();

    if (doReceive && needRead) {bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();} else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }

    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
        // 通过 ioctl 读写操作,与 Binder Driver 进行交互
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
        if (mProcess->mDriverFD <= 0) {err = -EBADF;}
    } while (err == -EINTR);
    // ...
    return err;
}

binder_write_read 构造体用来与 Binder 设施替换数据的构造, 通过 ioctl 与 mDriverFD 通信,是真正与 Binder 驱动进行数据读写交互的过程。先向 service manager 过程发送查问服务的申请(BR_TRANSACTION)。而后,service manager 会在之前开启的循环中监听到,并应用 svcmgr_handler() 办法进行解决。

// platform/framework/native/cmds/servicemanager.c
int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    // ...
    switch(txn->code) {
        case SVC_MGR_GET_SERVICE:
        case SVC_MGR_CHECK_SERVICE:
            s = bio_get_string16(msg, &len);
            if (s == NULL) {return -1;}
            handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
            if (!handle)
                break;
            bio_put_ref(reply, handle);
            return 0;
        case SVC_MGR_ADD_SERVICE: // ...
        case SVC_MGR_LIST_SERVICES: // ...
    }
    return 0;
}

显然,这里会从 binder_transaction_data 中取出 code,即 SVC_MGR_GET_SERVICE,而后应用 do_find_service() 办法查找服务。而后再 binder_send_reply() 应答发起者将后果返回即可。

4.5 Binder 高效通信的起因

下面咱们梳理了 Binder 通信的过程,从下面咱们仿佛并没有看到能证实 Binder 高效的证据。那么 Binder 到底靠什么实现高效的呢?

实际上,Binder 之所以高效,从咱们下面的代码还真看不出来。因为,咱们下面的代码并没有波及 Binder 驱动局部。正如咱们之前形容的那样,ServiceManager、客户端和服务器理论是靠 Binder 驱动这个两头媒介进行交互的。而 Binder 高效的中央就产生在 Binder 驱动局部。

就像图片形容的那样,当两个过程之间须要通信的时候,Binder 驱动会在两个过程之间建设两个映射关系:内核缓存区和内核中数据接管缓存区之间的映射关系,以及内核中数据接管缓存区和接管过程用户空间地址的映射关系。这样,当把数据从 1 个用户空间拷贝到内核缓冲区的时候,就相当于拷贝到了另一个用户空间中。这样只须要做一次拷贝,省去了内核中暂存这个步骤,晋升了一倍的性能。实现内存映射靠的就是下面的 mmap() 函数。

4、Binder 的应用

4.1 代理模式

Binder 实质上只是一种底层通信形式,和具体服务没有关系。为了提供具体服务,Server 必须提供一套接口函数以便 Client 通过近程拜访应用各种服务。这时通常采纳 代理设计模式:将接口函数定义在一个抽象类中,ServerClient 都会以该抽象类为基类实现所有接口函数。所不同的是 Server 端是真正的性能实现,而 Client 端是对这些函数近程调用申请的包装。为了简化这种设计模式,Android 中提供了 AIDL 供咱们应用。下文中咱们会介绍 AIDL 相干的内容以及它的一些根本的应用形式。

4.2 AIDL

AIDL (Android Interface Definition Language,Android 接口定义语言) 是一种文件格式,用来简化 Binder 的应用。当应用 Binder 的时候,只须要创立一个后缀名为 .aidl 的文件,而后像定义接口一样定义方法。定义结束之后,应用工具 aidl.exe 即可生成 Binder 所须要的各种文件。当然,咱们的 AS 曾经为咱们集成了 aidl.exe,所以,只须要在定义了 AIDL 文件之后,编译 即可生成应用 Binder 时所需的文件。当然,不应用 AIDL,间接编写 Binder 所需的 java 文件也是能够的。

AIDL 是一种接口定义语言,它与 Java 中定义接口的形式有所区别。上面咱们通过一个例子来阐明 AIDL 的应用形式。

这里咱们模仿一个笔记治理的类,通过在 Activity 中与一个近程的 Service 进行交互来实现 IPC 的成果。这里,咱们先要定义数据实体 Note,它只蕴含两个字段,并且实现了 Parcelable。这里 Note 所在的目录是 me.shouheng.advanced.aidl,而后,咱们须要在 src/main 建设一个同样的包门路,而后定义所需的 AIDL 文件:

    // INoteManager.aidl
    package me.shouheng.advanced.aidl;
    import me.shouheng.advanced.aidl.Note;
    interface INoteManager {Note getNote(long id);
        void addNote(long id, String name);
    }

    // Note.aidl
    package me.shouheng.advanced.aidl;
    parcelable Note;

留神,在 INoteManager 文件中,咱们定义了近程服务所需的各种办法。这里只定义了两个办法,一个用来获取指定 id 的笔记,一个用来向近程服务中增加一条笔记记录。

这样定义完了之后,咱们能够对我的项目进行编译,这样就能够 build 目录上面失去为咱们生成好的 INoteManager 类文件。当前,咱们就能够应用这个文件中生成类和办法来进行近程通信。但在应用该接口之前,咱们还是先来看一下其中都生成了些什么货色:

package me.shouheng.advanced.aidl;

public interface INoteManager extends android.os.IInterface {

    // 交给近程来实现具体的业务逻辑
    public static abstract class Stub extends android.os.Binder implements me.shouheng.advanced.aidl.INoteManager {

        private static final java.lang.String DESCRIPTOR = "me.shouheng.advanced.aidl.INoteManager";

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

        // 应用代理包装近程对象
        public static me.shouheng.advanced.aidl.INoteManager asInterface(android.os.IBinder obj) {if ((obj==null)) {return null;}
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof me.shouheng.advanced.aidl.INoteManager))) {return ((me.shouheng.advanced.aidl.INoteManager)iin);
            }
            // 返回代理对象
            return new me.shouheng.advanced.aidl.INoteManager.Stub.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 {switch (code) {
                case INTERFACE_TRANSACTION: {reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getNote: {data.enforceInterface(DESCRIPTOR);
                    long _arg0;
                    _arg0 = data.readLong();
                    // 应用模板办法来实现业务
                    me.shouheng.advanced.aidl.Note _result = this.getNote(_arg0);
                    reply.writeNoException();
                    if ((_result!=null)) {reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {reply.writeInt(0);
                    }
                    return true;
                }
                case TRANSACTION_addNote: {data.enforceInterface(DESCRIPTOR);
                    long _arg0;
                    _arg0 = data.readLong();
                    java.lang.String _arg1;
                    _arg1 = data.readString();
                    // 应用模板办法来实现业务
                    this.addNote(_arg0, _arg1);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        // 代理对象,包装了近程对象,外部调用近程对象获取近程的服务信息
        private static class Proxy implements me.shouheng.advanced.aidl.INoteManager {

            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 me.shouheng.advanced.aidl.Note getNote(long id) throws android.os.RemoteException {android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                me.shouheng.advanced.aidl.Note _result;
                try {_data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeLong(id);
                    // 理论外部调用近程对象,在另一个过程实现业务逻辑
                    mRemote.transact(Stub.TRANSACTION_getNote, _data, _reply, 0);
                    _reply.readException();
                    if ((0!=_reply.readInt())) {_result = me.shouheng.advanced.aidl.Note.CREATOR.createFromParcel(_reply);
                    } else {_result = null;}
                } finally {_reply.recycle();
                    _data.recycle();}
                return _result;
            }

            @Override
            public void addNote(long id, java.lang.String name) 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.writeLong(id);
                    _data.writeString(name);
                    // 理论外部调用近程对象,在另一个过程实现业务逻辑
                    mRemote.transact(Stub.TRANSACTION_addNote, _data, _reply, 0);
                    _reply.readException();} finally {_reply.recycle();
                    _data.recycle();}
            }
        }

        // 办法 id,用来标记以后调用的是哪个办法
        static final int TRANSACTION_getNote = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addNote = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public me.shouheng.advanced.aidl.Note getNote(long id) throws android.os.RemoteException;

    public void addNote(long id, java.lang.String name) throws android.os.RemoteException;
}

如果只是看这下面的生成的代码,兴许你依然无奈理解这些生成的类到底有什么作用。上面就让咱们通过应用下面生成的类来阐明 AIDL 的具体工作流程。

首先,咱们要定义近程的服务,并在该服务中实现业务逻辑:

public class NoteService extends Service {private CopyOnWriteArrayList<Note> notes = new CopyOnWriteArrayList<>();

    // 以后服务运行于另一个过程,这里实现业务逻辑
    private Binder binder = new INoteManager.Stub() {
        @Override
        public Note getNote(long id) {return Observable.fromIterable(notes).filter(note -> note.id == id).singleOrError().blockingGet();
        }

        @Override
        public void addNote(long id, String name) {notes.add(new Note(id, name));
        }
    };

    @Override
    public void onCreate() {super.onCreate();
        notes.add(new Note(100, "Note 100"));
        notes.add(new Note(101, "Note 101"));
    }

    // 将 binder 返回,客户端能够应用连贯来获取并调用
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {return binder;}
}

这里在 onCreate() 办法中创立了两条记录,并且创立了 INoteManager.Stub 的实例,并在 onBind() 办法中将其返回。而后,咱们在一个 Activity 中启动该近程服务,并尝试从该服务中获取指定 id 的笔记记录。从冀望的后果来看,它的性能有些相似于 ContentProvider,即用来向调用者提供数据。

上面是该 Activity 的实现。这里咱们在 onCreate() 办法中启动上述服务。并将实例化的 ServiceConnection 作为参数启动该服务。在 ServiceConnection 的办法中,咱们调用 INoteManager.StubasInterface(IBinder) 办法来讲 service 转换成 INoteManager,而后从其中获取指定 id 的笔记记录即可。

    // 创立服务连贯
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 返回代理对象
            INoteManager noteManager = INoteManager.Stub.asInterface(service);
            try {
                // 应用代理对象
                Note note = noteManager.getNote(100);
                LogUtils.d(note);
            } catch (RemoteException e) {e.printStackTrace();
            }
        }

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

    @Override
    protected void doCreateView(Bundle savedInstanceState) {Intent intent = new Intent(this, NoteService.class);
        // 绑定服务
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {super.onDestroy();
        // 解绑服务
        unbindService(connection);
    }
}

依据 INoteManager.StubasInterface() 办法的定义,该办法中会将传入的 service 包装成一个 INoteManager.Stub.Proxy 返回,所以,咱们在 onServiceConnected() 办法中理论调用的是该代理类的 getNote() 办法。而该代理类的 getNote() 办法中又调用了传入的 mRemote.transact() 办法。而这里的 service 正是咱们在 NoteService 中创立的 binder。也就是说,当咱们在 onServiceConnected() 中调用 getNote() 办法的时候,实际上调用了 INoteManager.Stubtransact() 办法。

所以,从下面咱们看出:

  1. 这里就像是在以后过程中调用了另一个过程的办法一样。这个调用的过程是通过 Binder 来实现的。
  2. 当调用 INoteManager.Stubtransact() 办法的时候,通过传入了一个整型的 code 来作为要触发的办法的标识,这就是咱们下面提到的办法的编号。

于是,咱们能够通过上面的这张图来总结在下面应用 AIDL 的过程中各局部表演的角色:

也就是客户端通过 Proxy 拜访 Binder 驱动,而后 Binder 驱动调用 Stub,而 Stub 中调用咱们的业务逻辑。这里的 ProxyStub 用来对立接口函数,Proxy 用来通知咱们近程服务中有哪些可用的办法,而具体的业务逻辑则由 Stub 来实现。Binder 的过程通信就产生在 ProxyStub 之间。

总结

这里只是从实现逻辑上简略介绍了下 Binder 机制的工作原理,想要深刻了解 Binder 机制,还得本人下功夫,看源码,只管这个过程很苦楚。一遍看不懂就再来一遍,跟着博客思路看了不下十遍。致力总会有播种的。如果你是短少学习材料,或者没有找不到重点思路,正好我这里有一份 阿里内部资料——《Android Binder 原理》,都是阿里一些大神级的人物整顿的。

敌人们如果有须要,能够我的【Github】浏览下载.

【点击我】收费获取。

退出移动版