前言
转载请声明, 转自[https://www.cnblogs.com/andy-songwei/p/11471355.html] , 谢谢!
SystemUI 是系统启动中第一个用户肉眼可见的应用, 其功能包罗万象, 比如开机后看到的锁屏界面, 充电时充电界面, 状态栏, 导航栏, 多任务栏等, 都是与 Android 手机用户息息相关的功能. 所以不止 SystemUI 开发者, 普通的应用开发者也很有必要去了解一下 SystemUI. 本系列文章会基于 Android P 和 Android Q 来介绍 SystemUI 的各个方面, 本篇作为本系列第一篇, 主要介绍了 SystemUI 的启动流程, 以及主要功能简介.
本文的主要内容如下:
一, SystemUI 简介
SystemUI, 顾名思义是系统为用户提供的系统级别的信息显示与交互的一套 UI 组件, 所以其功能包罗万象. 比如锁屏, 状态栏, 底部导航栏, 最近使用 App 列表等, 大部分功能相互独立, 按需启动, 后文会继续列出更多功能. 在系统源码中, 其位置为: frameworks/base/package/SystemUI. 尽管从表现形式上看, SystemUI 和普通的 Android App 有较大的差别, 但其本质和普通 App 并没有什么差别, 也是以 apk 的形式存在, 如下图所示:
以当前测试机为例, 其就预置在系统指定的目录下. 也是通过 Android 的 4 大组件中的 Activity,Service,BroadcastReceiver 来接受外界的请求并执行相关的操作, 只不过它们所接受的请求主要来自各个系统服务而已.
二, Lambda 表达式简介
由于后面有个流程中用到了 Lambda 表达式, 为了后面便于讲解, 这里咱们先简单介绍一下它, 并简单演示其使用方法, 这里不做深入探讨, 有兴趣的可以自行研究.
Lambda 表达式是一个匿名函数, 即没有函数名的函数, 是基于数学中的λ演算得名. 在 java 中, 从 java8 开始引入, 使用它来设计代码会更加简洁. 下面在 Android 项目中举两个例子来直观感受一下 Lambda 语法的使用.
以下是一个很常见的设置点击事件的例子, 先看看不用 Lambda 表达式时的情况:
- mTextView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Log.i("songzheweiwang", "test lambda");
- }
- });
在采用 Lambda 表达式后, 就是下面这种情况:
- mTextView.setOnClickListener(onClickListener -> {
- Log.i("songzheweiwang", "test lambda");
- });
其中 "onClickListener" 是随意取的一个字符串, 我们取名的时候便于识别就可以了. 可见整个代码简洁了很多, 阅读起来也非常简单.
另外再看一个更加明显的例子, 不使用 Lambda 表达式时是这样:
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- Log.i("songzheweiwang", "test lambda");
- }
- };
使用 Lambda 表达式后, 就成了这样:
1 Runnable runnable2 = () -> Log.i("songzheweiwang", "test lambda");
如上的 "->" 符号可以读作 "go to". 使用 Lambda 表达式来代替匿名的内部类, 确实是非常的方便, 但是使用的时候需要注意 java 的版本号, 前面说了, 是在 java8 中才引入的, 否则在编译时会报如下的错误:
如上内容参考[Lambda 表达式_百度百科]
三, SystemUI 的启动时机
在[[乘风破浪] Android 系统启动篇] 中, 我介绍过 Android 系统的大致流程, 在第 6 步中讲到了 SystemServer 进程的启动. SystemServer 进程启动时, 会执行下面的代码:
- //=========SystemServer.java=========
- public static void main(String[] args) {
- new SystemServer().run();
- }
- private void run() {
- ......
- // 创建消息 Looper
- Looper.prepareMainLooper();
- // 加载动态库 libandroid_servers.so, 初始化 native 服务
- System.loadLibrary("android_servers");
- ......
- // 初始化系统 context
- createSystemContext();
- // 创建 SystemServiceManager
- mSystemServiceManager = new SystemServiceManager(mSystemContext);
- ......
- // 启动引导服务, 如 AMS 等
- startBootstrapServices();
- // 启动核心服务
- startCoreServices();
- // 启动其它服务, 如 WMS,SystemUI 等
- startOtherServices();
- ....
- }
在执行完第 18,20 行的代码后, 会启动引导服务和一些核心服务, 如 AMS 等, 然后第 22 行中就会启动其他服务, 其中 SystemUI 就在其中.
- //======SystemServer.java======
- private void startOtherServices() {
- ......
- // We now tell the activity manager it is okay to run third party
- // code. It will call back into us once it has gotten to the state
- // where third party code can really run (but before it has actually
- // started launching the initial applications), for us to complete our
- // initialization.
- mActivityManagerService.systemReady(() -> {
- ......
- traceBeginAndSlog("StartSystemUI");
- try {
- startSystemUi(context, windowManagerF);
- } catch (Throwable e) {
- reportWtf("starting System UI", e);
- }
- traceEnd();
- ......
- }, BOOT_TIMINGS_TRACE_LOG);
- ......
- }
在第 9 行中, 前面讲过 AMS 先启动了, mActivityManagerService 调用 systemReady 方法, 这里就用到了前面介绍过的 Lambda 表达式, systemReady 方法的源码如下:
- public void systemReady(final Runnable goingCallback, BootTimingsTraceLog traceLog) {
- ......
- if (goingCallback != null) {
- goingCallback.run();
- }
- ......
- }
这里参照前面介绍的 Lambda 表达式的使用方法就容易理解了, 实际上就是执行 Runnable 的回调而已, 这里其实就等同于如下代码:
- mActivityManagerService.systemReady(new Runnable(){
- new Runnable() {
- @Override
- public void run() {
- //Lambda 表达式中的回调代码
- }
- }
- },BOOT_TIMINGS_TRACE_LOG);
实际上在 Lambda 表达式还未引入前, 即早期的代码中就是这样写法.
当一切就绪后, 回调开始执行, 就开始执行第 13 行的 startSystemUI 方法了. 该方法的源码如下:
- static final void startSystemUi(Context context, WindowManagerService windowManager) {
- Intent intent = new Intent();
- intent.setComponent(new ComponentName("com.android.systemui",
- "com.android.systemui.SystemUIService"));
- intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
- //Slog.d(TAG, "Starting service:" + intent);
- context.startServiceAsUser(intent, UserHandle.SYSTEM);
- windowManager.onSystemUiStarted();
- }
第 3,4 行中给出了包名和类名, 这样就开始启动 SystemUI 了. 从这段代码可以看到, SystemUI 是通过 Service 来启动的, 而且是以系统的身份来启动它的.
四, Service 启动流程浅析
上节中 startSystemUI 方法中开始启动 SystemUIService,Service 的启动流程比较复杂, 这里不做详细分析, 仅简单介绍一下其中和本节息息相关的关键流程.
上节代码第 7 行 startSystemUI 方法的调用者看起来是 Context 类型的 context,Context 是一个抽象类, 实际执行者其实是 ContextImpl. 调用流程会通过 Binder 方式从 ContextImpl 跳转到 AMS 中, 再通过 Binder 方式跳转到 ActivityThread 中的内部类 ApplicationThread 中的 scheduleCreateService 方法. 在该方法中会发送给 Handler H 来处理, Handler H 的实例化是使用的主线程的 Looper, 所以其回调方法 handleMessage 就是在主线程中执行的, 此时会在该方法中调用 handleCreateService 方法, 咱们从这个方法开始看.
- private void handleCreateService(CreateServiceData data) {
- ......
- Service service = null;
- try {
- ......
- service = packageInfo.getAppFactory()
- .instantiateService(cl, data.info.name, data.intent);
- } catch (Exception e) {
- ......
- }
- try {
- ......
- ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
- context.setOuterContext(service);
- Application App = packageInfo.makeApplication(false, mInstrumentation);
- service.attach(context, this, data.info.name, data.token, App,
- ActivityManager.getService());
- service.onCreate();
- ......
- } catch (Exception e) {
- ......
- }
- }
第 6 行创建了 service 的实例, 第 13 行创建上下文, 第 15 行创建 Application, 并在其中执行了 Application 的 onCreate 方法, 第 18 行执行了 service 的 onCreate 方法. 这里进入到第 15 行的 makeApplication 方法. 下面截取了关键代码:
- public Application makeApplication(boolean forceDefaultAppClass,
- Instrumentation instrumentation) {
- ......
- Application App = null;
- String appClass = mApplicationInfo.className;
- if (forceDefaultAppClass || (appClass == null)) {
- appClass = "android.app.Application";
- }
- try {
- ......
- ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
- App = mActivityThread.mInstrumentation.newApplication(
- cl, appClass, appContext);
- appContext.setOuterContext(App);
- } catch (Exception e) {
- ......
- }
- mActivityThread.mAllApplications.add(App);
- ......
- if (instrumentation != null) {
- try {
- instrumentation.callApplicationOnCreate(App);
- } catch (Exception e) {
- ......
- }
- }
- ......
- return App;
- }
因为是初始启动, 所以会走到第 7 行. 第 12 行的 newApplication 源码如下:
- public Application newApplication(ClassLoader cl, String className, Context context)
- throws InstantiationException, IllegalAccessException,
- ClassNotFoundException {
- Application App = getFactory(context.getPackageName())
- .instantiateApplication(cl, className);
- App.attach(context);
- return App;
- }
继续追踪 instantiateApplication 方法:
- public @NonNull Application instantiateApplication(@NonNull ClassLoader cl,
- @NonNull String className)
- throws InstantiationException, IllegalAccessException, ClassNotFoundException {
- return (Application) cl.loadClass(className).newInstance();
- }
这里就通过类加载器的形式创建了 Application 的实例. 可见前面的 makeApplication 方法第 12 行的作用就是创建 Application 实例了, 然后走到该方法的第 22 行, 进入该方法:
- public void callApplicationOnCreate(Application App) {
- App.onCreate();
- }
该方法中 Application 执行了 onCreate 方法.
到这里 service 的大致启动流程就明了了, 这里咱们需要记住一个执行顺序(因为我看过不少资料容易在这里犯错, 说是 Application 会比 Service 先实例化, 通过这个流程我们可以看到这种说法是错误的, 所以这里着重提出来):
(1)实例 Service;
(2)实例 Application;
(3)Application 实例执行 onCreate 方法;
(4)Service 实例执行 onCrate 方法.
五, SystemUIApplication 中 onCreate 方法处理逻辑
上一节我们分析了, 会先执行 Application 的 onCreate 方法, 在执行 Service 的 onCreate 方法, 这里先分析 SystemUIApplication 中 onCreate 方法的执行逻辑.
- //============SystemUIApplication.java========
- private SystemUI[] mServices;
- @Override
- public void onCreate() {
- super.onCreate();
- ......
- // 设置主题
- setTheme(R.style.Theme_SystemUI);
- ......
- if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
- IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
- bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mBootCompleted) return;
- if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
- unregisterReceiver(this);
- mBootCompleted = true;
- if (mServicesStarted) {
- final int N = mServices.length;
- for (int i = 0; i <N; i++) {
- mServices[i].onBootCompleted();
- }
- }
- }
- }, bootCompletedFilter);
- IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
- registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
- if (!mBootCompleted) return;
- // Update names of SystemUi notification channels
- NotificationChannels.createAll(context);
- }
- }
- }, localeChangedFilter);
- } else {
- ......
- startSecondaryUserServicesIfNeeded();
- }
- }
这里说一下第 9 行的 if-else 逻辑, 我们知道 Linux 是多用户操作系统, 所以这个 if-else 语句就是判断是系统用户, 还是切换到了其它用户. SystemUI 大多数功能对所有用户都是一样的, 只有少部分功能会因为不同的用户而表现不一样, 比如通知, 多任务功能等, 这里后面会再讲到. 如果是系统用户就会走 if 中的流程, 这里注册了两个广播接收器, 用于监听 Intent.ACTION_BOOT_COMPLETED 和 Intent.ACTION_LOCALE_CHANGED.
Intent.ACTION_BOOT_COMPLETED 是监听开机启动, 这里分析的 Android9.0 的系统源码, 当前系统中使用的是 FBE 加密方式 (读者请自行查阅 FBE 加密方式, 这里不做详细介绍), 这种方式下, 要等到系统启动并锁屏界面解锁后, 在进入到桌面过程中, 系统才会发送发送该广播, 所以接收该广播的处理逻辑会比较延后. 通过第 15 行和第 18 行可以看到, 该广播只会处理一次, 就会反注册该广播, 以后就不会再接收了. 在这个逻辑当中, 第 20 行到第 25 行, 判断 mServicesStarted 变量, 该变量表示 SystemUIService 是否已经启动了, 实际上由于该广播接收的时机比较延后, 会在 SystemUIService 启动完后才接收到该广播, 所以这里面的代码会在此时执行. 第 23 行的 mService[] 数组存储的是 SystemUI 的子服务, 当整个系统启动完成后, 这里面的每个子服务都会执行 onBootCompleted()方法, 让各个子服务知道系统启动完成了, 要做自己该做的事情了. mService[]的赋值以及它存储的 SytemUI 子服务, 下一节会详细讲解, 这里我们只需要知道, 这个过程发生在 SystemUIService 的启动阶段即可.
Intent.ACTION_LOCALE_CHANGED 广播是用于监听设备当前区域设置已更改时发出的广播, 简单来说就是修改语言时发出的广播(暂时不知道其它动作是否也会发送该广播).
第 42 行就是在当前用户不是系统用户时的情况, 即切换用户后的场景, 该动作发生时系统是已经启动了的, 不会再触发 Intent.ACTION_BOOT_COMPLETED 广播. 这里看一看它的执行过程:
- void startSecondaryUserServicesIfNeeded() {
- String[] names =
- getResources().getStringArray(R.array.config_systemUIServiceComponentsPerUser);
- startServicesIfNeeded(names);
- }
第 2 行和第 4 行其实就是启动资源文件指定的功能, 如下所示:
- <string-array name="config_systemUIServiceComponentsPerUser" translatable="false">
- <item>com.Android.systemui.Dependency</item>
- <item>com.Android.systemui.util.NotificationChannels</item>
- <item>com.Android.systemui.recents.Recents</item>
- </string-array>
可以看到包含了通知 (第 3 行) 和多任务(第 4 行), 这几个功能会因用户不同而异, 第 2 行是什么功能暂时不清楚, 读者可以自己查阅. 另外我们会发现, 实际上这几个子服务, 在下一节的 config_systemUIServiceComponents 数组资源中也都是包含的, 也就正好对应了前面说的, 切换到个人用户后这几个功能会因用户不同而表现不同, 需要重新加载一次. startSecondaryUserServicesIfNeeded 方法的处理逻辑, 在下一节会详细讲到, 这里咱们只需要清楚这一块的功能即可.
六, SystemUIService 中 onCreate 方法处理逻辑
如前文所述, SystemUI 通过 "com.android.systemui.SystemUIService" 这个服务来启动, 在 Application 的 onCreate 方法执行完后, 就会执行自己的 onCreate 方法. 下面看看 SystemUIService 启动过程中做了哪些工作:
- public class SystemUIService extends Service {
- @Override
- public void onCreate() {
- super.onCreate();
- ((SystemUIApplication) getApplication()).startServicesIfNeeded();
- ......
- }
- ......
- }
该类中关键代码是第 5 行代码, 其它的没有什么重要逻辑, 继续追踪 startServicesIfNeeded()方法:
- //===========SystemUIApplication==========
- public void startServicesIfNeeded() {
- String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);
- startServicesIfNeeded(names);
- }
第 3 行在资源文件中对应的数组如下所示, 每一项都对应了一个子服务(这里并不是表示它们是 Service, 而是指某项功能模块), 实际上在 Android O 及以前的版本中, 这些类都是以数组的形式保存在代码中的.
- <string-array name="config_systemUIServiceComponents" translatable="false">
- <item>com.Android.systemui.Dependency</item>
- <item>com.Android.systemui.util.NotificationChannels</item>
- <item>com.Android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
- <item>com.Android.systemui.keyguard.KeyguardViewMediator</item>
- <item>com.Android.systemui.recents.Recents</item>
- <item>com.Android.systemui.volume.VolumeUI</item>
- <item>com.Android.systemui.stackdivider.Divider</item>
- <item>com.Android.systemui.SystemBars</item>
- <item>com.Android.systemui.usb.StorageNotification</item>
- <item>com.Android.systemui.power.PowerUI</item>
- <item>com.Android.systemui.media.RingtonePlayer</item>
- <item>com.Android.systemui.keyboard.KeyboardUI</item>
- <item>com.Android.systemui.pip.PipUI</item>
- <item>com.Android.systemui.shortcut.ShortcutKeyDispatcher</item>
- <item>@string/config_systemUIVendorServiceComponent</item>
- <item>com.Android.systemui.util.leak.GarbageMonitor$Service</item>
- <item>com.Android.systemui.LatencyTester</item>
- <item>com.Android.systemui.globalactions.GlobalActionsComponent</item>
- <item>com.Android.systemui.ScreenDecorations</item>
- <item>com.Android.systemui.fingerprint.FingerprintDialogImpl</item>
- <item>com.Android.systemui.SliceBroadcastRelayHandler</item>
- </string-array>
在 Android Q 上将第 21 行修改为了
1 <item>com.Android.systemui.biometrics.BiometricDialogImpl</item>
就是将指纹识别功能改成了生物识别功能, 在 Android Q 上开始, 除了指纹识别外, 还增加了人脸识别. 在原来的基础上另外再添加了 3 条:
- <item>com.Android.systemui.SizeCompatModeActivityController</item>
- <item>com.Android.systemui.statusbar.notification.InstantAppNotifier</item>
- <item>com.Android.systemui.theme.ThemeOverlayController</item>
打开这每一个类后, 会发现它们都继承自 SystemUI 类, SystemUI 类是一个抽象类, 提供了如下接口:
- public abstract class SystemUI implements SysUiServiceProvider {
- ......
- public abstract void start();
- protected void onConfigurationChanged(Configuration newConfig) {
- }
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- }
- protected void onBootCompleted() {
- }
- ......
- }
- }
startServicesIfNeeded(names)方法源码如下:
- private SystemUI[] mServices;
- private void startServicesIfNeeded(String[] services) {
- ......
- mServices = new SystemUI[services.length];
- ......
- final int N = services.length;
- for (int i = 0; i < N; i++) {
- String clsName = services[i];
- Class cls;
- try {
- cls = Class.forName(clsName);
- mServices[i] = (SystemUI) cls.newInstance();
- } catch (ClassNotFoundException ex) {
- throw new RuntimeException(ex);
- } catch (IllegalAccessException ex) {
- throw new RuntimeException(ex);
- } catch (InstantiationException ex) {
- throw new RuntimeException(ex);
- }
- ......
- mServices[i].mContext = this;
- mServices[i].mComponents = mComponents;
- ......
- mServices[i].start();
- ......
- if (mBootCompleted) {
- mServices[i].onBootCompleted();
- }
- }
- }
实际上就是通过反射的方式将前面的各个子服务类实例化, 并执行这些对象中的 start()方法, 来启动这些服务. 这样整个 SystemUI 就算启动了, 上述逻辑还是比较简单的.
我们需要注意的是, 这里使用了模板模式. SystemUI 是一个基类, 其中定义了 4 个抽象或空方法, 作为模板指定了子类的行为模式. 资源文件中定义的众多子服务类都是 SystemUI 的子类, 既然都继承自 SystemUI 类, 那么这些子类就有一些共同的行为模式, 在某些阶段应该有什么表现, 只是具体如何表现因不同子类而异. 比如说, 在上述代码中第 24 行和 27 行分别规定了 SystemUI 子类们在启动时要执行 start()方法, 系统启动后要执行 onBootCompleted()方法, 所以在这些子类中都重写了这两个方法, 到一定的阶段都会以回调的方式执行, 但是具体要在这些方法中干什么, 子类们自己说了算. 这就是典型的模板模式使用, 至于具体介绍和使用模板模式, 这里不展开讲, 读者可以自行查资料, 该模式在 Android 系统中使用还是很常见的, 读者最好能好好掌握.
到这里为止, SystemUI 的启动流程就介绍完了, 这里归纳起来就是执行了如下几个阶段:
(1)系统启动就绪后, SystemServer 进程下达启动 SystemUIService 的命令;
(2)SystemUI 的 SystemUiApplication 中执行 onCreate 方法, 注册系统启动广播和区域设置更改广播.
(3)SystemUI 的 SystemUIService 中执行 onCreate 方法, 启动公共用户的各项服务, 各子服务执行 onStart()回调方法.
(4)系统启动后, 第二步注册的广播会接收到系统启动广播, 然后各个子服务执行 onBootCompleted()回调方法.
(5)在切换都个人用户时, 再次加载因人而异的子服务功能.
七, SystemUI 包含的功能模块
上一节中通过数组的形式列出了 SystemUI 的子服务类, 这些类都分别表示什么功能呢? 下面我简单介绍其中几项, 读者可以根据名称来对号入座. 至于更详细的介绍, 有需要的话会专门写一篇文章来做介绍.
- (1)Status bars(状态栏)
- (2)Navigation bars(导航栏)
- (3)Notification(通知)
- (4)Keyguard(锁屏)
- (5)Quick settings(快速设置)
- (6)Recent task panel(最近任务面板)
- (7)VolumeUI(音量 UI)
- (8)Screenshot(截屏)
- (9)PowerUI(电量 UI)
- (10)RingtonePlayer(铃声播放器)
- (11)StackDivider(分屏)
- (12)PipUI(画中画 UI)
- (13)Biometrics(生物识别解锁功能, 如指纹解锁, 人脸解锁, 虹膜解锁等)
结语
SystemUI 的启动流程就介绍到这里, 由于讲得还算比较详细, 所以涉及的内容及细节不少, 一定会有些描述不准确或者不妥的地方, 如果发现, 请读者不吝赐教. 另外由于篇幅有限, 有些地方还是仅提到或者简单介绍而已, 比如 FBE 加密, 模板模式, Lambda 表达式等, 在平时系统开发中都会经常碰到, 读者都可以继续拓展深入学习.
来源: https://www.cnblogs.com/andy-songwei/p/11471355.html