开机自启动预置App最简单、最快方案

很多开发者之所以觉得开机拉起慢,是因为采用了"等系统发 BOOT_COMPLETED 广播,App 再接收广播调 startActivity"的常规做法(由于广播排队,往往要等开机后 5~10 秒)。

要达到极致最快(开机动画结束的第 1 秒界面就已就绪),利用 persistent="true" 结合特权 Service / Direct Boot 才是最快、最优雅的官方正道。


第一步:APK 端配置(零多余代码,天然常驻)

在 APK 的 AndroidManifest.xml 中配置:

xml 复制代码
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.remote.control"
    android:sharedUserId="android.uid.system"> <!-- 建议绑定 system uid,获得最高系统权限 -->

    <application
        android:name=".RemoteApplication"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher"
        android:directBootAware="true"      <!-- 支持 Direct Boot,在用户解锁前即完成初始化 -->
        android:persistent="true">          <!-- 核心:标记为系统级常驻保活应用 -->

        <activity
            android:name=".ui.MainActivity"
            android:exported="true"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
</manifest>
极速启动核心:在 Application.onCreate() 中主动拉起主界面

因为标记了 persistent="true"系统开机阶段会自动创建 Application 进程 。在 Application 初始化的第一时间直接拉起 MainActivity完全不需要任何 Receiver,也不需要等待任何开机广播

kotlin 复制代码
package com.remote.control

import android.app.Application
import android.content.Intent
import android.util.Log
import com.remote.control.ui.MainActivity

class RemoteApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        Log.i("RemoteApp", "Persistent Process Started by SystemServer. Auto-launching UI...")

        // 系统拉起本进程后,直接将主界面推向前台
        val intent = Intent(this, MainActivity::class.java).apply {
            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or 
                     Intent.FLAG_ACTIVITY_CLEAR_TOP or 
                     Intent.FLAG_ACTIVITY_SINGLE_TOP)
        }
        startActivity(intent)
    }
}

第二步:系统源码预置配置(AOSP 端)

1. 编写 Android.bp

将现成的 RemoteControl.apk 放入 vendor/<vendor>/packages/apps/RemoteControl/

blueprint 复制代码
android_app_import {
    name: "RemoteControl",
    apk: "RemoteControl.apk",
    privileged: true,          // 安装到 /system/priv-app/ 成为特权 App
    certificate: "platform",   // 必须使用系统平台签名(persistent 应用必须是 platform 签名或系统应用)
    extract_jni: true,
    dex_preopt: {
        enabled: false,
    },
    required: ["privapp-permissions-remote.xml"],
}

prebuilt_etc {
    name: "privapp-permissions-remote.xml",
    sub_dir: "permissions",
    src: "privapp-permissions-remote.xml",
    filename_from_src: true,
}
2. 特权权限白名单 privapp-permissions-remote.xml
xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<permissions>
    <privapp-permissions package="com.remote.control">
        <!-- 允许作为系统后台持续运行 -->
        <permission name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    </privapp-permissions>
</permissions>
3. 将模块打包进编译文件 (device.mk)
makefile 复制代码
PRODUCT_PACKAGES += \
    RemoteControl \
    privapp-permissions-remote.xml

三、 为什么这是最优、最快、最稳定的解决方案?

