Android大脑--systemserver进程

用心坚持输出易读、有趣、有深度、高质量、体系化的技术文章,技术文章也可以有温度。

本文摘要

系统native进程的文章就先告一段落了,从这篇文章开始写Java层的文章,本文同样延续自述的方式来介绍systemserver 进程,通过本文您将了解到systemserver进程是啥?它包含那么多的服务,是如何管理它们的?如何启动它们的?(文中代码基于Android13)

本文大纲

我是谁

我的出生

我的启动

总结

1. 我是谁

我是一个进程,是一个既可以运行Java代码也可以运行C和C++代码的全能型选手,不像我的"叔叔辈儿"如logd进程lmkd进程只能运行C和C++代码。我拥有全能型能力还要感谢我的"父亲"zygote,因为他是全能型的,所以我才把这些能力继承了过来。

我的uid是1000,uid是啥呢?它与你们人类的身份证一样,每个安装到Android设备上的程序都有一个uid,只要apk被安装就分配一个唯一的uid,只要apk不卸载,这个uid是不会变化的。uid为0也就是root用户的程序它可是拥有无限超级能力。

我其实还有一个真名叫system_process ,我觉得这个名字不霸气、不能体现我的职责,因此我对外让大家都称呼我为systemserver ,你看这名字多好系统服务,这名字多霸气、多威武。

1.1 我的父亲

我的"爷爷"init 进程有非常非常多的孩子,这或许是偶像的力量吧,我的"父亲"zygote 也有非常非常多的孩子。凡是Android中能运行Java代码的进程都是zygote 的孩子,并且它们也都是我的"弟弟",为啥是弟弟呢?因为我是zygote的第一个孩子,也就是"长子"。

其实我对我的"父亲"zygote也是有蛮多怨言的,为啥这样说呢?因为他只负责孵化子进程,而孵化完毕后,他就做一个甩手掌柜的,对于子进程不管不顾,他甚至完全不知道自己孵化了多少子进程,而这些任务却偏偏都落在了我的身上,我还得负责管理这些子进程。这或许就是""长兄如父""的完美体现吧。

1.2 我的弟弟们

我所有的"弟弟们",它们若想要"出生"的话,都需要经过我向zygote 发送孵化它们的请求,zygote孵化出它们后,我负责来管理它们,比如当Android设备处于低内存状态时候,我会尝试把最不常用、处于后台的子进程杀掉。**不知道你们有没有发现个问题,我的uid是1000并非是0,那我怎么具有杀其他进程的能力的呢?**先卖个关子,在后面会介绍到我如何具有此能力。

我不单单充当"长子"的角色,我还把我自己比作Android的大脑 ,这可不是我骄傲事实确实是这样的,所有可运行Java代码进程都需要以我为中心,我离了它们是可以的,它们可是万万离不开我,比如这些进程想要启动Activity,那需要经过我的ActivityTaskManagerService服务的校验,再比如某个进程需要发出振动的话,那也需要经过我的VibratorManagerService服务进而由它发向hal层发出振动的命令。

(如下图展示app进程与systemserver进程的一些交互信息)

大家可别把我想象的如超人那样,我也不是事无巨细的"诸葛亮",我能完成这么多复杂的工作,完全是靠我拥有的各种service (服务),这些service会处理各自擅长的事情,像上面提到的ActivityTaskManagerService和VibratorManagerService就是其中两个service,那来看下我拥有的service吧。

1.3 拥有的service

下图是我拥有的部分service

那就来简单介绍下部分service吧

Installer

installd进程中介绍过它,它与apk的安装有关系,它对installd 进程做了封装,这样就可以在Java层使用installd的功能了。

PowerStatsService

想要知道电量的使用情况,可以找它啊,它可是电量使用情况的专家

ActivityTaskManagerService

该服务与启动Activity有非常大的关系,进程中启动Activity必定要经过该服务

PowerManagerService

电源管理服务是一个非常重要的系统服务,它负责管理系统的电源状态,包括设备的唤醒、休眠、屏幕亮度调节、电池电量管理等

ThermalManagerService

该服务是温度监控服务,当温度超过一定阈值后,手机会做出一些策略比如cpu降频操作,保证手机温度不至于一直上升

DisplayManagerService

该服务负责管理设备上的所有显示相关的资源和操作。它负责屏幕的开启与关闭、屏幕亮度的调整、显示分辨率的变更、屏幕旋转以及多窗口管理等任务

UserManagerService

该服务主要是管理设备用户,一个设备默认情况下有一个主用户它的id是0,可以为设备增加用户,用户之间的数据是隔离的

