Skip to content

延迟启动

区分 BootPhase 延后、异步投递、定时合并、条件创建与 lazy Binder 按需拉起的真实生效边界。

基于android-17.0.0_r1
AndroidSystemServerBootPhaseHandlerLazyService性能源码阅读

延迟启动 ​

本文面向已经读过 BootPhase阶段、服务启动耗时、启动时间分析 和 并行启动 的读者。前面的文章解决“什么时候推进 phase”和“任务怎样并行”;本文继续追踪另一个常见优化动作:把非关键工作移出开机关键路径。

“延迟服务启动”在源码中至少有四种不同含义:SystemService 对象已经创建,只把重工作推迟到 phase;phase 回调再把工作投递给长期线程;Handler/ScheduledExecutor 按时间延后或合并;独立 native Binder 进程由首次客户端请求拉起。它们的 owner、可见状态和失败语义完全不同。读完后,你应能判断某段代码只是换了执行时间,还是改变了服务可用契约,并能指出 sys.boot_completed 是否仍会被它阻塞。

1. 四种延迟 ​

模式对象何时存在工作何时执行典型 owner是否阻塞 boot completed
Phase 延后服务组启动时指定 onBootPhase()SystemServiceManager同步 callback 会阻塞
异步投递服务已存在callback 中 execute/postIoThread、服务 Executorcallback 返回后通常不阻塞
定时延后服务已存在postDelayed/schedule 到期Handler/ScheduledExecutor不阻塞,但需处理过期状态
请求拉起进程尚未运行Binder 首次查找servicemanager + init独立进程,不属于 system_server phase

SystemServer 的 bootstrap/core/other 分组只是依赖分组。一个服务排在 startOtherServices() 后半段,不等于它是 lazy service;它仍会在开机过程中同步构造并执行 onStart()。

2. Phase并非终点 ​

PHASE_BOOT_COMPLETED 的名称容易让人误以为 sys.boot_completed 已经是 1。真实顺序相反:AMS 先同步调用所有 SystemService 的 phase 1000 callback,SystemServiceManager 随后关闭初始化线程池,AMS 才继续设置属性。

源码文件:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

java
// Let system services know.
mSystemServiceManager.startBootPhase(
        t, SystemService.PHASE_BOOT_COMPLETED);

phase 返回后,AMS 先释放 hold 进程并处理低级 factory-test 分支,随后才在同一个锁区间设置启动完成属性:

java
// Tell anyone interested that we are done booting!
    SystemProperties.set("sys.boot_completed", "1");
    SystemProperties.set("dev.bootcomplete", "1");

SystemServiceManager 不会把 callback 当作 fire-and-forget。串行模式直接调用;并行 BootPhase flag 开启时也会等待所有 Future。任何同步 I/O、Binder wait 或锁竞争都会推迟属性写入、init 的 on property:sys.boot_completed=1 动作和 bootchart stop。

源码文件:frameworks/base/services/core/java/com/android/server/SystemServiceManager.java

java
if (phase == SystemService.PHASE_BOOT_COMPLETED) {
    final long totalBootTime =
            SystemClock.uptimeMillis()
            - mRuntimeStartUptime;
    t.logDuration("TotalBootTime", totalBootTime);
    shutdownInitThreadPool();
}

初始化线程池在 callback 全部返回后立即 shutdown,并最多等待其中未完成的任务。因此 phase 1000 callback 把工作提交回 SystemServerInitThreadPool,并不是真正延后:shutdown 仍会等待它,任务过慢还会触发 20 秒 timeout。需要脱离启动屏障的工作应交给 IoThread、BackgroundThread、服务自己的 HandlerThread 或 Executor。

3. IoThread延后 ​

AutofillManagerService 在 startOtherServices() 中按设备 feature 创建,Binder 服务已经存在;它只把持久 master seed 的文件 I/O 延迟到 phase 1000,并继续投递给 IoThread。

源码文件:frameworks/base/services/java/com/android/server/SystemServer.java

