小白也能懂的Android 8.0开机启动脚本保姆级教程
你是不是也遇到过这样的问题:想让自己的程序在Android设备一开机就自动运行,比如自动开启某个服务、设置系统属性、或者执行一些初始化操作?但一看到“init.rc”“SELinux”“te文件”这些词就头大?别担心,这篇教程就是为你准备的——不讲晦涩理论,不堆专业术语,只说你能听懂的话,带你从零开始,亲手完成一个真正能跑起来的开机启动脚本。
本文基于真实工程实践整理,所有步骤均在Android 8.0(Oreo)系统上实测通过,适配主流MTK平台,也适用于高通、展锐等常见方案。哪怕你没编译过Android源码、没碰过SELinux,只要会用adb、能看懂shell命令,就能跟着一步步做完。我们不追求一步到位的“完美方案”,而是先让你的脚本稳稳跑起来,再逐步优化。
1. 先搞清楚:开机启动到底要动哪几块地方?
很多新手卡住,不是因为技术难,而是不知道整个流程分几步、每步干什么。其实Android 8.0的开机脚本启动,就四个核心环节,像搭积木一样,缺一不可:
- 写一个能干活的shell脚本:这是你的“任务清单”,告诉系统开机后具体做什么
- 告诉系统“这个脚本是可信的”:也就是SELinux权限配置,Android 8.0默认强制启用,跳不过
- 告诉系统“开机时记得叫它”:通过init.rc或其子文件注册为一个service
- 确保它真能被系统找到并执行:路径、权限、解释器都得对,一个都不能错
这四步环环相扣。很多人试了十次都不成功,往往是因为只改了其中一两处,比如写了脚本却忘了加SELinux规则,或者加了规则但init.rc里路径写错了。所以咱们不着急写代码,先理清逻辑。
2. 第一步:写一个最简单的开机脚本(5分钟搞定)
别一上来就想做复杂功能。我们先写一个“打个招呼”的脚本,目标只有一个:开机后设置一个系统属性,然后我们用adb命令立刻验证它是否生效。这是最轻量、最可控的测试方式。
2.1 创建脚本文件
新建一个纯文本文件,命名为init.test.sh(注意名字必须以init.开头,这是Android init机制的约定),内容如下:
#!/system/bin/sh # 这是Android专用的shell解释器路径,千万别写成 /bin/sh 或 /usr/bin/sh # 如果你不确定,可以先用 adb shell ls /system/bin/sh 确认一下 # 设置一个测试属性,值为 "booted" setprop test.boot_status "booted" # 可选:写一行日志,方便后续调试(需要logcat权限) # log -t INIT_TEST "Script executed successfully"关键提醒:
- 第一行
#!/system/bin/sh必须严格写对。Android和Linux的shell路径不同,写错直接静默失败; - 不要用Windows编辑器保存(避免换行符问题),推荐用VS Code、Sublime Text或Linux/macOS自带编辑器;
- 文件编码必须是UTF-8无BOM。
2.2 手动测试:确认脚本能独立运行
这一步极其重要!很多问题其实出在脚本本身。先别急着放进系统,我们把它临时推到手机上手动执行一次:
# 将脚本推送到手机的/system/bin目录(需要root权限) adb root adb remount adb push init.test.sh /system/bin/ # 赋予可执行权限 adb shell chmod 755 /system/bin/init.test.sh # 手动运行它 adb shell /system/bin/init.test.sh # 检查属性是否设置成功 adb shell getprop test.boot_status如果最后输出booted,恭喜!你的脚本语法正确、路径可用、解释器匹配。如果报错,比如not found或permission denied,请回头检查上面的提醒点。
3. 第二步:让系统“信任”这个脚本(SELinux配置)
Android 8.0起,SELinux处于enforcing模式,任何进程访问资源(包括执行脚本)都必须有明确授权。你的脚本再完美,没有SELinux许可,init进程根本不会让它启动。
别被“te文件”“file_contexts”吓到,它们本质就是两张“通行证”:
- te文件:定义“谁(test_service)能对什么(test_service_exec)做什么(execute)”;
- file_contexts:定义“哪个文件(/system/bin/init.test.sh)属于哪个类型(test_service_exec)”。
我们按顺序来,全部用最简配置。
3.1 创建SELinux类型定义文件(test_service.te)
新建一个文件test_service.te,内容如下:
# 定义一个新的域(domain),叫 test_service type test_service, domain; # 定义一个新的文件类型,叫 test_service_exec type test_service_exec, exec_type, file_type; # 告诉init:test_service 是一个守护进程域 init_daemon_domain(test_service); # 允许 test_service 域执行 test_service_exec 类型的文件 allow test_service test_service_exec:file { execute read open getattr };说明:
- 这比参考博文里的内容更清晰、更安全。我们没用
permissive(宽容模式),而是精准放行execute权限; init_daemon_domain是关键宏,它自动赋予test_service域访问init所需的基础权限(如读取property_service);- 这个文件通常放在
device/your_company/sepolicy/vendor/non_plat/目录下(不同厂商路径略有差异,MTK多在basic/non_plat)。
3.2 关联脚本文件与SELinux类型(file_contexts)
在device/your_company/sepolicy/vendor/non_plat/file_contexts文件末尾,添加一行:
/system/bin/init\.test\.sh u:object_r:test_service_exec:s0注意:
- 路径中的点
.需要转义为\.,这是正则表达式语法,表示匹配字面量的点; - 这行必须独占一行,前后不能有空格;
- 即使你临时关闭了SELinux(
setenforce 0),这行也必须加,否则init无法识别该文件类型。
4. 第三步:把脚本“注册”进开机流程(修改init.rc)
Android 8.0采用模块化init设计,不再建议直接修改顶层init.rc。芯片厂商通常提供专门的扩展入口,比如init.mtk.rc、init.qcom.rc或init.vendor.rc。我们要把服务加到对应的位置。
4.1 找到正确的init.rc扩展文件
进入你的Android源码目录,执行:
find device/ -name "init.*.rc" | grep -E "(mtk|qcom|vendor)"假设你用的是MTK平台,大概率会找到device/mediatek/sepolicy/basic/init.mtk.rc或类似路径。打开它,在合适位置(比如末尾)添加:
# 定义一个名为 test_service 的服务 service test_service /system/bin/init.test.sh class main user root group root oneshot seclabel u:object_r:test_service_exec:s0字段解释:
service test_service ...:服务名,任意取,但需唯一;/system/bin/init.test.sh:脚本绝对路径,必须和你push的路径一致;class main:表示它属于main类,会在init启动main类服务时被拉起;user/group root:以root身份运行,确保有足够权限;oneshot:执行完即退出,适合一次性初始化任务(如设属性);seclabel:关联前面定义的SELinux类型,必须完全一致。
4.2 验证init.rc语法(可选但强烈推荐)
Android提供工具检查rc文件语法:
# 在源码根目录执行 m -j32 checkinit # 或单独检查 ./system/core/init/checkinit init.mtk.rc如果报错,根据提示修正路径或格式即可。
5. 第四步:编译、烧录与最终验证
前三步都是“纸上谈兵”,现在到了见证奇迹的时刻。
5.1 编译并打包system镜像
# 清理旧产物(可选) m clean # 编译sepolicy和init.rc相关模块 m sepolicy_policy.conf init.mtk.rc # 编译整个system.img(或使用增量编译) m systemimage编译完成后,生成的out/target/product/xxx/system.img就是你要烧录的镜像。
5.2 烧录并重启
用fastboot将新system.img烧录到设备:
fastboot flash system out/target/product/xxx/system.img fastboot reboot等待设备完全启动(约1-2分钟)。
5.3 终极验证:开机后立刻检查
设备启动完毕后,立即执行:
adb shell getprop test.boot_status如果返回booted,说明你的开机脚本已100%成功运行!
你还可以用以下命令查看服务是否被init拉起过:
adb shell dmesg | grep -i "test_service" # 或查看init日志 adb shell logcat -b events | grep -i "test_service"6. 常见问题与避坑指南(血泪总结)
实际操作中,90%的问题都集中在以下几个点。如果你失败了,请优先对照这里排查:
6.1 脚本根本没执行?先看这三点
- 路径写错:init.rc里写的路径,必须和你实际存放脚本的路径完全一致(包括大小写、.sh后缀);
- 权限不足:
/system/bin/init.test.sh必须是755权限,且属主为root:root; - 解释器失效:确认
/system/bin/sh存在且可执行(adb shell ls -l /system/bin/sh)。
6.2 SELinux报错?看log最准
如果开机后getprop查不到值,立即执行:
adb shell dmesg | grep avc你会看到类似这样的拒绝日志:
avc: denied { execute } for path="/system/bin/init.test.sh" dev="sda31" ino=123456 scontext=u:r:init:s0 tcontext=u:object_r:default_file:s0 tclass=file permissive=0这说明:
tcontext(目标上下文)是default_file,但我们需要的是test_service_exec;- 解决方案:回去检查
file_contexts文件,确认路径正则是否匹配、是否漏掉转义、是否多空格。
6.3 修改后不生效?记住两个“必须”
- 必须重新编译system.img:只改te或rc文件,不重新编译,改动不会进镜像;
- 必须完整烧录system分区:用
fastboot flash system,不要用adb remount && adb push替代,后者无法更新SELinux策略。
6.4 进阶建议:让脚本更健壮
当你跑通第一个脚本后,可以逐步升级:
- 加日志:在脚本里用
log -t YOUR_TAG "message"记录关键步骤; - 加判断:用
if [ -f "/data/local/tmp/done" ]; then exit 0; fi避免重复执行; - 加延时:某些服务依赖网络或GPU,可在脚本开头加
usleep 5000000(5秒); - 用vendor分区:把脚本放到
/vendor/bin/,对应修改file_contexts为/vendor/bin/init\.test\.sh,更符合Android Treble规范。
7. 总结:你已经掌握了Android开机启动的核心能力
回顾一下,我们完成了什么:
- 写了一个功能明确、可独立验证的shell脚本;
- 配置了最小集的SELinux策略,理解了te和file_contexts的作用;
- 将服务注册进init流程,知道如何选择正确的rc文件;
- 经历了从编译、烧录到验证的完整闭环;
- 掌握了dmesg和getprop这两个最实用的调试武器。
这不再是“照着抄就能跑”的教程,而是你亲手搭建的一条可靠路径。以后无论是启动一个守护进程、预置配置文件,还是集成第三方SDK的初始化逻辑,你都有了可复用的方法论。
下一步,你可以尝试把一个真实的业务脚本(比如自动挂载U盘、初始化传感器校准参数)套用这个流程。记住:先让最简版本跑通,再叠加功能;先验证单点,再串联流程。这才是工程落地的正确姿势。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。