好了,由于时间关系,我不可能把所有的service都介绍完毕,就先介绍以上这些service

service在什么环境下工作呢?

先来说下这里的环境一词,其实指的是在哪个线程工做。

首先需要肯定的一点是不可能所有的service都在我systemserver的唯一的一个主线程里面工作,如果是这样这些service肯定会出现各种问题,其次是性能低下。

其实对于这一点我是没有制定规则的,每个service的复杂程度不一样,因此有的service会启动自己的线程 来做工作,而有的service是与别的service共享一个线程 来做工作,有的service就在主线程里面做自己的工作。

1.4 service如何让使用者使用

我既然拥有这么多的service,那如何让使用者使用这些service的能力呢?

如果使用者都与我systemserver位于同一进程的话,这些事情就特别好办了,但却不是这样的使用者它们绝大多数都是位于别的进程,那这种情况就有些棘手了。

虽然棘手,但是是有解的,大家上眼了先看下下面这张类图

看了这张图是不是有一种似曾相识的感觉,没错这就是使用aidl生成的类图结构,大家都知道aidl其实是为binder通信设计的,那service如果让使用者使用的话那就是使用binder通信了,先暂停一下,继续把上面的类图交代清楚 (熟悉的小伙伴可以跳过这部分)

IXXXManager

它是一个接口,定义了该service提供的能力,它继承了IInterface接口。每个service都有自己的对应接口,比如ActivityManagerService对应的是IActivityManager接口、ActivityTaskManagerService对应的是IActivityTaskManager

IXXXManager.Stub

该类是一个抽象类,它实现了IXXXManager接口,同时也继承了Binder类。

XXXService

该类就是每个service了,每个service又继承了IXXXManager.Stub,也就是说每个service其实又是Binder的子类。下面是几个例子

public class ActivityTaskManagerService extends IActivityTaskManager.Stub 

public class ActivityManagerService extends IActivityManager.Stub
IXXXManager.Stub.Proxy

该类如它的名字就是一个代理类,它实现了IXXXManager接口,该类有一个属性mRemote它一般是BinderProxy类型的。而该类是让使用者来使用的,调用该类的相应方法,就可以通过binder通信最终调用到对应service的能力了。

同样每个service都有自己对应的IXXXManager.Stub.Proxy,比如ActivityManagerService对应的是IActivityManager.Stub.Proxy、ActivityTaskManagerService对应的是IActivityTaskManager.Stub.Proxy

上面的类图就是每个service让使用者使用的一个框架,说直白点就是使用了binder通信,因为binder通信是client/server 模式,每个service是Binder的子类,那它就是server端,而使用者要想使用对应service的能力那就需要使用对应的IXXXManager.Stub.Proxy类了。

既然每个service是一个Binder,那就有必要把自己注册在ServiceManager中,只有注册了使用者才可以使用。(如下伪代码)

XXXService service = new XXXService()
ServiceManager.addService("XXXService",service)

使用者使用的话从ServiceManager中获取对应的service的BinderProxy对象,并把它作为IXXXManager.Stub.Proxy类的参数,来构造IXXXManager.Stub.Proxy这样的一个对象 ,通过该对象即可使用service的能力。(如下伪代码)

BinderProxy bp = ServiceManager.getService("XXXService")
IXXX.Stub.Proxy proxy = new IXXX.Stub.Proxy(bp)
# 就可以调用proxy对象来使用service的功能了

1.5 小结

我是一个既能运行Java代码又能运行C/C++代码的全能型进程,我在所有可运行Java代码的进程中的地位犹如人类的大脑,那可是妥妥的C位。我的地位虽然如此高贵,但我可不是一个集权者,我拥有很多的service,是它们替我分担了我的职责,它们有条不紊的处理着各种需求。其他可运行Java代码的进程要想使用这些service的能力,那就需要通过binder通信来使用。

好了,关于我就先介绍到此,作为C位的我,大家对我的出生一定很感兴趣,那就来讲下我的出生吧。

2. 我的出生

我的出生其实是被动的,为什么说是被动的呢?我的出生并不是我主动发起的,像我的"叔叔辈儿"、"弟弟们"他们的出生基本都是由他们主动发起的,比如logd 进程会主动配置rc脚本文件,在把脚本文件交给init 进程。而我的出生是在我完全不知晓的情况下由我的"父亲"zygote直接决定的。

我的出生大致分为三步:孵化前孵化孵化后,那就按这三步来讲下我的出生吧

2.1 孵化前