java
if (mPackageManager.hasSystemFeature(
        PackageManager.FEATURE_AUTOFILL)) {
    t.traceBegin("StartAutoFillService");
    mSystemServiceManager.startService(
            AutofillManagerService.class);
    t.traceEnd();
}

源码文件:frameworks/base/services/autofill/java/com/android/server/autofill/AutofillManagerService.java

java
@Override
public void onBootPhase(int phase) {
    super.onBootPhase(phase);
    if (phase == PHASE_BOOT_COMPLETED) {
        // Loading the masterseed involves file I/O using the IoThread, hence doing it after
        // boot completed to avoid regressing boot time.
        if (Flags.stringRebuildPersistentMasterseed()) {
            Slog.d(TAG,
                    "Loading master seed from the storage");
            IoThread.getExecutor().execute(
                    this::loadNoiseInjectionMasterSeed);
        } else {
            Slog.d(TAG,
                    "Persistent masterseed flag is OFF, generating new seed");
            mNoiseInjectionMasterSeed.set(
                    UUID.randomUUID().toString());
        }
    }
}

IoThread 是 system_server 进程级长寿命线程。它按第一次访问延迟创建,HandlerExecutor 只负责把 Runnable 入队,不返回 Future。

源码文件:frameworks/base/services/core/java/com/android/server/IoThread.java

java
public final class IoThread extends ServiceThread {
    private static final class NoPreloadHolder {
        private static final IoThread sInstance =
                new IoThread();
    }

    private final Handler mHandler;
    private final HandlerExecutor mHandlerExecutor;

    private IoThread() {
        super("android.io",
                android.os.Process.THREAD_PRIORITY_DEFAULT,
                true /*allowIo*/);
        start();
        final Looper looper = getLooper();
        looper.setTraceTag(
                Trace.TRACE_TAG_SYSTEM_SERVER);
        mHandler = new Handler(looper,
                /*callback=*/ null,
                /* async=*/ false,
                /* shared=*/ true);
        mHandlerExecutor =
                new HandlerExecutor(mHandler);
    }

    public static Executor getExecutor() {
        return NoPreloadHolder.sInstance
                .mHandlerExecutor;
    }
}

master seed 的 owner 是 AtomicReference<String>。初始值为 null;worker 读取 AtomicFile,文件缺失时生成 UUID 并原子写入,写失败则保持 null。

java
private void loadNoiseInjectionMasterSeed() {
    String seed = null;
    AtomicFile noiseSeedFile = new AtomicFile(getNoiseSeedFile());
    try {
        seed = new String(noiseSeedFile.readFully(), StandardCharsets.UTF_8);
        if (TextUtils.isEmpty(seed)) {
            // Treat empty file as no seed
            seed = null;
        } else {
            Slog.i(TAG, "Loaded existing noise injection master seed.");
        }
    } catch (IOException e) {
        Slog.i(TAG, "No existing noise injection master seed file found.");
    }

    // If no seed is loaded, generate a new one and save it to the file.
    if (seed == null) {
        seed = UUID.randomUUID().toString();
        FileOutputStream fos = null;
        try {
            // Ensure directory exists
            File parentDir = noiseSeedFile.getBaseFile().getParentFile();
            if (!parentDir.exists() && !parentDir.mkdirs()) {
                Slog.e(TAG, "Failed to create directory for noise seed: " + parentDir);
                mNoiseInjectionMasterSeed.set(null);
                return;
            }
            fos = noiseSeedFile.startWrite();
            fos.write(seed.getBytes(StandardCharsets.UTF_8));
            noiseSeedFile.finishWrite(fos);
            Slog.i(TAG, "Generated and wrote new noise injection master seed.");
        } catch (IOException e) {
            Slog.e(TAG, "Failed to write noise injection master seed to file.", e);
            if (fos != null) {
                noiseSeedFile.failWrite(fos);
            }
            // Set seed to null on failure
            seed = null;
        }
    }
    mNoiseInjectionMasterSeed.set(seed);
}

消费者没有等待屏障。请求先到时 getter 记录错误并返回 null,调用链必须接受尚未加载或失败的状态。

