延迟启动
本文面向已经读过 BootPhase阶段、服务启动耗时、启动时间分析 和 并行启动 的读者。前面的文章解决“什么时候推进 phase”和“任务怎样并行”;本文继续追踪另一个常见优化动作:把非关键工作移出开机关键路径。
“延迟服务启动”在源码中至少有四种不同含义:SystemService 对象已经创建,只把重工作推迟到 phase;phase 回调再把工作投递给长期线程;Handler/ScheduledExecutor 按时间延后或合并;独立 native Binder 进程由首次客户端请求拉起。它们的 owner、可见状态和失败语义完全不同。读完后,你应能判断某段代码只是换了执行时间,还是改变了服务可用契约,并能指出 sys.boot_completed 是否仍会被它阻塞。
1. 四种延迟
| 模式 | 对象何时存在 | 工作何时执行 | 典型 owner | 是否阻塞 boot completed |
|---|---|---|---|---|
| Phase 延后 | 服务组启动时 | 指定 onBootPhase() | SystemServiceManager | 同步 callback 会阻塞 |
| 异步投递 | 服务已存在 | callback 中 execute/post | IoThread、服务 Executor | callback 返回后通常不阻塞 |
| 定时延后 | 服务已存在 | 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
// Let system services know.
mSystemServiceManager.startBootPhase(
t, SystemService.PHASE_BOOT_COMPLETED);phase 返回后,AMS 先释放 hold 进程并处理低级 factory-test 分支,随后才在同一个锁区间设置启动完成属性:
// 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
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
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
@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
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。
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,调用链必须接受尚未加载或失败的状态。
@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
@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 和延迟写入在同一执行序列中,减少内部操作乱序。
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 不持锁。
@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 线程。
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
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.javaframeworks/base/services/core/java/com/android/server/am/ActivityManagerConstants.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 内硬编码:
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
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
if (mPackageManager.hasSystemFeature(
PackageManager.FEATURE_CONTEXT_HUB)) {
t.traceBegin("StartContextHubSystemService");
mSystemServiceManager.startService(
ContextHubSystemService.class);
t.traceEnd();
}build variant 是另一种创建条件;当前 tag 只在 debuggable/eng 构建中创建 CpuMonitorService:
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
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 线程。
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
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
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 的活跃客户端。客户端归零时进入下面的注销与退出路径:
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. 失败与一致性
延迟工作从启动主线移走后,失败也会从同步异常变成异步状态。设计时必须回答:谁记录失败、消费者看见什么、是否重试、进程重启后能否恢复。
| 模式 | 失败如何出现 | 消费者策略 |
|---|---|---|
同步 onBootPhase | RuntimeException 传播,阻断 phase/属性 | 仅用于开机必须成功的状态 |
IoThread execute | callback 已返回,错误由任务/线程处理 | null、fallback、重试或 ready signal |
| service Executor | Future 未保存时 phase 不感知 | 内存状态需有缺失语义 |
postDelayed | 到期前可能被取消或进程死亡 | 工作必须可丢弃或可重建 |
| lazy Binder | init start/注册失败,客户端继续等不到 service | rc/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
@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
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
@Override
public Executor getBackgroundExecutor() {
// Just execute immediately in tests.
return r -> r.run();
}这让状态断言确定,但没有覆盖生产 single-thread executor 的窗口和竞争。阅读异步测试时必须先看 Executor 注入,不能仅凭生产代码中的 execute() 推断测试真的跨线程。
运行时定位应同时观察触发点、队列和最终状态:
# 输入:一次完整开机的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 实现“无人使用就退出”。这两个边界决定了延迟优化是安全的状态迁移,还是把竞态留给第一个客户端。