在孵化前,我的"父亲"zygote 已经做了很多的工作的:加载JVM 、加载公共的jni方法、提前预加载classes、预加载resource、预加载字体等。

其中预加载classes,会把这些classes加载到BootClassLoader.getInstance()对象中,该ClassLoader可是顶级ClassLoader,还有预加载的这些classes主要是framework.jar下面的类。预加载resource,会把这些resource加载到Resources.getSystem()对象中。

除了上面的工作,还有一个工作就是准备孵化时候要用到的参数,这些参数有几个是需要重点介绍下的。

setuid
--setuid=1000

如上设置了我systemserver进程的uid为1000

setgid
--setgid=1000

设置了我systemserver进程所属的组id是1000

capabilities

Linux Capabilities是Linux操作系统中的一种权限管理机制,它允许对进程的权限进行细粒度的控制,以实现最小特权原则 。这一机制将root用户的特权划分为具体的功能集,允许将部分root特权授予非root进程,从而增强了系统的安全性。
Linux Capabilities是在Linux内核2.2之后引入的,它将传统上与超级用户root(UID=0)关联的特权细分为不同的功能组。这些功能组作为线程(Linux中并不真正区分进程和线程)的属性存在,每个功能组都可以独立启用和禁用。这样一来,权限检查的过程就变成了:在执行特权操作时,如果线程的有效身份不是root,就去检查其是否具有该特权操作所对应的capabilities,并以此为依据,决定是否可以执行特权操作

上面是capabilities 功能的介绍,用一句话概括:就是说非root用户进程也可以通过设置capabilities,来让该进程具有相应的特权。

还记得我systemserver可以杀掉一些处于后台的进程来释放内存吗?杀掉其他进程的特权就是通过capabilities设置的,当然还设置了别的特权,如下相关代码:

long capabilities = posixCapabilitiesAsBits(
    //主要用于控制进程对共享内存片段的锁定能力            
    OsConstants.CAP_IPC_LOCK,
    //杀掉其他进程的能力
    OsConstants.CAP_KILL,
    //用于控制对网络配置和管理的访问权限
    OsConstants.CAP_NET_ADMIN,
    //它允许进程绑定到1024以下的端口号。这些端口号通常被认为是特权端口
    OsConstants.CAP_NET_BIND_SERVICE,
    //它允许进程发送广播消息到网络
    OsConstants.CAP_NET_BROADCAST,
    //它允许进程创建RAW和PACKET套接字以及进行任意地址的绑定
    OsConstants.CAP_NET_RAW,
    //它允许用户或进程加载(或卸载)内核模块
    OsConstants.CAP_SYS_MODULE,
    //它允许进程调整其他进程或自身进程的nice值,从而影响其调度优先级
    OsConstants.CAP_SYS_NICE,
    //它允许进程跟踪系统上的任何其他进程,这种能力通常与调试和安全审计相关
    OsConstants.CAP_SYS_PTRACE,
    //它允许进程设置系统时间
    OsConstants.CAP_SYS_TIME,
    //它允许进程进行TTY设备的配置任务。
    OsConstants.CAP_SYS_TTY_CONFIG,
    //它允许进程设置唤醒系统(或唤醒挂起系统)的定时器
    OsConstants.CAP_WAKE_ALARM,
    //它允许进程阻止系统进入挂起(suspend)状态
    OsConstants.CAP_BLOCK_SUSPEND
        );

在配置了上面的这些capabilities后,我systemserver也就具有了这些特权了。

入口类
com.android.server.SystemServer

入口类就是当systemserver进程被fork成功后,最先开始执行的类。如上这个类就是SystemServer类

这些参数配置完毕后,孵化前的准备工作就完成了。

下面是相关代码,请自行取阅:

//ZygoteInit类