java
@Nullable
String getNoiseInjectionMasterSeedValue() {
    String seed =
            mNoiseInjectionMasterSeed.get();
    if (seed == null) {
        Slog.e(TAG,
                "Noise injection master seed not loaded yet.");
    }
    return seed;
}

这是一种“最终可用、允许短暂降级”的模型,不适合必须在第一个 Binder 请求前完成的安全状态。若状态是强依赖,应保存 Future/CompletableFuture,或推迟接口发布,而不是只换 Executor。

4. 服务自有Executor ​

AppHibernationService 更完整地展示了“服务早发布、数据晚恢复”。SystemServer 无条件创建对象;构造器注册 package receiver、LocalService 和 usage listener,onStart() 发布 Binder。全局磁盘状态直到 phase 1000 才由服务自己的 single-thread scheduled executor 读取。

源码文件:frameworks/base/services/core/java/com/android/server/apphibernation/AppHibernationService.java

java
@Override
public void onStart() {
    publishBinderService(
            Context.APP_HIBERNATION_SERVICE,
            mServiceStub);
}

@Override
public void onBootPhase(int phase) {
    if (phase == PHASE_BOOT_COMPLETED) {
        mBackgroundExecutor.execute(() -> {
            List<GlobalLevelState> states =
                    mGlobalLevelHibernationDiskStore
                            .readHibernationStates();
            synchronized (mLock) {
                initializeGlobalHibernationStates(
                        states);
            }
        });
    }
    if (phase ==
            SystemService.PHASE_SYSTEM_SERVICES_READY) {
        sIsServiceEnabled =
                isDeviceConfigAppHibernationEnabled();
        DeviceConfig.addOnPropertiesChangedListener(
                NAMESPACE_APP_HIBERNATION,
                ActivityThread.currentApplication()
                        .getMainExecutor(),
                this::onDeviceConfigChanged);
    }
}

生产 Injector 建立一个服务私有的 single-thread scheduled executor;读取、hibernate/unhibernate 和延迟写入在同一执行序列中,减少内部操作乱序。

java
private static final class InjectorImpl
        implements Injector {
    private final ScheduledExecutorService
            mScheduledExecutorService;

    InjectorImpl(Context context) {
        mContext = context;
        mScheduledExecutorService =
                Executors.newSingleThreadScheduledExecutor();
        mUserLevelHibernationProto =
                new UserLevelHibernationProto();
    }

    @Override
    public Executor getBackgroundExecutor() {
        return mScheduledExecutorService;
    }
}

worker 先读取 disk snapshot,再在 mLock 下建立以当前 installed packages 为基准的 map,最后用有效磁盘记录覆盖默认状态。锁只保护内存发布,磁盘 I/O 不持锁。

