APP开发经验总结AndroidAndroid

从getSystemService()开始,开撸Binder通讯

2017-07-14  本文已影响2538人  CoorChice
image

Binder系列第一篇:《从getSystemService()开始,开撸Binder通讯机制》http://www.jianshu.com/p/1050ce12bc1e

Binder系列第二篇:《能用【白话文】来分析Binder通讯机制?》http://www.jianshu.com/p/fe816777f2cf

Binder系列第三篇:《Binder机制之一次响应的故事》http://www.jianshu.com/p/4fba927dce05

Binder系列第二篇已经上线!我为什么选择用【白话文】来分析Binder通讯机制?- http://www.jianshu.com/p/fe816777f2cf

始于getSystemService()

类似下面的代码,相信各位已经写了100遍了!

val accessibilityManager: AccessibilityManager =
        getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager

很简单,我们通过getSystemService()获取到了系统服务代理AccessibilityManager,我们通过应用内的AccessibilityManager可以系统中真正的AccessibilityManager互动。怎么做到的呢?我们慢慢来看,先从这个服务是怎么获取到的开始吧。

getSystemService()实质

我们平时调用getSystemService()实际获取到的是在SystemServiceRegistry中注册的各种系统服务的代理,它们大多在注册的时候会通过ServiceManager.getService()来取得和对应Service交互的IBinder对象,然后创建Service代理。

所以,ServiceManager.getService()应该才算是真正的起点吧。这个方法CoorChice这篇文章《3分钟看懂Activity启动流程》:http://www.jianshu.com/p/9ecea420eb52 中有提到过,在那里ActivityManagerNative通过ServiceManager.getService("Activtiy")获取到了和ActivityManager交互的IBinder对象,并使用它创建了应用程序的ActivityManagerProxy。就不多说,感兴趣的同学可以先看看那篇文章。

这是一张重要的图

image

还是从ServiceManager.getService()开始吧

上面这张图其实已经把整个流程描绘的很清楚了,流程不算长,接下来就根据这张图分析下。

图是从红色箭头开始看的,然后你可以跟着箭头一步一梳理这个流程,箭头上都有数字的!然后就是哪些个圈圈,相同颜色的表示同一个对象。So Sweet!

步骤1 开始创建IServiceManager

首先,既然是ServiceManager.getService()方法,那我们当然要毫不犹的直接看这个方法。

public static IBinder getService(String name) {
    try {
        // 先从缓存取
        IBinder service = sCache.get(name);
        if (service != null) {
            return service;
        } else {
            // 没有就去获取
            return getIServiceManager().getService(name);
        }
    } catch (RemoteException e) {
        Log.e(TAG, "error in getService", e);
    }
    return null;
}

可以看出,ServiceManager中会缓存用过的系统服务的IBinder。只有首次使用才去获取,非常科学。需要注意的是,是通过ServiceManager中的成员IServiceManager去获取的。

接下来,我们需要看看ServiceManager的获取过程。进入getIServiceManager()方法。

private static IServiceManager getIServiceManager() {
    if (sServiceManager != null) {
        //确保sServiceManager在进程中是单例
        return sServiceManager;
    }
    
    // 第一次会去获取sServiceManager
    sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
    return sServiceManager;
}

非常简单的一个方法。从中我们可以看出,IServiceManager在应用程序中是一个单例,它用于应用程序内管理Service,它实际是系统的ServiceManager的代理。

在首次调用这个方法时,自然是要创建IServiceManager。通过ServiceManagerNative.asInterface(BinderInternal.getContextObject())这么简单的一句创建了。假象!这一句隐藏了很多玄机。我们接着探索。

步骤2 获取参数IBinder

按照代码执行顺序来,我们先看ServiceManagerNative.asInterface()方法的参数BinderInternal.getContextObject()干了些什么?

手动档单击进入BinderInternal.java后可以看到,这是个native方法!

image
public static final native IBinder getContextObject();

WTF?我好慌,而且很害怕!没JNI层源码怎么办?

《AndroidXRed》http://androidxref.com一个神奇的网站!

这个网站可以查看Android从底层到上层的所有源码,并且支持多种搜索,基本上能实现手动挡单击跳转的伪效果。非常的屌!对于CoorChice这种没什么内存,不敢编译源码的低配电脑而言,简直是福音!

现在可以看源码了,CoorChice这里选择了Lollipop - 5.0.0_r2版本的源码。

image

