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

在 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

java 复制代码
public final class SystemServer implements Dumpable {
    ......
    public static void main(String[] args) {
        new SystemServer().run();
    }
}

这里启动了 run() 方法。

run

java 复制代码
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

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

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

startSystemUi

java 复制代码
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、获取服务信息

java 复制代码
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

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

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

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

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

2、创建 Intent 对象

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

3、启动服务

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

Context

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

java 复制代码
public abstract ComponentName startServiceAsUser(Intent service, UserHandle user);

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

ContextImpl

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

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

java 复制代码
windowManager.onSystemUiStarted();

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

WindowManagerService

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

java 复制代码
WindowManagerPolicy mPolicy;

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

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

PhoneWindowManager

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

java 复制代码
@Override
public void onSystemUiStarted() {
    bindKeyguard();
}

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

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

相关推荐
踢球的打工仔14 小时前
PHP面向对象(7)
android·开发语言·php
安卓理事人14 小时前
安卓socket
android
安卓理事人20 小时前
安卓LinkedBlockingQueue消息队列
android
万能的小裴同学21 小时前
Android M3U8视频播放器
android·音视频
q***57741 天前
MySql的慢查询(慢日志)
android·mysql·adb
JavaNoober1 天前
Android 前台服务 "Bad Notification" 崩溃机制分析文档
android
城东米粉儿1 天前
关于ObjectAnimator
android
zhangphil1 天前
Android渲染线程Render Thread的RenderNode与DisplayList,引用Bitmap及Open GL纹理上传GPU
android
火柴就是我1 天前
从头写一个自己的app
android·前端·flutter
lichong9511 天前
XLog debug 开启打印日志,release 关闭打印日志
android·java·前端