news 2026/4/15 13:26:31

Android系统开机自动运行脚本,新手入门必看

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android系统开机自动运行脚本,新手入门必看

Android系统开机自动运行脚本,新手入门必看

在Android设备开发和定制过程中,经常需要让某些服务或脚本在系统启动完成时自动运行——比如初始化硬件参数、配置网络环境、启动后台守护进程,或者执行一些诊断检测任务。但很多刚接触Android底层开发的朋友会发现:明明写好了shell脚本,也push进系统了,却始终无法在开机时自动执行。问题往往不出在脚本本身,而在于Android特有的启动机制、权限模型和安全策略。

本文不是讲Linux通用的rc.local方案(那在Android上根本不可用),也不是堆砌晦涩的SELinux术语,而是以一个真实可运行的“测试开机启动脚本”镜像为蓝本,手把手带你走通从写脚本、加权限、改配置到验证成功的完整链路。所有步骤均基于Android 8.0+主流版本实测通过,适配MTK等常见平台,不依赖ADB调试器,也不要求串口支持——你只需要一台已root或可刷机的设备,就能跟着一步步操作成功。

全文没有抽象理论,只有具体命令、明确路径、可复制的代码片段,以及每个环节背后“为什么必须这么做”的直白解释。哪怕你之前没碰过init.rc、没听过te文件、甚至分不清/system/bin/sh和/bin/sh的区别,也能照着做完。


1. 先搞清楚:Android开机启动和Linux有什么不一样

很多人以为Android就是个Linux发行版,所以想当然地把Linux那一套搬过来——比如修改/etc/rc.local、写systemd service、或者直接往init.d里丢脚本。结果发现全都不生效。原因很简单:Android不用sysvinit,也不用systemd,它用的是自己定制的init进程,整套启动逻辑完全不同。

Android的init进程在内核启动后立即运行,它读取的是/system/etc/init/下的.rc文件(如init.rc)或/system/etc/init/目录中按规则命名的配置文件。这些文件定义了哪些服务要启动、以什么用户身份运行、依赖关系如何、是否只运行一次等。

更重要的是,从Android 5.0开始,SELinux默认处于enforcing模式。这意味着:

  • 即使你的脚本语法完全正确,
  • 即使路径写得毫无错误,
  • 即使init.rc里已经声明了service,
    只要SELinux策略没放行,脚本就会被静默拦截,连日志都看不到。

所以,真正的开机自启流程只有四步,缺一不可:

  1. 写一个能在Android上跑起来的shell脚本;
  2. 告诉SELinux:“这个脚本是可信的,允许它被init调用”;
  3. 在init配置中注册这个服务;
  4. 验证它是否真的在开机时执行了。

下面我们就按这个顺序,一项一项来。


2. 第一步:写出真正能跑的Android shell脚本

2.1 脚本位置与解释器选择

Android的shell环境和标准Linux不同。它的/bin/sh通常不存在,系统级脚本必须使用/system/bin/sh(或少数平台用/system/xbin/sh)。如果你写成#!/bin/sh,脚本将直接失败,且不会报错——init只会默默跳过它。

正确写法:

#!/system/bin/sh

❌ 错误写法(常见坑):

#!/bin/sh #!/usr/bin/env sh #!/system/xbin/bash # Android默认不带bash

小贴士:怎么确认你设备用的是哪个解释器?用adb shell连上去后执行which shls -l /system/bin/sh就能看到真实路径。

2.2 一个最小可用的测试脚本

我们新建一个名为init.test.sh的脚本,内容极简,只为验证“是否真能执行”:

#!/system/bin/sh # 设置一个系统属性,这是最轻量、最安全的验证方式 # 不涉及文件读写、不触发权限检查、不依赖外部命令 setprop test.boot.started 1 # 可选:再写一条便于调试的日志(需确保logd已就绪) # log -t "INIT_TEST" "Script executed at $(date)"

注意事项:

  • 不要尝试touch /data/test.txtecho "ok" > /sdcard/log.txt——早期init阶段/data和/sdcard可能还未挂载,会失败;
  • 不要用sleep 5等待其他服务——init有严格的超时机制,卡住会导致服务被kill;
  • setprop是init自带能力,无需额外权限,且属性值可在任意时刻用getprop test.boot.started查看,是最可靠的验证手段。

2.3 手动测试脚本是否可用

别急着改系统配置,先确保脚本本身没问题:

# 将脚本推送到设备(假设已root) adb push init.test.sh /system/bin/ # 赋予可执行权限(Android对权限更严格) adb shell chmod 755 /system/bin/init.test.sh # 手动执行一次,看是否成功 adb shell /system/bin/init.test.sh adb shell getprop test.boot.started # 应输出 1

如果这一步失败,请回头检查:

  • 脚本首行是否为#!/system/bin/sh
  • 是否用了Windows换行符(CRLF)?请用Unix换行(LF)保存;
  • /system分区是否已remount为可写?adb shell mount -o rw,remount /system