从这里开始,你需要了解一些c/c++和JNI的知识。快复习一下吧!

步骤3 在JNI中看IBinder的获取过程

上面那个方法的JNI是在这个文件中的/frameworks/base/core/jni/android_util_Binder.cpp

//这些方法会通过动态注册的方式注册到BinderInternal.java中
static const JNINativeMethod gBinderInternalMethods[] = {
    //getContextObject()就是JNI函数android_os_BinderInternal_getContextObject()
    { "getContextObject", "()Landroid/os/IBinder;", (void*)android_os_BinderInternal_getContextObject },
    ...
};

如果我们要找getContextObject()这个名字的函数,你会发现找不见,因为这里采用了动态注册的方法来注册相关函数的。所以,我们会在上面的方法数组中发现一个字符串"getContextObject"。嗯,就是它了。它所对应的函数是android_os_BinderInternal_getContextObject()

static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
    //通过ProcessState单例获取handle句柄为0的IBinder
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    return javaObjectForIBinder(env, b);
}

这个函数很简单,但是暗藏玄机+1!通过ProcessState获取到了一个IBinder,并转换成Java层的IBinder返回给Java层。

接下来看看是怎么通过ProcessState获取到这个IBinder的。

步骤4 获取ProcessState的函数self()

首先,从ProcessState::self()看来,ProcessState应该是个单例。我们到self()里验证下。

sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) {
        // 有就直接返回
        return gProcess;
    }
    //没有就创建一个ProcessState
    gProcess = new ProcessState;
    return gProcess;
}

很简短的单例代码,首次调用直接new。

下面看看ProcessState对象是怎么被构造的。

步骤5 ProcessState的创建

ProcessState::ProcessState()
     //打开“/dev/binder”Binder驱动文件,将设备文件描述符保存在mDriverFD中
    : mDriverFD(open_driver())
    , mVMStart(MAP_FAILED)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
{
    ...
}

主要是初始化成员变量。其中mDriverFD(open_driver())是比较关键的一个环节,需要单独说一说。它通过调用open_driver()函数,打开了dev/binder驱动文件,并将文件描述符赋值给mDriverFD。那么,ProcessState作为持有Binder驱动文件描述符的对象,就拥有了和Binder驱动产生联系的资格。

下面简单看一下open_driver()函数。

步骤6 简要分析open_driver()函数

static int open_driver()
{
    //打开"/dev/binder"Binder驱动文件,并获得其描述符
    int fd = open("/dev/binder", O_RDWR);
    ...
    //获取Binder驱动程序的版本号
    status_t result = ioctl(fd, BINDER_VERSION, &vers);
    ...
    size_t maxThreads = 15;
    //告知驱动程序最多可以多少条线程处理事务
    result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
    ...
    return fd;
}

这个函数主要是负责打开/dev/binder文件,并配置一些和Binder驱动交互的参数,最后把获得的Binder驱动文件描述符fd返回。由于ProcessState在应用程序中是单例,所以保证了一个应用程序只打开一次/dev/binder

至于具体是怎么打开/dev/binder文件的,后面在详细说明。这里下跳过看下一步。

步骤7 通过getContextObject()获取的IBinder是什么?

ProcessState获取到后,就要调用它的getContextObject()函数来获取到一个IBinder对象了。

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{ 
    //获取handle句柄为0的Binder
    return getStrongProxyForHandle(0);
}

这个函数看起来是直接用来获取handle为0的IBinder的。这个为handle为0的IBinder代表着谁呢?对,你可能没猜到!它代表了系统的ServiceManager。也就是通过这个handle为0的IBinder可以和ServiceManager交互。也就是说,这个IBinder是Client用来和系统的ServiceManager交互的。这里也先不提了。

步骤8 ProcessState管理着IBinder

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;
    
    ...
    //尝试获取handle对应的handle_entry对象,没有的话会创建一个
    handle_entry* e = lookupHandleLocked(handle);
    
    if (e != NULL) {
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            // 上面的判断确保了同一个handle不会重复创建新的BpBinder
            ...
            //创建一个BpBinder
           //handle为0时创建的是ServiceManager对应的BpBinder
            b = new BpBinder(handle);
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;  //待会儿返回b
        }
        ...
    }
    
    return result;
}

这段代码流程有点长啊,虽然CoorChice已经去掉了很大一部分了。

image