private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        long capabilities = posixCapabilitiesAsBits(
                OsConstants.CAP_IPC_LOCK,
                OsConstants.CAP_KILL,
                OsConstants.CAP_NET_ADMIN,
                OsConstants.CAP_NET_BIND_SERVICE,
                OsConstants.CAP_NET_BROADCAST,
                OsConstants.CAP_NET_RAW,
                OsConstants.CAP_SYS_MODULE,
                OsConstants.CAP_SYS_NICE,
                OsConstants.CAP_SYS_PTRACE,
                OsConstants.CAP_SYS_TIME,
                OsConstants.CAP_SYS_TTY_CONFIG,
                OsConstants.CAP_WAKE_ALARM,
                OsConstants.CAP_BLOCK_SUSPEND
        );
        
        省略代码······

        String[] args = {
                "--setuid=1000",
                "--setgid=1000",
                "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
                        + "1024,1032,1065,3001,3002,3003,3005,3006,3007,3009,3010,3011,3012",
                "--capabilities=" + capabilities + "," + capabilities,
                "--nice-name=system_server",
                "--runtime-args",
                "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
                "com.android.server.SystemServer",
        };
        ZygoteArguments parsedArgs;

        int pid;

        try {
            ZygoteCommandBuffer commandBuffer = new ZygoteCommandBuffer(args);
            try {
                parsedArgs = ZygoteArguments.getInstance(commandBuffer);
            } catch (EOFException e) {
                throw new AssertionError("Unexpected argument error for forking system server", e);
            }
            commandBuffer.close();
            // 设置允许所有的app可以调试
            Zygote.applyDebuggerSystemProperty(parsedArgs); 
            Zygote.applyInvokeWithSystemProperty(parsedArgs);

            省略其他代码······
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }

        省略其他代码······
    }

2.2 孵化

现在进入最激动人心的阶段孵化 ,因为孵化前已经把参数等信息都准备好了,那孵化就是调用系统的fork 方法来创建子进程,当然在调用fork 方法的时候是需要把从zygote 进程继承来的fd 关闭掉的,否则会影响原先的父进程。在fork成功后,会把传递的这些参数设置给我systemserver进程。

因为fork 机制采用的是写时复制 ,即如果子进程没有对父子进程共享的区域进行 操作的话,则这片区域是共享的。我的"父亲"zygote在孵化之前做的准备工作:如加载JVM、加载jni方法、预加载classes、预加载resource,都是我与我父亲共享的。

上面的话是不是有些懵,还是看下图吧,毕竟一图胜千言

在孵化之前zygote加载了JVM、预加载的classes加载到了BootClassLoader.getInstance()方法返回的BootClassLoader对象中、预加载的resources加载到了Resources.getSystem()方法返回的Resources对象中,当然还有别的。

而在孵化之后,systemserver 进程就把zygote 进程中的JVM实例、BootClassLoader.getInstance()返回的对象、Resources.getSystem()方法返回的Resources对象及其他的实例都完完全全的共享 过来了。这也就是zygote提前做各种准备能在子进程中生效的具体原因。

2.3 孵化后

在孵化后会有关闭socket创建ClassLoader打开binder进入入口类这几件事情要做,那接下来就介绍下。

2.3.1 关闭socket

因为fork 机制创建子进程,会把父进程打开的socket对应的fd也继承过来,因此需要把继承过来的socket对应的fd关闭掉。zygote 是会启动一个ServerSocket,如果想要孵化子进程,则其他进程与该ServerSocket建立连接即可。因此systemserver进程需要把继承过来的ServerSocket关闭掉。

相关的代码,请自行取阅:

  //ZygoteInit类
  private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        省略代码······
        
        //在fork成功后,下面的代码会分别由zygote的线程和systemserver的线程执行,
        //pid==0 代表 systemserver的线程会执行
        if (pid == 0) {
            省略代码······
            //在systemserver进程把zygote socket关闭掉,因为systemserver进程不需要它
            //查看下面的closeServerSocket方法
            zygoteServer.closeServerSocket();
            return handleSystemServerProcess(parsedArgs);
        }

        return null;
    }
    
  //ZygoteServer类
  void closeServerSocket() {
        try {
            if (mZygoteSocket != null) {
                FileDescriptor fd = mZygoteSocket.getFileDescriptor();
                mZygoteSocket.close();
                if (fd != null && mCloseSocketFd) {
                    Os.close(fd);
                }
            }
        } catch (IOException ex) {
            Log.e(TAG, "Zygote:  error closing sockets", ex);
        } catch (ErrnoException ex) {
            Log.e(TAG, "Zygote:  error closing descriptor", ex);
        }

        mZygoteSocket = null;
    }
2.3.2 创建ClassLoader

为啥要创建ClassLoader呢?

原因是这样的,当前被孵化出来的systemserver 进程,它其实与zygote 共享一个BootClassLoader对象,而该ClassLoader只是加载了进程之间公用的classes,而systemserver 自己特有的各种classes在共享的BootClassLoader对象中是找不到的,比如入口类SystemServer在BootClassLoader找不到。那咋办呢,答案当然是创建自己的ClassLoader,并且把自己的各种jar、so文件路径设置给ClassLoader,同时该ClassLoader的parent是共享的BootClassLoader对象。这样通过自己的创建的ClassLoader就可以找到systemserver 进程自己特有的classes,同时也能找到zygote之前预加载的classes。