只有手动执行成功了,才能进入下一步。


3. 第二步:为脚本添加SELinux策略(绕不开的关卡)

Android 8.0之后,SELinux是硬性门槛。即使你临时关闭SELinux(setenforce 0),脚本仍可能因缺少file_context而无法加载——因为init在解析.rc文件时,会先查文件上下文(file context),找不到就拒绝启动服务。

整个SELinux适配分三部分:定义类型、声明上下文、赋予执行权限。

3.1 定义服务类型(test_service.te)

在厂商SELinux策略目录下(如device/mediatek/sepolicy/basic/non_plat/),新建文件test_service.te

# 声明一个新的域类型,代表这个服务的运行环境 type test_service, coredomain; # 声明脚本文件的类型,用于匹配file_context type test_service_exec, exec_type, vendor_file_type, file_type; # 让test_service域可以被init_daemon_domain管理(即允许init启动它) init_daemon_domain(test_service); # 允许test_service域执行test_service_exec类型的文件 allow test_service test_service_exec:file { read open getattr execute };

关键说明:

  • coredomain表示这是一个核心服务域,具备基础系统访问能力;
  • exec_type是Android SELinux中对可执行文件的标准分类;
  • init_daemon_domain()是必须调用的宏,它自动赋予test_service域访问init所需资源的权限(如socket、property_service等);
  • 最后一行allow ... execute才是让脚本真正跑起来的关键授权。

如果你不确定该加哪条allow规则,可以先注释掉最后一行,开机后用adb shell dmesg | grep avc查看被拒绝的操作,再针对性添加。

3.2 声明文件上下文(file_contexts)

在同一目录下的file_contexts文件末尾,添加一行:

/system/bin/init\.test\.sh u:object_r:test_service_exec:s0

注意:

  • 路径必须用正则转义.(即\.),否则匹配失败;
  • 类型名必须和.te文件中定义的test_service_exec完全一致;
  • s0是MLS级别,在大多数设备上固定为s0,无需改动。

这条语句的意思是:“所有路径匹配/system/bin/init.test.sh的文件,其SELinux上下文设为u:object_r:test_service_exec:s0”。

3.3 编译并刷入新策略

修改完SELinux策略后,必须重新编译bootimage或systemimage,并刷入设备。具体命令取决于你的编译环境,典型流程如下:

# 清理旧策略缓存 m clean # 重新编译sepolicy(会生成policy.db) m sepolicy # 编译system.img(包含更新后的file_contexts和policy.db) m systemimage # 刷入system分区(需fastboot模式) fastboot flash system system.img

如果你使用的是预编译镜像(如本文提到的“测试开机启动脚本”镜像),这一步已由镜像制作者完成,你只需确认镜像中已包含上述策略即可。


4. 第三步:在init配置中注册服务