java
@GuardedBy("mLock")
private void initializeGlobalHibernationStates(
        @Nullable List<GlobalLevelState> diskStates) {
    List<PackageInfo> packages;
    try {
        packages = mIPackageManager
                .getInstalledPackages(
                        PACKAGE_MATCH_FLAGS
                                | MATCH_ANY_USER,
                        0 /* userId */)
                .getList();
    } catch (RemoteException e) {
        throw new IllegalStateException(
                "Package manager not available", e);
    }

    for (int i = 0, size = packages.size();
            i < size; i++) {
        String packageName =
                packages.get(i).packageName;
        GlobalLevelState state =
                new GlobalLevelState();
        state.packageName = packageName;
        mGlobalHibernationStates.put(
                packageName, state);
    }

服务已经对外可见,因此消费者必须定义“数据尚未恢复”的结果。这里查询发现 state 不存在时返回 false,而不是阻塞 Binder 线程。

java
boolean isHibernatingGlobally(
        String packageName) {
    if (!sIsServiceEnabled) {
        return false;
    }
    getContext().enforceCallingOrSelfPermission(
            android.Manifest.permission.MANAGE_APP_HIBERNATION,
            "Caller does not have MANAGE_APP_HIBERNATION permission.");
    synchronized (mLock) {
        GlobalLevelState state =
                mGlobalHibernationStates.get(
                        packageName);
        if (state == null
                || !mPackageManagerInternal
                        .canQueryPackage(
                                Binder.getCallingUid(),
                                packageName)) {
            return false;
        }
        return state.hibernated;
    }
}

返回 false 是明确降级,但也意味着初始化完成前不能区分“确实未休眠”和“状态尚未载入”。如果业务需要区分,应增加 ready 状态或回调,而不是依靠空 map 推断。

5. 定时与合并 ​

“延迟”有时不是为了等待 BootPhase,而是等待系统状态稳定或合并高频更新。

5.1 稳定窗口 ​

CpuMonitorService 在 onStart() 已创建 HandlerThread 并开始调试监控。phase 1000 到达后,它仍等待两分钟再停止 periodic cpuset reading,因为设备实现可能在 boot completed 后继续更新 cpuset,传播需要时间。

源码文件:frameworks/base/services/core/java/com/android/server/cpu/CpuMonitorService.java

java
private static final long
        STOP_PERIODIC_CPUSET_READING_DELAY_MILLISECONDS =
        TimeUnit.MINUTES.toMillis(2);

@Override
public void onBootPhase(int phase) {
    if (phase != PHASE_BOOT_COMPLETED
            || mHandler == null) {
        return;
    }
    Slogf.i(TAG,
            "Stopping periodic cpuset reading on boot complete");
    mHandler.postDelayed(
            () -> mCpuInfoReader
                    .stopPeriodicCpusetReading(),
            mStopPeriodicCpusetReadingDelayMillis);
}

owner 是 CpuMonitorService 的 Handler;phase callback 只入队。若服务生命周期允许重启或配置变化,必须考虑移除旧 callback。当前服务随 system_server 存活,任务无需跨进程持久化。

AMS 还有一个更晚的例子:sys.boot_completed 设置后,UserController 完成 boot broadcast 回调,AMS 才记录 timestamp,并按 FULL_PSS_MIN_INTERVAL 延迟全进程 PSS。默认间隔是 20 分钟。

源码文件:

  • frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
  • frameworks/base/services/core/java/com/android/server/am/ActivityManagerConstants.java
java
mUserController.onBootComplete(
        new IIntentReceiver.Stub() {
    @Override
    public void performReceive(
            Intent intent, int resultCode,
            String data, Bundle extras,
            boolean ordered, boolean sticky,
            int sendingUser) {
        mBootCompletedTimestamp =
                SystemClock.uptimeMillis();
        mHandler.postDelayed(() -> {
            synchronized (mProcLock) {
                mAppProfiler.requestPssAllProcsLPr(
                        SystemClock.uptimeMillis(),
                        true, false);
            }
        }, mConstants.FULL_PSS_MIN_INTERVAL);
    }
});

该 Handler delay 的默认值来自 ActivityManagerConstants,而不是 callback 内硬编码:

java
private static final long
        DEFAULT_FULL_PSS_MIN_INTERVAL =
        20 * 60 * 1000;

long FULL_PSS_MIN_INTERVAL =
        DEFAULT_FULL_PSS_MIN_INTERVAL;

这里的触发点已经晚于属性写入,而且还等待 boot broadcast completion,再延迟 20 分钟。它不应计入启动完成耗时,但可能影响后续内存和电量测试窗口。

5.2 写入合并 ​

AppHibernation 的 disk store 将写入延后一分钟。后续状态变化只替换 mScheduledStatesToWrite,若已有 Future 就不创建第二个定时任务;到期后写入最新 snapshot。

源码文件:frameworks/base/services/core/java/com/android/server/apphibernation/HibernationStateDiskStore.java

java
private static final long DISK_WRITE_DELAY =
        1L * DateUtils.MINUTE_IN_MILLIS;

private List<T> mScheduledStatesToWrite =
        new ArrayList<>();
private ScheduledFuture<?> mFuture;

void scheduleWriteHibernationStates(
        @NonNull List<T> hibernationStates) {
    synchronized (this) {
        mScheduledStatesToWrite =
                hibernationStates;
        if (mExecutorService.isShutdown()) {
            Slog.e(TAG,
                    "Scheduled executor service is shut down.");
            return;
        }
        if (mFuture != null) {
            Slog.i(TAG,
                    "Write already scheduled. Skipping schedule.");
            return;
        }
        mFuture = mExecutorService.schedule(
                this::writeHibernationStates,
                DISK_WRITE_DELAY,
                TimeUnit.MILLISECONDS);
    }
}

这不是节流到“丢弃后续状态”,而是 debounce:Future 不变,payload 更新。正常写完后 mFuture=null,下一批变化才能建立新窗口。崩溃或断电发生在一分钟内时,最新内存状态可能未落盘,因此这种优化只适合可重建或允许短暂丢失的数据。

6. 条件创建 ​

SystemServer 常用 feature、build variant 或 DeviceConfig 决定是否创建服务对象。这能避免不支持设备承担构造、线程和 Binder 成本,但不是运行时首次请求拉起。

源码文件:frameworks/base/services/java/com/android/server/SystemServer.java

java
if (mPackageManager.hasSystemFeature(
        PackageManager.FEATURE_CONTEXT_HUB)) {
    t.traceBegin("StartContextHubSystemService");
    mSystemServiceManager.startService(
            ContextHubSystemService.class);
    t.traceEnd();
}

build variant 是另一种创建条件;当前 tag 只在 debuggable/eng 构建中创建 CpuMonitorService:

java
if (Build.IS_DEBUGGABLE || Build.IS_ENG) {
    t.traceBegin("CpuMonitorService");
    mSystemServiceManager.startService(
            CpuMonitorService.class);
    t.traceEnd();
}

条件为 false 时对象、Binder 和生命周期 callback 全部不存在,客户端必须先用 feature API 或可空 service 查询判断能力。条件判断发生在开机期间,一旦创建通常不会因 feature 后续改变而卸载。

7. Lazy Binder ​

真正的“客户端请求才启动”由 servicemanager 与 init 协作,适用于独立 native Binder 进程。getService() 找不到 service 时会请求拉起;checkService() 则只检查,不触发启动。

源码文件:frameworks/native/cmds/servicemanager/ServiceManager.cpp

cpp
Status ServiceManager::getService(
        const std::string& name,
        sp<IBinder>* outBinder) {
    *outBinder = tryGetBinder(
            name, true).service;
    return Status::ok();
}

Status ServiceManager::checkService(
        const std::string& name,
        sp<IBinder>* outBinder) {
    *outBinder = tryGetBinder(
            name, false).service;
    return Status::ok();
}

missing service 且 startIfNotFound=true 时,servicemanager 在 detached thread 设置 ctl.interface_start=aidl/<name>,避免阻塞自身 Binder 线程。

cpp
void ServiceManager::tryStartService(
        const Access::CallingContext& ctx,
        const std::string& name) {
    ALOGI("%s Since '%s' could not be found trying to start it as a lazy AIDL service. (if it's "
          "not configured to be a lazy service, it may be stuck starting or still starting).",
          ctx.toDebugString().c_str(),
          name.c_str());

    std::thread([=] {
        if (!base::SetProperty("ctl.interface_start", "aidl/" + name)) {
            ALOGI("%s Tried to start aidl service %s as a lazy service, but was unable to. Usually "
                  "this happens when a service is not installed, but if the service is intended to "
                  "be used as a lazy service, then it may be configured incorrectly.",
                  ctx.toDebugString().c_str(), name.c_str());
        }
    }).detach();
}

init 根据 service 的 interface aidl <instance> 配置查找 owner 并启动进程。

源码文件:system/core/init/builtins.cpp

cpp
static Result<void> do_interface_start(
        const BuiltinArguments& args) {
    Service* svc = ServiceList::GetInstance()
            .FindInterface(args[1]);
    if (!svc) {
        return Error()
                << "interface " << args[1]
                << " not found";
    }
    if (auto result = svc->Start();
            !result.ok()) {
        return Error()
                << "Could not start interface: "
                << result.error();
    }
    return {};
}

server 进程通过 LazyServiceRegistrar 注册 lazy flag 和 client callback。当所有 lazy interfaces 都没有客户端时,它尝试注销并退出;init service 必须配置 disabled、oneshot 和 interface。

源码文件:frameworks/native/libs/binder/LazyServiceRegistrar.cpp

cpp
bool ClientCounterCallbackImpl::registerServiceLocked(
        const sp<IBinder>& service,
        const std::string& name,
        bool allowIsolated,
        int dumpFlags) {
    auto manager = interface_cast<AidlServiceManager>(
            asBinder(defaultServiceManager()));

    bool reRegister = mRegisteredServices.count(name) > 0;
    std::string regStr = (reRegister)
            ? "Re-registering" : "Registering";
    ALOGI("%s service %s", regStr.c_str(), name.c_str());

    if (dumpFlags & android::os::IServiceManager::FLAG_IS_LAZY_SERVICE) {
        ALOGW("FLAG_IS_LAZY_SERVICE flag already set. This should only be set by "
              "ClientCounterCallbackImpl in LazyServiceRegistrar");
    }
    dumpFlags |= android::os::IServiceManager::FLAG_IS_LAZY_SERVICE;

    if (Status status = manager->addService(
            name.c_str(), service,
            allowIsolated, dumpFlags);
            !status.isOk()) {
        ALOGE("Failed to register service %s (%s)",
              name.c_str(), status.toString8().c_str());
        return false;
    }

    if (!reRegister) {
        if (Status status = manager->registerClientCallback(
                name, service,
                sp<android::os::IClientCallback>::fromExisting(this));
                !status.isOk()) {
            ALOGE("Failed to add client callback for service %s (%s)",
                  name.c_str(), status.toString8().c_str());
            return false;
        }

        mRegisteredServices[name] = {
              .service = service,
              .allowIsolated = allowIsolated,
              .dumpFlags = dumpFlags
        };
    }

    return true;
}

注册完成后,client callback 负责跟踪这个进程中所有 lazy interfaces 的活跃客户端。客户端归零时进入下面的注销与退出路径:

cpp
void ClientCounterCallbackImpl::tryShutdownLocked() {
    ALOGI("Trying to shut down the service. No clients in use for any service in process.");

    if (tryUnregisterLocked()) {
        ALOGI("Unregistered all clients and exiting");
        exit(EXIT_SUCCESS);
    }

    reRegisterLocked();
}

完整请求路径由客户端查找、servicemanager 触发 init、server 注册和零客户端退出四个阶段组成:

system_server 不能套用这个模型:它承载大量 Java/Binder 服务,不能因为某一个接口无人使用就退出整个进程。system_server 内的“按需”通常是 feature gate、延迟创建内部对象或首次调用初始化,而不是真正进程级 lazy Binder。

8. 失败与一致性 ​

延迟工作从启动主线移走后,失败也会从同步异常变成异步状态。设计时必须回答:谁记录失败、消费者看见什么、是否重试、进程重启后能否恢复。

模式失败如何出现消费者策略
同步 onBootPhaseRuntimeException 传播,阻断 phase/属性仅用于开机必须成功的状态
IoThread executecallback 已返回,错误由任务/线程处理null、fallback、重试或 ready signal
service ExecutorFuture 未保存时 phase 不感知内存状态需有缺失语义
postDelayed到期前可能被取消或进程死亡工作必须可丢弃或可重建
lazy Binderinit start/注册失败,客户端继续等不到 servicerc/interface/SELinux/注册日志联合排查

AppHibernation 查询在状态未建立时返回 false;Autofill getter 返回 null;两者都是显式的不完整状态。CpuMonitor 的延迟任务只是停止临时采样,即使 system_server 在两分钟内重启,新实例会重新建立正确状态。AMS PSS 也是可重新采集的数据。这些任务适合延后,因为失败不会损坏不可恢复的核心配置。

相反,权限表、package settings、SELinux policy 或用户密钥等状态不能只“post 后继续开机”。它们的消费者需要强一致,必须保留同步屏障或可靠事务。

9. 验证方法 ​

CpuMonitorService 的 testBootCompleted() 直接发送 phase 1000,随后与 service Handler 同步,再断言 stopPeriodicCpusetReading() 被调用。输入是可控的短 delay;断言证明 phase callback 只是投递,真正执行属于 HandlerThread。

源码文件:frameworks/base/services/tests/mockingservicestests/src/com/android/server/cpu/CpuMonitorServiceTest.java

java
@Test
public void testBootCompleted() throws Exception {
    mService.onBootPhase(PHASE_BOOT_COMPLETED);

    syncWithHandler(
            mServiceHandler,
            0 /* delayMillis */);

    verify(mMockCpuInfoReader,
            timeout(ASYNC_CALLBACK_WAIT_TIMEOUT_MILLISECONDS))
            .stopPeriodicCpusetReading();
}

HibernationStateDiskStoreTest 先调度 C,D,在定时任务执行前再调度 A,B,最后执行唯一 scheduled task 并读取磁盘,断言结果是 A,B。它证明后一次调用更新 payload、不会建立第二个 Future;没有证明一分钟 delay 在真实设备上不会因进程死亡丢失。

源码文件:frameworks/base/services/tests/servicestests/src/com/android/server/apphibernation/HibernationStateDiskStoreTest.java

java
mHibernationStateDiskStore
        .scheduleWriteHibernationStates(
                new ArrayList<>(
                        Arrays.asList("C", "D")));

List<String> toWrite =
        new ArrayList<>(
                Arrays.asList("A", "B"));
mHibernationStateDiskStore
        .scheduleWriteHibernationStates(toWrite);
mMockScheduledExecutorService
        .executeScheduledTask();

List<String> storedStrings =
        mHibernationStateDiskStore
                .readHibernationStates();
for (int i = 0; i < toWrite.size(); i++) {
    assertEquals(
            toWrite.get(i),
            storedStrings.get(i));
}

AppHibernationServiceTest 的 MockInjector 使用 r -> r.run(),所以测试里的 background executor 实际同步执行:

源码文件:frameworks/base/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java

java
@Override
public Executor getBackgroundExecutor() {
    // Just execute immediately in tests.
    return r -> r.run();
}

这让状态断言确定,但没有覆盖生产 single-thread executor 的窗口和竞争。阅读异步测试时必须先看 Executor 注入,不能仅凭生产代码中的 execute() 推断测试真的跨线程。

运行时定位应同时观察触发点、队列和最终状态:

bash
# 输入:一次完整开机的system_server日志。输出:phase回调和延迟任务的提交/失败日志。
adb logcat -b system -v threadtime -d \
  'SystemServiceManager:*' \
  'AutofillManagerService:*' \
  'AppHibernationService:*' \
  'CpuMonitorService:*' '*:S'

# 输入:源码树。输出:phase 1000同步工作、异步投递和定时延迟的候选位置。
rg -n 'PHASE_BOOT_COMPLETED|postDelayed\(|\.execute\(|\.schedule\(' \
  frameworks/base/services

# 输入:目标lazy AIDL instance。输出:init是否声明interface、进程是否运行、Binder是否注册。
adb shell dumpsys -l | rg 'your.interface.name'
adb shell getprop init.svc.your_lazy_service
adb shell logcat -b all -d \
  | rg -i 'trying to start it as a lazy AIDL service|ctl.interface_start|your.interface.name'

评估一项改动时至少比较 wm_boot_animation_done、sys.boot_completed、延迟任务首次可用时间和失败 fallback。属性更快但第一个真实请求拿到 null,说明成本被移到了用户路径;启动不变但 I/O 峰值从首屏窗口移开,仍可能是有价值的资源调度优化。

检查自己是否理解这篇文章,可以回答两个问题:为什么在 phase 1000 callback 中提交到 InitThreadPool 仍可能拖慢 sys.boot_completed,而提交到 IoThread 通常不会;以及一个 Java SystemService 为什么不能通过 LazyServiceRegistrar 实现“无人使用就退出”。这两个边界决定了延迟优化是安全的状态迁移,还是把竞态留给第一个客户端。