机制维度 传统普通方案(广播拉起) 本方案(persistent="true" + Direct Boot)
拉起时机 开机流程走完 →\rightarrow → 发送 BOOT_COMPLETED →\rightarrow → 接收广播 →\rightarrow → 启动进程 →\rightarrow → 启动 Activity(总耗时:开机后 5~10 秒 SystemServer 启动直接 fork 进程 →\rightarrow Application.onCreate() 秒级直启 Activity(总耗时:开机动画结束前已在后台就绪,0 秒直达
保活能力 易被系统 LMK 机制杀掉,需写 JobScheduler/双进程守护等 Hack 手段 系统内核级保活(OOM_ADJ = -800) ,系统永远不杀;若发生 Native Crash,AMS 自动重新拉起
退出行为 容易陷入防退出和正常退出的逻辑死结 用户点 Home 键可正常退出到原生桌面;退到后台后进程依然常驻、视频通信/机器狗指令长连接不中断
代码量 需写 Receiver、Watchdog、各种保活 Service 零多余代码 ,仅依赖 Android 原生架构提供的 persistent 机制

核心结论

android:persistent="true" + 在 Application.onCreate() 中拉起 UI,是 Android 系统级定制设备(如车载中控、大疆遥控器、特种工业屏)最标准、性能最高、代码量最少的黄金实践。

问题:如果原生 Launcher 和 定制App 在开机时几乎同时被拉起

  • 如果定制App先调用 startActivity,随后系统 ATMS 又拉起了 Launcher3,那么 Launcher3 可能会把具微遥控的主界面覆盖在下方(退回后台)
  • 用户开机后看到的依然是原生桌面,需要用户自己去点图标,破坏了"开机 100% 自动置顶前台"的体验。

解决:Framework 层定制代码

修改 ActivityTaskManagerService.java(或 RootWindowContainer.java)。为了避免开机瞬间系统 I/O 拥堵,加入安全的排队调度:

  • 源码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
scala 复制代码
package com.android.server.wm;

import android.content.ComponentName;
import android.content.Intent;
import android.os.UserHandle;
import android.util.Slog;

public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
    private static final String TAG_KIOSK = "ATMS_KioskBoot";
    private static final String TARGET_PKG = "com.remote.control";
    private static final String TARGET_CLS = "com.remote.control.ui.MainActivity";

    /**
     * 在桌面启动完成后,由系统直接派发特权 APP 置顶
     */
    void startHomeOnAllDisplays(String reason) {
        super.startHomeOnAllDisplays(reason);

        // 利用 Handler 异步排队,确保底层的 Home Task 完成了初始事务
        mH.post(() -> {
            try {
                Intent intent = new Intent(Intent.ACTION_MAIN);
                intent.setComponent(new ComponentName(TARGET_PKG, TARGET_CLS));
                
                // 关键 Flags:
                // 1. FLAG_ACTIVITY_NEW_TASK: 系统 Context 启动外部 App 必须携带
                // 2. FLAG_ACTIVITY_RESET_TASK_IF_NEEDED: 保持/重置任务栈
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK 
                              | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);

                // 针对 Android 16 多用户安全机制,明确指定启动到当前前台用户 (UserHandle.USER_CURRENT)
                mContext.startActivityAsUser(intent, UserHandle.CURRENT);
                Slog.i(TAG_KIOSK, "Successfully launched " + TARGET_PKG + " on top of Launcher.");
            } catch (Exception e) {
                Slog.e(TAG_KIOSK, "Failed to auto-start " + TARGET_PKG, e);
            }
        });
    }
}
  • 时序与稳定性

    • 开机后,原生 Launcher 在最底层待命。
    • 定制APP自动在最前台呈现(耗时随开机动画结束直接秒出)。
    • 用户按 Home 键或 Back 键逐级返回,可以正常退出并看到原生桌面
    • 随时点击桌面图标可再次进入定制APP。
相关推荐
恋猫de小郭2 小时前
Flutter iOS 的深度优化 PR,搞笑的是贡献者被 Gemini 评审折磨
android·前端·flutter
撩得Android一次心动2 小时前
Kotlin 语言【知识点整理2】
android·开发语言·kotlin
福大大架构师每日一题2 小时前
webrtc-rs/webrtc v0.20.3更新:Android 网络切换后 ICE Restart 卡死约 10 秒的问题终于解决
android·网络·webrtc
mmsx2 小时前
基于 Android 的本地商城订单 App:SQLite + 多角色架构的技术实践(有源码和文档)
android·架构·sqlite
又见情义13 小时前
RK3568 Android 13 板载驱动适配-USB
android·arm开发·驱动开发
delta_hell13 小时前
【阅读源码-Android】动画之Choreographer-2
android·源码·choreographer
mmsx14 小时前
基于 Android 的博物馆文化展示与预约 App —— 设计与实现(有源码和文档)
android·app·源码·文档
一笑的小酒馆15 小时前
AndroidAGP9.0升级之路
android
阮胜昌15 小时前
MySQL 中的动态数据脱敏:无需更改应用程序即可保护敏感数据
android·mysql·adb