下面是相关代码,请自行取阅:

  //ZygoteInit类
  private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        省略代码······
        
        //在fork成功后,下面的代码会分别由zygote的线程和systemserver的线程执行,
        //pid==0 代表 systemserver的线程会执行
        if (pid == 0) {
            省略代码······
            //查看下面handleSystemServerProcess方法
            return handleSystemServerProcess(parsedArgs);
        }
        return null;
    }
    
    private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
        省略代码······
        if (parsedArgs.mInvokeWith != null) {
            省略代码······
        } else { 
            //创建ClassLoader
            ClassLoader cl = getOrCreateSystemServerClassLoader();
            if (cl != null) {
                //为当前线程设置ClassLoader
                Thread.currentThread().setContextClassLoader(cl);
            }

            省略代码······
        }

        /* should never reach here */
    }
3.2.3 打开binder

打开binder这可是非常重要的一步,只有执行了这步被孵化的进程才具有了binder通信的能力,直接看代码吧

//ZygoteInit类
  private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        省略代码······
        
        //在fork成功后,下面的代码会分别由zygote的线程和systemserver的线程执行,
        //pid==0 代表 systemserver的线程会执行
        if (pid == 0) {
            省略代码······
            //查看下面handleSystemServerProcess方法
            return handleSystemServerProcess(parsedArgs);
        }
        return null;
    }
    
    private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
        省略代码······
        if (parsedArgs.mInvokeWith != null) {
            省略代码······
        } else { 
            省略代码······
            //查看下面 zygoteInit 方法
            return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                    parsedArgs.mDisabledCompatChanges,
                    parsedArgs.mRemainingArgs, cl);
        }
    }
    
    public static Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
            String[] argv, ClassLoader classLoader) {
        省略代码······
        //nativeZygoteInit 方法会调用到下面的 com_android_internal_os_ZygoteInit_nativeZygoteInit 方法
        ZygoteInit.nativeZygoteInit(); 
    }
    
    //core/jni/AndroidRuntime.cpp
    static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
    {
      //会调用到下面的onZygoteInit
      gCurRuntime->onZygoteInit();
    }
    
    //cmds/app_process/app_main.cpp
    virtual void onZygoteInit()
    {
        //打开binder驱动
        sp<ProcessState> proc = ProcessState::self();
        //启动binder线程池
        proc->startThreadPool();
    }
3.2.4 进入入口类

这是孵化后的最后一步,也是systemserver 进入自己代码的起始点,还记得入口类是SystemServer吧,而这一步就是要进入SystemServer的main方法。

下面是相关方法,请自行取阅:

//ZygoteInit类
  private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        省略代码······
        
        //在fork成功后,下面的代码会分别由zygote的线程和systemserver的线程执行,
        //pid==0 代表 systemserver的线程会执行
        if (pid == 0) {
            省略代码······
            //查看下面handleSystemServerProcess方法
            return handleSystemServerProcess(parsedArgs);
        }
        return null;
    }
    
    private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
        省略代码······
        if (parsedArgs.mInvokeWith != null) {
            省略代码······
        } else { 
            省略代码······
            //查看下面 zygoteInit 方法
            return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                    parsedArgs.mDisabledCompatChanges,
                    parsedArgs.mRemainingArgs, cl);
        }
    }
    
    public static Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
            String[] argv, ClassLoader classLoader) {
        省略代码······
        //查看下面的 applicationInit方法
        return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
                classLoader);
    }
    
    //RuntimeInit类
    protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
            String[] argv, ClassLoader classLoader) {
        省略代码······
        //查找 static main方法
        //查看下面的 findStaticMain 方法
        return findStaticMain(args.startClass, args.startArgs, classLoader);
    }
    
    protected static Runnable findStaticMain(String className, String[] argv,
            ClassLoader classLoader) {
        Class<?> cl;

        try {
            //查找SystemServer的Class
            cl = Class.forName(className, true, classLoader);
        } catch (ClassNotFoundException ex) {
            省略代码······
        }

        Method m;
        try {
            //查找SystemServer的main方法
            m = cl.getMethod("main", new Class[] { String[].class });
        } catch (NoSuchMethodException ex) {
            throw new RuntimeException(
                    "Missing static main on " + className, ex);
        } catch (SecurityException ex) {
            throw new RuntimeException(
                    "Problem getting static main on " + className, ex);
        }

        int modifiers = m.getModifiers();
        if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
            throw new RuntimeException(
                    "Main method is not public and static on " + className);
        }

        //返回MethodAndArgsCaller实例,它持有找到的main方法的Method实例
        return new MethodAndArgsCaller(m, argv);
    }

