Android SystemUI——服务启动流程(二)

server/2025/1/15 15:03:41/

        在 Andorid 系统源码中,package/apps下放的是系统内置的一些 APP,例如 Settings、Camera、Phone、Message 等等。而在 framework/base/package 下,它们也是系统的 APP,SystemUI 就在此目录下。它控制着整个 Android 系统的界面,但其实他也是一个 APP,不同于一般的 APP,它不可卸载也不可以被第三方应用替换。对于用户而言,SystemUI 的改动是最能直观感受到的。因此,每个 Android 版本在 SystemUI 上都有比较大的改动。而对开发者而言,理解 Android SystemUI 对优化 Android 系统界面,改善用户体验十分重要。

一、启动流程

        因为 SystemUI 是系统应用,所以它也是一个 APK,有入口 Application,只不过它是由 SystemServer 进程进行启动的。

1、SystemServer

        在 Android 系统之后,系统首先会启动一个名为 Zygote 的进程,而 Zygote 进程又会启动 SystemServer 进程,SystemServer 又会启动 SystemUI,这里我们先来看 SystemServer 的 main() 方法。

源码位置:/frameworks/base/services/java/com/android/server/SystemServer.java

main

public final class SystemServer implements Dumpable {……public static void main(String[] args) {new SystemServer().run();}
}

        这里启动了 run() 方法。

run

private void run() {TimingsTraceAndSlog t = new TimingsTraceAndSlog();……// Start services.try {t.traceBegin("StartServices");startBootstrapServices(t);startCoreServices(t);startOtherServices(t);} catch (Throwable ex) {……throw ex;} finally {t.traceEnd(); // StartServices}……
}

        在 run 方法中调用了startOtherServices() 方法。

startOtherServices

private void startOtherServices() {……mActivityManagerService.systemReady(() -> {……try {startSystemUi(context, windowManagerF);} catch (Throwable e) {reportWtf("starting System UI", e);}……}
}

        在 startOtherServices() 方法里面,mActivityManagerService 的 systemReady 回调方法中会创建线程去执行 startSystemUi() 方法。

startSystemUi

private static void startSystemUi(Context context, WindowManagerService windowManager) {PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);Intent intent = new Intent();intent.setComponent(pm.getSystemUiServiceComponent());intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);//Slog.d(TAG, "Starting service: " + intent);context.startServiceAsUser(intent, UserHandle.SYSTEM);windowManager.onSystemUiStarted();
}

        可以看到 startSystemUi() 方法首先获取 PackageManagerInternal 对象实例 pm,再调用 pm 的 getSystemUiServiceComponent() 方法获取 SystemUIService 组件的路径,最后再调用 startServiceAsUser() 方法启动 SystemUIService 服务。

二、启动信息详解

        这里主要通过使用 Context 和 WindowManagerService 来初始化并启动 SystemUI

1、获取服务信息

PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
pm.getSystemUiServiceComponent();

        这里使用 LocalServices.getService() 方法来获取 PackageManagerInternal 的单例实例。然后通过 PackageManagerInternal 中的 getSystemUiServiceComponent() 方法获取 SystemUI 的包名和类名。 

PackageManagerInternal

源码位置:/frameworks/base/services/core/java/android/content/pm/PackageManagerInternal.java

public abstract class PackageManagerInternal implements PackageSettingsSnapshotProvider {……/*** 返回 SystemUI 服务组件名*/public abstract ComponentName getSystemUiServiceComponent();
}

        PackageManagerInternal 是一个抽象类,同样 getSystemUiServiceComponent() 也是抽象方法,该方法在 PackageManagerService 的内部类 PackageManagerInternalImpl 中进行了具体实现。

PackageManagerService

源码位置:/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

private class PackageManagerInternalImpl extends PackageManagerInternal {……public ComponentName getSystemUiServiceComponent() {return ComponentName.unflattenFromString(mContext.getResources().getString(com.android.internal.R.string.config_systemUIServiceComponent));}……
}

        可以看到 ComonentName 是从一个内部资源字符串com.android.internal.R.string.config_systemUIServiceComponent 获取 SystemUIService 组件完整类名的。

config.xml

源码位置:/frameworks/base/core/res/res/values/config.xml

<!-- SystemUi service component -->
<string name="config_systemUIServiceComponent" translatable="false">com.android.systemui/com.android.systemui.SystemUIService</string>

        可以发现 config_systemUIServiceComponent 这个资源字符串的具体位置和内容如上所示。

2、创建 Intent 对象

Intent intent = new Intent();
intent.setComponent(pm.getSystemUiServiceComponent());
intent.addFlags(Intent.FLAG_DEBUG_TRIADED_MISSING);
  • 创建一个新的 Intent 对象,用于描述要启动的服务。
  • 通过 setComponent() 指定了要启动的服务(即 SystemUI)的包名和类名时。
  • 通过 addFlags() 设置相关标识,FLAG_DEBUG_TRIAGED_MISSING 标志,这个标志通常用于调试目的,可以帮助追踪某些类型的错误或问题。

3、启动服务

//Slog.d(TAG, "Starting service: " + intent);
context.startServiceAsUser(intent, UserHandle.SYSTEM);
  • 注释掉的日志记录语句原本可以用来输出调试信息。
  • 使用 context.startServiceAsUser() 方法以 UserHandle.SYSTEM 用户身份启动服务。这意味着服务将以系统的名义运行,拥有更高的权限。

Context 

源码位置:/frameworks/base/core/java/android/content/Context.java 

public abstract ComponentName startServiceAsUser(Intent service, UserHandle user);

         这里调用了 startServiceAsUser() 方法来启动服务,而该方法的实现是在 ContextImpl 中实现。