这里会调用lookupHandleLocked()函数来获取一个handle_entry类型的指针。我们还是先看看这个函数吧。

ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
{
    //mHandleToObject是一个Vector,用于存放handle_entry结构体
    //该结构体会保存handle对应的IBinder指针
    const size_t N=mHandleToObject.size();
    if (N <= (size_t)handle) {
        //没有handle对应的handle_entry就新建一个插入mHandleToObject中
        //此时handle_entry还没有数据
        handle_entry e;
        e.binder = NULL;
        e.refs = NULL;
        status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
        if (err < NO_ERROR) return NULL;
    }
     //取出mHandleToObject中handle对应的handle_entry
    return &mHandleToObject.editItemAt(handle);
   
}

很短的函数,并且注释已经足够清晰了吧?不多说了,回到getStrongProxyForHandle()函数中。

在根据传入的handle(我们分析这个流程中handle为0)获取到对应的handle_entry后,会检查其binder成员是否存在,不存在则创建,存在直接返回。

可以看到,返回的IBinder实际是一个BpBinder类型的。这个东西也后面再说,总之它与Client发送数据到Service有关。

到此,ServiceManagerNative.asInterface()函数就成功获取到了一个IBinder对象作为参数。下面看看它用这个参数干了啥?

步骤9、10 创建ServiceManagerProxy

拿到IBinder对象后,我们回到Java中,进入ServiceManagerNative.asInterface()方法。你可以注意看一下上图中的红色圈,看看这个IBinder

static public IServiceManager asInterface(IBinder obj)
{
    ...
    //创建一个ServiceManagerProxy
    return new ServiceManagerProxy(obj);
}

反正最后就是使用刚刚获取到的IBinder创建了一个ServiceManagerProxy代理,用来在应用程序内代表系统的ServiceManager,并通过它和系统的ServiceManager交互。

public ServiceManagerProxy(IBinder remote) {
        mRemote = remote;
}

可以看到,ServiceManagerProxy的构造函数十分简单。注意,ServiceManagerProxy是ServiceManagerNative的一个内部类。

实际上这个模式在CoorChice这篇文章《3分钟看懂Activity启动流程》:http://www.jianshu.com/p/9ecea420eb52中也是出现过的。

好了,既然拿到代理ServiceManagerProxy了,我们该看看getService()方法是如何获取到相应的Service的IBinder的。

步骤11 通过IPC获取对应Service的IBinder

public IBinder getService(String name) throws RemoteException {
    // 准备数据包
    Parcel data = Parcel.obtain(); //用于发送的数据包
    Parcel reply = Parcel.obtain(); //用于响应的数据包
    data.writeInterfaceToken(IServiceManager.descriptor);
    //将需要的Service的名字放到数据包中
    data.writeString(name);
    //通过远程的Binder发送data到Binder驱动,并将响应保存到reply中
    mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
    //从响应结果中获取可用于和Service交互的Binder
    IBinder binder = reply.readStrongBinder();
    reply.recycle();
    data.recycle();
    return binder;
}

在这个方法中,创建了两个Parcel数据包。data是用来发送数据的,reply是用来接收数据的。

注意mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0)这里调用了刚刚从底层返回的IBinder对象的transact()方法,意思是要发送数据了,并且把reply一起传过去准备接收数据。这个过程是阻塞式的。接着,当远程Service(这里指的是系统的ServiceManager)响应后,代码就可以接着走了,而这时reply中已经装了响应结果了。我们调用Parcel的readStrongBinder()方法来取出来自Service响应的IBinder对象。然后我们拿着这个IBinder对象就可以和对应的系统Service通过IPC机制交互了。

总结

  • 抽出空余时间写文章分享需要动力,还请各位看官动动小手点个赞,给我点鼓励😄
  • 我一直在不定期的创作新的干货,想要上车只需进到我的【个人主页】点个关注就好了哦。发车喽~

嗯,这篇文章从getSystemService()方法开始撸了系统Service的获取过程。当然这只是个开始,CoorChice留了很多坑,比如open("/dev/binder", O_RDWR)发生了什么,IBinder.transact()又发生了什么?还有IBinder是个啥?Parcel又是如何发挥作用的?等等一些列的谜团,后面的文章中CoorChice会一一拨开它们的迷雾。这只是个开始。

image

大家可以先对着图捋一捋。

功力有限,有错还请指出一起交流交流。

看到这里的童鞋快奖励自己一口辣条吧!

想要看CoorChice的更多文章,请点个关注哦!

上一篇下一篇

猜你喜欢

热点阅读