2.4 小结

以上就是我的出生过程,我的"父亲"zygote 为了加快子进程的启动速度,在孵化之前做了很多准备工作:加载JVM、加载通用jni方法、预加载classes、预加载resource等,而我systemserver 进程在fork成功后与zygote 共享在准备阶段产生的这些实例,同时我还打开了binder驱动 ,这样我才具有了binder通信能力,我还创建了自己的ClassLoader ,并且进入了SystemServer类的main方法,至此我"出生"了。

3. 我的启动

我虽然"出生"了,但是我就像人类的婴儿一样,还没有任何的能力,因此我需要经历启动过程,只有经历了启动过程我才真正的"长大成人"。

3.1 解决多service带来的启动问题

大家都知道我有很多很多的service,而启动过程其实就是把所有的service启动起来,service多是好事也同时是坏事,这不在启动过程中就体现出来了,因为service多,在启动过程中肯定会出现某个service的启动要依赖于另外的service,而另外的service又要依赖于别的service;甚至会出现更复杂的情况,某个或多个service要依赖多个其他的service,这种依赖关系就像一团乱麻着实令人头疼。

针对这种情况,确实花费了我很多的脑细胞,想出了管理service对service分类分发启动阶段信号这三个办法来解决此问题。

3.1.1 管理service

管理service就是要对这些service进行管理,不管理确实会出现各种乱套,那该如何管理呢?

老规矩先看下我设计的类图吧

如图,我主要设计了SystemService和SystemServiceManager两个类,先来解释下这两个类

SystemService

每个service作为能力提供方的话是Binder类的子类,而要作为一个service被systemserver进程管理的话,那它需要继承SystemService类

该类是一个抽象类,它定义了一些service都会用到的方法比如publishBinderService ,它的主要作用就是service可以把自己注册到ServiceManager中。

还定义了一些一些抽象方法,其中onStartonBootPhase特别类似Activity的生命周期方法。

onStart方法

onStart 被调用的话,则代表当前的service可以调用publishBinderService 方法,把自己注册到ServiceManager中。

onBootPhase方法

systemserver 会通过onBootPhase方法告知service当前启动到了哪一步了。

onUserStarting方法

systemserver会通过该方法告知service当前是哪个用户正在启动,其他的onUserXXX方法也是类似作用。

图中ActivityManagerService类为啥没有继承SystemService呢?主要原因是Java只支持单继承,因为ActivityManagerService已经继承了IActivityManager.Stub类

SystemServiceManager

而该类的主要作用就是管理所有的SystemService,它的mService 属性存储了所有启动的SystemService。同时它拥有几个简化创建service流程的方法,暂且拿下面这个方法举例子

startService(Class)

该方法把构建service对象添加service对象调用service的onStart方法 这几个步骤都集中起来。启动一个service只需要调用该方法,service只需要实现自己的onStart 方法逻辑,onStart方法具体啥时候调用,service不需要关心。

3.1.2 对service分类

对service进行分类,把service分为bootstrap servicescore servicesother services,如下图:

bootstrap services代表别的service对它们的依赖最多,因此需要最先启动。

3.1.3 分发启动阶段信号

分发启动阶段信号 就是告知每个service当前处于启动的哪个阶段了,service根据相应的阶段信号可以做一些自己的事情。

分发启动阶段信号 这个词如果不好理解的话,那用个例子来解释:在下载文件的时候,是不是会显示下载进度,而启动阶段信号其实与下载进度非常类似,就是告知service当前的启动进度是啥样。

3.1.4 小结

service继承SystemService,并且把它们都添加到SystemServiceManager,这样就可以把所有的service管理起来;再对service进行分类,按类别来分别启动它们;把启动阶段信号 通过SystemServiceManager发送给所有的service,这样service根据启动阶段信号来做自己的事情。以上三种办法可以解决启动service混乱的问题。

3.2 启动

既然多service启动的问题解决了,那咱就进入正题开始介绍我的启动过程吧,我把启动过程分为启动前启动bootstrap services启动core services启动other services进入无休工作模式 ,当然还有启动Apex services这一步,因为它不是重点在这就不赘述了,那就从这几步来介绍启动。

3.2.1 启动前

启动前是需要做一些准备工作的设置binder线程池个数开启Looper加载android_servers可执行文件创建Context,那就依次来介绍下。

设置binder线程池个数

