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 设备能够在启动后立即提供安全可靠的锁屏功能,并与其他系统组件无缝协作,为用户提供一致且流畅的交互体验。

相关推荐
c1assy17 分钟前
天机学堂3-ES+Caffeine
android·java·elasticsearch
Dnelic-8 小时前
Kotlin | Android Provider 的实现案例
android·数据库·kotlin·provider·自学笔记
walkskyer11 小时前
Golang strconv包详解:高效类型转换实战
android·开发语言·golang
氦客11 小时前
Android Compose 显示底部对话框 (ModalBottomSheet),实现类似BottomSheetDialog的效果
android·dialog·ui·compose·modal·bottomsheet·底部对话框
我命由我1234511 小时前
Android Room 构建问题:There are multiple good constructors
android·开发语言·java-ee·android studio·android jetpack·android-studio·android runtime
zhangphil12 小时前
Android ValueAnimator根据屏幕刷率动态出帧/刷新,Kotlin
android·kotlin
芝士就是力量啊 ೄ೨12 小时前
Kotlin 循环语句详解
android·java·开发语言·kotlin
Alex老夫子12 小时前
kotlin sortedBy 与sortedWith的区别
android·开发语言·kotlin
Android采码蜂13 小时前
SurfaceFlinger07-Layer销毁流程
android