Android不推荐直接修改/system/etc/init/init.rc,因为它是AOSP标准文件,容易被OTA升级覆盖。更稳妥的做法是:在厂商专用的init配置中添加,例如:

  • MTK平台:/system/etc/init/init.mtk.rcdevice/mediatek/common/rootdir/etc/init.mtk.rc
  • 高通平台:/system/etc/init/init.qcom.rc
  • 或统一放在/system/etc/init/目录下,以.rc结尾的独立文件(如test_boot.rc

我们新建一个/system/etc/init/test_boot.rc,内容如下:

# 定义一个名为test_boot的服务,执行我们的脚本 service test_boot /system/bin/init.test.sh class main user root group root oneshot seclabel u:object_r:test_service_exec:s0 # 指定该服务在main类启动时运行(即系统基本服务就绪后) on property:sys.boot_completed=1 start test_boot

各字段含义:

  • class main:表示该服务属于main启动类,会在init启动main类时准备;
  • user/group root:以root身份运行(必要,普通用户无权设置系统属性);
  • oneshot:执行完即退出,不常驻——适合一次性初始化任务;
  • seclabel:必须与file_contexts中定义的类型一致;
  • on property:sys.boot_completed=1:这是最关键的触发条件!它确保脚本在Android框架层完全启动(即桌面可操作)后再运行,避免因服务未就绪而失败。

验证技巧:你可以把start test_boot改成exec - -- /system/bin/sh -c 'setprop test.boot.delayed 1',然后用getprop test.boot.delayed确认是否触发。


5. 第四步:验证脚本是否真正在开机时运行

完成以上三步后,重启设备,执行以下命令验证:

# 查看服务是否被init识别 adb shell ls -Z /system/bin/init.test.sh # 应显示 u:object_r:test_service_exec:s0 # 查看服务状态(Android 8.0+支持) adb shell dumpsys activity services | grep test_boot # 检查属性是否已设置(最直接证据) adb shell getprop test.boot.started # 应输出 1 # 查看init日志(确认是否启动过) adb shell dmesg | grep -i "test_boot\|init\.test"

如果getprop test.boot.started返回1,恭喜你,脚本已在开机时成功执行!

如果仍是空值,请按顺序排查:

  1. ls -Z是否显示正确的SELinux上下文?→ 检查file_contexts和策略编译;
  2. dumpsys activity services是否列出test_boot?→ 检查.rc文件路径和语法;
  3. dmesg是否有avc denied日志?→ 根据提示补全SELinux allow规则;
  4. getprop sys.boot_completed是否为1?→ 确保触发条件时机正确。

6. 进阶建议:让脚本更健壮、更实用

上面只是一个最小可行示例。在实际项目中,你可能需要:

6.1 支持多平台的路径兼容写法

有些设备/system/bin不可写,或脚本需放在vendor分区。可统一用/vendor/bin/,并在file_contexts中同步修改:

/vendor/bin/init\.test\.sh u:object_r:test_service_exec:s0

同时在.rc中改为:

service test_boot /vendor/bin/init.test.sh

6.2 添加错误捕获与日志记录

虽然init阶段logd可能未就绪,但你可以用logwrapper兜底:

#!/system/bin/sh logwrapper setprop test.boot.started 1

或重定向输出到临时文件(需确保目录存在):

#!/system/bin/sh echo "$(date): boot script started" >> /data/local/tmp/boot.log 2>&1 setprop test.boot.started 1

6.3 实现条件启动(仅特定设备型号运行)

利用系统属性判断机型:

#!/system/bin/sh if getprop ro.product.model | grep -q "MyDevice"; then setprop test.boot.started 1 fi

6.4 启动后唤醒应用或发送广播(需framework层配合)

# 启动Activity(需声明exported) am start -n com.example/.BootReceiver # 发送自定义广播 am broadcast -a com.example.BOOT_COMPLETED

这些功能都建立在基础启动能力之上,只要第一步跑通,后续扩展水到渠成。


7. 总结:开机自启不是玄学,而是可复现的工程流程

回顾整个过程,你会发现所谓“Android开机自启”,其实是一套清晰、严谨、可验证的工程链路:

  • 脚本本身必须适配Android的shell环境,用/system/bin/sh,做最小化验证;
  • SELinux策略不是可选项,而是必答题,它由te文件定义行为、file_contexts绑定路径、allow规则放开权限;
  • init配置要选对位置、写对语法、设对触发时机,oneshot + sys.boot_completed是最稳妥组合;
  • 验证手段要直接有效,getprop比看日志更快,ls -Z比猜路径更准。

你不需要成为SELinux专家,也不必读懂整个init源码。只需要理解每一行配置的作用,知道它为什么在这里、不加会怎样、加错会怎样——这就足够在真实项目中稳定落地。

现在,你已经掌握了Android开机自动运行脚本的核心方法。下一步,就是把它用在你的硬件初始化、AI模型预热、IoT设备自检等真实场景中。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 11:01:14

手把手教你用YOLOv10镜像做图像预测,小白也能行

手把手教你用YOLOv10镜像做图像预测,小白也能行 你是不是也遇到过这样的情况:看到别人用YOLO模型检测出图中所有物体,心里直痒痒,可一打开GitHub就懵了——环境怎么配?权重怎么下?命令怎么敲?报…

作者头像 李华
网站建设 2026/4/15 4:36:50

教育场景实战:用Unsloth训练学科答疑AI

教育场景实战:用Unsloth训练学科答疑AI 1. 为什么教育行业需要专属的学科答疑AI? 你有没有遇到过这样的情况:学生在课后反复问同一个物理概念,老师已经讲了三遍,但仍有学生抓不住关键;或者一个化学方程式…

作者头像 李华
网站建设 2026/4/11 14:49:54

支持剪贴板粘贴!这抠图工具细节做得太贴心了

支持剪贴板粘贴!这抠图工具细节做得太贴心了 1. 一个被忽略却极重要的交互细节:CtrlV 直接粘贴图片 你有没有过这样的经历—— 刚截了一张图,想立刻抠掉背景,却得先保存到桌面、再打开软件、再点上传、再选文件……三步操作&…

作者头像 李华
网站建设 2026/4/13 21:42:14

用Qwen-Image-Layered做了个图像分层项目,全过程分享

用Qwen-Image-Layered做了个图像分层项目,全过程分享 你有没有试过这样改图:想把一张风景照里的天空换成晚霞,结果一调色,山体也跟着发红;想给产品图换背景,抠图边缘毛刺明显,反复擦除反而伤了…

作者头像 李华
网站建设 2026/4/8 11:38:29

AMD Nitro-E:304M轻量AI绘图,4步生成速度新标杆

AMD Nitro-E:304M轻量AI绘图,4步生成速度新标杆 【免费下载链接】Nitro-E 项目地址: https://ai.gitcode.com/hf_mirrors/amd/Nitro-E 导语:AMD推出轻量级文本到图像扩散模型Nitro-E,以304M参数实现4步快速图像生成&#…

作者头像 李华