普通进程打开binder驱动后,能启动最大的binder线程个数是15个,前面也一直在强调我可不是普通的进程,更何况我拥有非常多的service,这些service每个都是binder server,因此我启动的最大binder线程个数是31个

不知道大家有没有考虑过这样的问题:最大binder线程个数少的话会有啥问题?

其中一个重要的问题就是性能降低。我举个例子比如工厂里面正常工作量的话,15个人刚好干完,但是由于工作量加大,15个人的话就干不完了,那这种情况该咋办呢?其中一种是15个人加班干,另外一种是增加工人个数。同样binder最大线程数小的话,就会导致大量的请求积压在队列中,这样就导致性能下降。

下面是相关代码,请自行取阅:

//SystemServer方法
public static void main(String[] args) {
   //run方法在下面
   new SystemServer().run();
}

private void run() {
  省略代码······
  //sMaxBinderThreads的值为31,该方法设置开启binder线程的最大个数
  BinderInternal.setMaxThreads(sMaxBinderThreads);
  省略代码······
}
开启Looper

Looper在Android中的作用不必说,开启了Looper后,其他的线程或者当前线程就可以通过Handler把Message传递到当前线程。

加载android_servers可执行文件

android_servers可执行文件中包含了jni方法和很多native方法,使用System.loadLibrary方法可以把android_servers可执行文件加载到内存中,这样就可以保证jni方法可用

创建Context

Context在Android中的作用那可是非常重要,而在systemserver 进程中也是需要Context对象的,会创建两个Context,一个用来做业务逻辑等处理,另一个用来做UI显示,在systemserver也是会显示一些UI的 (比如关机对话框)

下面是相关代码,请自行取阅:

//SystemServer方法
public static void main(String[] args) {
   //run方法在下面
   new SystemServer().run();
}

private void run() {
  省略代码······
  //createSystemContext方法在下面
  createSystemContext();
  省略代码······
}

private void createSystemContext() {
    //创建systemserver具有的ActivityThread对象
    ActivityThread activityThread = ActivityThread.systemMain();
    //获取系统Context对象
    mSystemContext = activityThread.getSystemContext();
    //为Context设置主题
    mSystemContext.setTheme(DEFAULT_SYSTEM_THEME); 

    //该方法会根据mSystemContext创建UI类型的Context,它主要用来显示UI
    final Context systemUiContext = activityThread.getSystemUiContext(); 
    //为systemUiContext设置主题
    systemUiContext.setTheme(DEFAULT_SYSTEM_THEME);
}
小结

启动前期主要是为后面启动各种service做准备,接下来看剩下的步骤吧。

3.2.2 启动bootstrap services

还记得我已经对service进行了分类,该阶段就是对bootstrap services进行启动,在此阶段同时还会启动一个Watchdog类,它的作用是监控systemserver进程的对应线程是否发生了锁等待情况,后面会再次详细介绍它。

在此阶段,会发出第一个启动阶段信号PHASE_WAIT_FOR_DEFAULT_DISPLAY (它的值为100),此信号的意思是现在进入等待获取默认显示屏阶段,已经启动的service都会收到该信号,只不过只有DisplayManagerService才关心此信号。DisplayManagerService会尝试获取是否有可用的显示屏,有的话则进入下阶段;否则等待一定时间后还是没有可用的显示屏,则整个启动过程失败。

下面是相关代码,请自行取阅:

//DisplayManagerService
//下面方法会阻塞整个启动流程
public void onBootPhase(int phase) {
        //收到 PHASE_WAIT_FOR_DEFAULT_DISPLAY 信号
        if (phase == PHASE_WAIT_FOR_DEFAULT_DISPLAY) {
            synchronized (mSyncRoot) {
                //获取超时时间
                long timeout = SystemClock.uptimeMillis()
                        + mInjector.getDefaultDisplayDelayTimeout();
                //若没有可用显示屏,则进行等待
                while (mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY) == null
                        || mVirtualDisplayAdapter == null) {
                    long delay = timeout - SystemClock.uptimeMillis();
                    //若超时依然没有获取到可用显示屏,则抛异常
                    if (delay <= 0) {
                        throw new RuntimeException("Timeout waiting for default display "
                                + "to be initialized. DefaultDisplay="
                                + mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY)
                                + ", mVirtualDisplayAdapter=" + mVirtualDisplayAdapter);
                    }
                    省略代码······
                    try {
                        mSyncRoot.wait(delay);
                    } catch (InterruptedException ex) {
                    }
                }
            }
        } 
        省略代码······
    }

3.2.3 启动core services

