Android 开发高级面试题集合—— Binder 篇 <三>

Posted 清风Coolbreeze

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 开发高级面试题集合—— Binder 篇 <三>相关的知识,希望对你有一定的参考价值。

上一篇,我们介绍并详解了Binder的一次拷贝原理Binder传输数据的大小限制,如果还对这部分不理解的话,可以继续细读上篇。

这篇我们来讲一下东方集团和网易的高级面试题:

  • 面试题五:系统服务与bindService等启动的服务的区别(东方集团)
  • 面试题六:Binder线程、Binder主线程、Client请求线程的概念与区别(网易)

面试题五:系统服务与bindService等启动的服务的区别(东方集团)

服务可分为系统服务与普通服务,系统服务一般是在系统启动的时候,由SystemServer进程创建并注册到ServiceManager中的。

而普通服务一般是通过ActivityManagerService启动的服务,或者说通过四大组件中的Service组件启动的服务。这两种服务在实现跟使用上是有不同的,主要从以下几个方面:

  • 服务的启动方式
  • 服务的注册与管理
  • 服务的请求使用方式

首先看一下服务的启动上,系统服务一般都是SystemServer进程负责启动,比如AMS,WMS,PKMS,电源管理等,这些服务本身其实实现了Binder接口,作为Binder实体注册到ServiceManager中,被ServiceManager管理,而SystemServer进程里面会启动一些Binder线程,主要用于监听Client的请求,并分发给响应的服务实体类,可以看出,这些系统服务是位于SystemServer进程中(有例外,比如Media服务)。

在来看一下bindService类型的服务,这类服务一般是通过Activity的startService或者其他context的startService启动的,这里的Service组件只是个封装,主要的是里面Binder服务实体类,这个启动过程不是ServcieManager管理的,而是通过ActivityManagerService进行管理的,同Activity管理类似。

再来看一下服务的注册与管理:系统服务一般都是通过ServiceManager的addService进行注册的,这些服务一般都是需要拥有特定的权限才能注册到ServiceManager,而bindService启动的服务可以算是注册到ActivityManagerService,只不过ActivityManagerService管理服务的方式同ServiceManager不一样,而是采用了Activity的管理模型,详细的可以自行分析

最后看一下使用方式,使用系统服务一般都是通过ServiceManager的getService得到服务的句柄,这个过程其实就是去ServiceManager中查询注册系统服务。而bindService启动的服务,主要是去ActivityManagerService中去查找相应的Service组件,最终会将Service内部Binder的句柄传给Client。

面试题六:Binder线程、Binder主线程、Client请求线程的概念与区别(网易)

Binder线程是执行Binder服务的载体,只对于服务端才有意义,对请求端来说,是不需要考虑Binder线程的,但android系统的处理机制其实大部分是互为C/S的。比如APP与AMS进行交互的时候,都互为对方的C与S,这里先不讨论这个问题,先看Binder线程的概念。

Binder线程就是执行Binder实体业务的线程,一个普通线程如何才能成为Binder线程呢?很简单,只要开启一个监听Binder字符设备的Loop线程即可,在Android中有很多种方法,不过归根到底都是监听Binder,换成代码就是通过ioctl来进行监听。

拿ServerManager进程来说,其主线就是Binder线程,其做法是通过binder_loop实现不死线程:

void binder_loop(struct binder_state *bs, binder_handler func)
{
   ...
    for (;;) {
    <!--关键点1-->
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
     <!--关键点2-->
        res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
        。。
    }
}

上面的关键代码1就是阻塞监听客户端请求,2 就是处理请求,并且这是一个死循环,不退出。再来看SystemServer进程中的线程,在Android4.3(6.0以后打代码就不一样了)中SystemSever主线程便是Binder线程,同时一个Binder主线程,Binder线程与Binder主线程的区别是:线程是否可以终止Loop,不过目前启动的Binder线程都是无法退出的,其实可以全部看做是Binder主线程,其实现原理是,在SystemServer主线程执行到最后的时候,Loop监听Binder设备,变身死循环线程,关键代码如下:

extern "C" status_t system_init()
{
    ...
    ALOGI("System server: entering thread pool.\\n");
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
    ALOGI("System server: exiting thread pool.\\n");
    return NO_ERROR;
}

ProcessState::self()->startThreadPool()是新建一个Binder主线程,而PCThreadState::self()->joinThreadPool()是将当前线程变成Binder主线程。其实startThreadPool最终也会调用joinThreadPool,看下其关键函数:

void IPCThreadState::joinThreadPool(bool isMain)
{
    ...
    status_t result;
    do {
        int32_t cmd;
        ...关键点1 
        result = talkWithDriver();
        if (result >= NO_ERROR) {
           ...关键点2 
            result = executeCommand(cmd);
        }
        // 非主线程的可以退出
        if(result == TIMED_OUT && !isMain) {
            break;
        }
        // 死循环,不完结,调用了这个,就好比是开启了Binder监听循环,
    } while (result != -ECONNREFUSED && result != -EBADF);
 }

status_t IPCThreadState::talkWithDriver(bool doReceive)
{  
    do {
        ...关键点3 
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
   }   

先看关键点1 talkWithDriver,其实质还是去掉用ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)去不断的监听Binder字符设备,获取到Client传输的数据后,再通过executeCommand去执行相应的请求,joinThreadPool是普通线程化身Binder线程最常见的方式。不信,就再看一个MediaService,看一下main_mediaserver的main函数:

int main(int argc, char** argv)
{
   。。。
        sp<ProcessState> proc(ProcessState::self());
        sp<IServiceManager> sm = defaultServiceManager();
        ALOGI("ServiceManager: %p", sm.get());
        AudioFlinger::instantiate();
        MediaPlayerService::instantiate();
        CameraService::instantiate();
        AudioPolicyService::instantiate();
        registerExtensions();
        ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
    }

其实还是通过joinThreadPool变身Binder线程,至于是不是主线程,看一下下面的函数:

void IPCThreadState::joinThreadPool(bool isMain)

void ProcessState::spawnPooledThread(bool isMain)
{
    if (mThreadPoolStarted) {
        String8 name = makeBinderThreadName();
        ALOGV("Spawning new pooled thread, name=%s\\n", name.string());
        sp<Thread> t = new PoolThread(isMain);
        t->run(name.string());
    }
}

其实关键就是就是传递给joinThreadPool函数的isMain是否是true,不过是否是Binder主线程并没有什么用,因为源码中并没有为这两者的不同处理留入口,感兴趣可以去查看一下binder中的TIMED_OUT。

最后来看一下普通Client的binder请求线程,比如我们APP的主线程,在startActivity请求AMS的时候,APP的主线程成其实就是Binder请求线程,在进行Binder通信的过程中,Client的Binder请求线程会一直阻塞,知道Service处理完毕返回处理结果。

总结

好了,这篇就写到这里。如果觉得我写的不错的朋友或者很期待下次更新的朋友可以点个赞+关注哦!

PS:关于我


本人是一个拥有6年开发经验的帅气Android攻城狮,目前是菊厂某组任职 Android 架构师,记得看完点赞,养成习惯,关注这个喜欢写干货的程序员。

另外耗时两年整理收集的Android一线大厂面试完整考点PDF出炉,【完整版】已更新在我的【Github】,如有面试、进阶需要的朋友们可以去参考参考,如果对你有帮助,可以点个Star哦!

地址:【https://github.com/733gh/xiongfan】

以上是关于Android 开发高级面试题集合—— Binder 篇 <三>的主要内容,如果未能解决你的问题,请参考以下文章

面试专题2021年字节阿里网易等 Handler 面试题集合,Android高级开发必备!

Android 开发高级面试题集合——Binder篇

最新Android高级面试之Java集合

面试专题2021年大厂 Handler 面试题集合

干货2021最新《Android高级开发面试题2.0》中高级程序员必备“面试宝典”

Android高级开发面试题以及笞案整理