ContextImpl

源码位置:/frameworks/base/core/java/android/app/ContextImpl.java

public ComponentName startServiceAsUser(Intent service, UserHandle user) {return startServiceCommon(service, false, user);
}private ComponentName startServiceCommon(Intent service, boolean requireForeground, UserHandle user) {try {// 验证服务意图是否有效validateServiceIntent(service);// 准备将 Intent 发送到另一个进程service.prepareToLeaveProcess(this);// 实际启动服务ComponentName cn = ActivityManager.getService().startService(mMainThread.getApplicationThread(), service,service.resolveTypeIfNeeded(getContentResolver()), requireForeground,getOpPackageName(), getAttributionTag(), user.getIdentifier());……return cn;} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
}
  • mMainThread.getApplicationThread() 提供了当前应用线程的信息。
  • service.resolveTypeIfNeeded(getContentResolver()) 解析并确定服务的 MIME 类型(如果需要)。
  • requireForeground 决定服务是否应该以前台服务的形式启动。
  • getOpPackageName() 和 getAttributionTag() 分别提供操作包名和归属标签,用于追踪和记录目的。
  • user.getIdentifier() 指定服务启动时使用的用户 ID。

         真正的启动服务涉及到与 ActivityManagerService 的通信,后者负责管理所有应用程序的生命周期和状态。具体逻辑这里就不做过多介绍了,其中《AMS》专栏已经做了详细介绍。 

4、通知WMS

windowManager.onSystemUiStarted();

        调用 windowManager.onSystemUiStarted() 方法,通知 WindowManagerService SystemUI 已经开始启动。这一步对于确保窗口管理器和其他系统组件知道 SystemUI 的状态非常重要。

WindowManagerService 

源码位置:/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

WindowManagerPolicy mPolicy;public void onSystemUiStarted() {mPolicy.onSystemUiStarted();
}

        WindowManagerPolicy  同样是一个接口类,具体实现是在 PhoneWindowManager 中。

PhoneWindowManager

源码位置:/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

@Override
public void onSystemUiStarted() {bindKeyguard();
}private void bindKeyguard() {synchronized (mLock) {if (mKeyguardBound) {return;}mKeyguardBound = true;}mKeyguardDelegate.bindService(mContext);
}

        这里主要功能是绑定锁屏(Keyguard)服务,为了确保 Android 设备能够在启动后立即提供安全可靠的锁屏功能,并与其他系统组件无缝协作,为用户提供一致且流畅的交互体验。


http://www.ppmy.cn/server/158273.html

相关文章

MySQL 中联合索引相比单索引性能提升在哪?

首先我们要清楚所以也是要占用磁盘空间的&#xff0c;随着表中数据量越来越多&#xff0c;索引的空间也是随之提升的&#xff0c;因而单表不建议定义过多的索引&#xff0c;所以使用联合索引可以在一定程度上可以减少索引的空间占用其次&#xff0c;使用联合索引的情况下&#…

拷贝构造函数

文章目录 一、4. 拷贝构造函数 今天我们来学习拷贝构造函数。 一、4. 拷贝构造函数 如果⼀个构造函数的第⼀个参数是自身类型的引用&#xff0c;且任何额外的参数都有默认值&#xff0c;则此叫做拷贝构造函数&#xff0c;也就是说拷贝构造是⼀个特殊的构造函数。 它的形式是这…

docker虚拟机平台未启用问题

在终端中输入如下代码&#xff0c;重启电脑即可 Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform 对于Docker Desktop - Unexpected WSL error问题 参考链接 解决WSL2与docker冲突问题

青少年编程与数学 02-006 前端开发框架VUE 24课题、UI表单

青少年编程与数学 02-006 前端开发框架VUE 24课题、UI表单 一、UI表单二、Element Plus表单1. 安装Element Plus2. 配置Element Plus3. 创建表单组件4. 在App.vue中使用表单组件5. 运行项目 三、Vuetify表单1. 安装Vuetify2. 配置Vuetify3. 创建表单组件4. 在App.vue中使用表单…

Untiy中如何嵌入前端页面,从而播放推流视频?

最近工作中频繁用到unity,虽然楼主之前也搞过一些UNTY游戏开发项目&#xff0c;但对于视频这块还是不太了解&#xff0c;所以我们采用的方案是在Unity里寻找一个插件来播放推流视频。经过我的一番寻找&#xff0c;发现了这款Vuplex 3D WebView&#xff0c;它可以完美的打通Unit…

【Excel】【VBA】根据某列的编号顺序筛选对应的行导入相应的sheet中

Excel VBA 数据分类导入sheet 1. 程序功能 将Excel表格数据按照PC编号分类到不同Sheet。 2. 程序流程 #mermaid-svg-mKz9P9fN9JJgn75t {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-mKz9P9fN9JJgn75t .error-ic…

iOS - runtime总结

详细总结一下 Runtime 的核心内容&#xff1a; 1. 消息发送机制 // 消息发送的基本流程 id objc_msgSend(id self, SEL _cmd, ...) {// 1. 获取 isaClass cls object_getClass(self);// 2. 查找缓存IMP imp cache_getImp(cls, _cmd);if (imp) return imp(self, _cmd, ...);…

开源AI模型的优势、挑战与未来发展分析

开放源代码模型在灵活性和生态系统方面具有竞争力&#xff0c;可能会超越闭源API 开放源代码模型在灵活性和生态系统方面的竞争力主要体现在以下几个方面&#xff1a; 1. 灵活性 定制化能力: 开放源代码模型允许用户根据特定需求进行修改和调整。这种灵活性使得开发者能够根…