该阶段主要是启动类别为core的所有service,启动的service确实很多,我在这就不给大家一一介绍了,咱们进入下个阶段。

3.2.4 启动other services

同样此阶段会启动剩下的service,由于启动的service多,也不在此一一赘述了,当然在这个阶段还会启动systemui 进程,并且还会启动launcher,在这里介绍下几个关键的启动阶段信号。

PHASE_WAIT_FOR_SENSOR_SERVICE (它的值是200),此信号的意思是进入等待传感器服务阶段,同样所有启动的service都会收到此信号,只有SensorService才会关心它,SensorService在等待期间会阻塞启动过程。

下面是相关代码,请自行取阅:

//SensorService
public void onBootPhase(int phase) {
    if (phase == SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE) {
        ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart,
                    START_NATIVE_SENSOR_SERVICE);
        synchronized (mLock) {
            mSensorServiceStart = null;
        }
    }
}

PHASE_LOCK_SETTINGS_READY (它的值是480),此信号的意思是settings相关的数据你们各service可以使用了,因此已经启动的service,收到该信号后若需要读取settings的数据就可以读取了。

PHASE_SYSTEM_SERVICES_READY (它的值是500),此信号的意思是core services可以被安全的使用了,比如PowerManagerService、PackageManagerService等可以被别的service使用了。

PHASE_ACTIVITY_MANAGER_READY (它的值是550),此信号的意思是ActivityManagerService已经准备好了,广播等都可以使用了

PHASE_BOOT_COMPLETED (它的值是1000),此信号的意思是启动阶段完成 ,那啥时候发该信号呢?launcher界面展示成功后才会发出该信号,所有已经启动的service收到此信号后就可以做自己该做的事情了。

下面是相关代码,请自行取阅:

//ActivityManagerService

//下面的方法是launcher界面做完动画后会调用
final void finishBooting() {
        省略代码······
        //让所有的service都知道启动完成了
        mSystemServiceManager.startBootPhase(t, SystemService.PHASE_BOOT_COMPLETED);
        省略代码······
    }

3.2.5 进入无休工作模式

在ActivityManagerService发出PHASE_BOOT_COMPLETED信号后,所有的service都欢腾了起来,每个service身上紧绷着的弦也终于放松了,从此刻开始我和我的service小伙伴们就进入了无休工作模式,人类啊千万别在抱怨你们的工作时长长了,在长能有我长吗?

进入无休工作模式的代码也特别简单,如下

//SystemServer
private void run() {
  省略代码······
  Looper.loop(); 
  省略代码·····
}

其实就是调用了Looper.loop()方法,该方法作用是让当前线程会一直循环下去。

3.2.6 小结

我的启动其实就是启动所有的service,而由于service很多它们之间又存在复杂的依赖,导致启动过程很复杂,为了解决此问题,我使用SystemService和SystemServiceManager来管理这些service;并且对这些service进行分类,这样启动过程就可以按照分类来依次启动它们;在启动过程中某些service肯定要依赖于别的service或者别的事件,针对这种情况我想了分发启动阶段信号 这么个办法,发出相应的信号后,对该信号关心的service就可以着手处理相关的事情,其中PHASE_BOOT_COMPLETED 阶段信号是最关键信号,代表整个启动完成,而发该信号的时机是launcher界面展示完成。收到该信号后,我和我的service就完全准备好了,我们进入无休的工作模式

总结

我是systemserver 进程,我虽然是zygote 进程的"长子",但我同时也当担着"父亲"的职责,我会管理由zygote 孵化的所有进程。我把我比作Android的大脑,App进程如果想启动Activity那需要找我的ActivityTaskManagerService,如果需要让设备振动那也需要找我的VibratorManagerService,当然还有很多很多的事情都需要找我的service。

这就是我一个独一无二的进程。

相关推荐
姑苏风3 小时前
《Kotlin实战》-附录
android·开发语言·kotlin
数据猎手小k6 小时前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型
你的小107 小时前
JavaWeb项目-----博客系统
android
风和先行7 小时前
adb 命令查看设备存储占用情况
android·adb
AaVictory.8 小时前
Android 开发 Java中 list实现 按照时间格式 yyyy-MM-dd HH:mm 顺序
android·java·list
似霰9 小时前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
大风起兮云飞扬丶9 小时前
Android——网络请求
android
干一行,爱一行9 小时前
android camera data -> surface 显示
android
断墨先生9 小时前
uniapp—android原生插件开发(3Android真机调试)
android·uni-app
无极程序员11 小时前
PHP常量
android·ide·android studio