玩CoreELEC的盒子,多半都折腾过双系统:eMMC里留一个安卓负责日常点播,U盘或SD卡里装CoreELEC当纯粹的Kodi播放系统。这套搭配确实舒服,但有个非常烦人的细节——很多盒子只要检测到外部存储里有可启动系统,开机就会优先从U盘引导,直接进CoreELEC。想回安卓?要么拔U盘重启,要么带着遥控器卡准时机进Bootloader菜单,时间长了真的会暴躁。
后来我写了一个开机倒计时插件,思路很简单:CoreELEC启动早期弹一个倒计时窗口,比如默认5秒,期间不做任何操作就自动重启进入安卓系统;如果按一下遥控器上的确认键,就取消重启,留在CoreELEC里继续用。这样U盘常年插着也不影响正常开机,想看片的时候按个键的事。这篇文章就把这套插件的完整实现过程写出来,包括systemd服务怎么写、遥控器按键怎么监听、重启后怎么保证进安卓而不是又被拉回CoreELEC,以及我在不同盒子上踩过的坑。
1. 方案整体设计与思路拆解
1.1 先搞清楚双系统的启动链路
绝大多数Amlogic盒子(S905、S912、S922X这些)的启动过程都遵循类似路径:上电后引导ROM加载U-Boot,U-Boot再根据环境变量、设备树、按键状态等决定从哪里加载内核。当U盘或SD卡里存在CoreELEC时,U-Boot通常会优先把它当作启动盘。也就是说,只要外部存储插着,开机默认就是CoreELEC,安卓系统反而成了“备选”。
CoreELEC起来之后,系统其实运行在一个相对完整的Linux用户空间里,systemd作为1号进程在管所有服务。这意味着我们完全可以在它的启动链路上插入自己的逻辑——这就是“插件”能实现的基础。很多人在这一步会想着去改U-Boot的环境变量,把默认启动介质改成eMMC,但问题是每个盒子的U-Boot配置都不一样,改错了直接变砖,太冒险。
我的方案是在CoreELEC这一侧做判断和倒计时:既然系统已经被引导起来了,那就利用这个“已经运行”的状态做一次短时间等待,让用户决定去留。5秒内没反应,说明用户大概率只是想用安卓,就主动重启;有按键输入,说明是冲着CoreELEC来的,就放行继续启动Kodi。
1.2 为什么选择“倒计时 + 重启”而不是其他方式
第一个选择是把倒计时逻辑做成Kodi插件,也就是Kodi起来之后在界面上弹一个通知。这个方案体验很好,但有一个致命问题:CoreELEC从内核加载到Kodi完全显示出来,快的要15秒,慢的能到30秒。这么长的启动时间用户根本等不住,所以必须在Kodi之前、系统早期就完成判断。
第二个选择是在安卓侧改启动脚本,让安卓开机后自动检测U盘并决定是否跳转。这个方案对普通盒子玩家来说门槛太高,而且不同安卓版本的init脚本差异很大,刷一次机就被覆盖,维护成本不划算。
最终我选了systemd服务的方式:把倒计时脚本做成一个开机自启服务,放在Kodi服务启动之前,脚本完成后要么直接reboot,要么通过exit 0放行让系统继续启动。好处是逻辑独立、不侵入系统镜像、更新也方便。整个过程可以这样描述:
- CoreELEC内核启动,systemd接管;
- boot-select.service运行倒计时脚本;
- 脚本检测到本次是从U盘/SD启动;
- 进入倒计时循环,同时监听遥控器输入;
- 倒计时结束且无按键 → 执行“重启到安卓”命令;
- 用户在倒计时内按键 → 脚本退出,系统正常启动Kodi。
1.3 需要准备的环境与知识
动手之前先确认几件事:
- 一台刷好CoreELEC的盒子,系统版本建议9.2以上,因为老版本systemd的行为和新版本有一些差异;
- 能从SSH登录CoreELEC系统,默认用户root,密码你刷机时设置的;
- U盘或SD卡是CoreELEC的启动介质,eMMC里是安卓;
- 对systemd的基本概念有了解,知道service文件放在哪、daemon-reload怎么用;
- 具备最基础的Shell脚本阅读能力,否则后面改参数会吃力。
如果你完全没接触过SSH,建议先把CoreELEC的SSH服务打开,用PuTTY或终端连上去,把基础命令练熟。这套代码本身不复杂,但调试过程需要反复改文件、看日志、重启,SSH是唯一能实时观察到内部状态的手段。
2. 开机倒计时服务核心实现
2.1 创建systemd服务文件
CoreELEC的持久化存储挂载在/storage目录,重启不会丢文件。自定义systemd服务一般放在/storage/.config/system.d/下面。我先创建这个目录,然后写一个boot-select.service文件:
mkdir -p /storage/.config/system.d mkdir -p /storage/.config/boot-select touch /storage/.config/boot-select/boot-select.sh touch /storage/.config/boot-select/settings.conf chmod +x /storage/.config/boot-select/boot-select.shboot-select.service的内容如下:
[Unit] Description=Boot countdown selector After=local-fs.target Before=kodi.service [Service] Type=oneshot ExecStart=/storage/.config/boot-select/boot-select.sh TimeoutStartSec=30 RemainAfterExit=no [Install] WantedBy=multi-user.target这里有几个关键点要解释一下:
After=local-fs.target:确保所有本地文件系统挂载完成后再运行脚本,因为脚本要读取/storage下的配置;Before=kodi.service:让倒计时逻辑优先于Kodi执行,避免Kodi已经启动、界面弹到一半又突然重启的尴尬局面;Type=oneshot:这个服务只执行一次脚本,脚本退出服务就算结束;TimeoutStartSec=30:给脚本一个最大执行时限,防止脚本卡死导致整个系统卡在启动阶段。
有朋友问为什么要Before=kodi.service而不是放在multi-user.target之后。实测下来,如果Kodi先起来,它会抢占遥控器输入设备的读取,我们的脚本在后面就很难捕获按键事件。把倒计时放在Kodi之前,input设备还没有被Kodi独占,脚本能比较干净地读到原始输入事件。
2.2 倒计时主脚本逻辑拆解
boot-select.sh是整个插件的核心,代码不复杂,但每一段都有讲究:
#!/bin/sh # 加载用户配置 . /storage/.config/boot-select/settings.conf # 第一步:只有从外部介质启动才运行 root_dev=$(findmnt -n -o SOURCE /) case "$root_dev" in /dev/sd*|/dev/mmcblk1*) echo "[boot-select] External boot detected: $root_dev" ;; *) echo "[boot-select] Internal boot, skip." exit 0 ;; esac # 第二步:倒计时 + 按键检测 countdown=$COUNTDOWN_SECONDS echo "[boot-select] Press OK to enter CoreELEC in ${countdown}s..." > /dev/console while [ $countdown -gt 0 ]; do echo "[boot-select] ${countdown} seconds left..." if check_key_pressed; then echo "[boot-select] Key pressed, stay in CoreELEC." exit 0 fi sleep 1 countdown=$((countdown - 1)) done # 第三步:超时,进安卓 echo "[boot-select] Timeout, reboot to Android." boot_to_android注意/dev/console重定向,这是为了让倒计时文字直接显示在电视屏幕上。如果你的盒子HDMI输出的是Kodi的图形界面,console文字不一定能显示,但写进去没有副作用,不影响调试日志。实际能不能看到提示文字,取决于设备的虚拟终端是否绑定到了HDMI显示上,这个后面会专门说。
2.3 配置文件与参数调整
settings.conf里放了所有可以调的参数:
# 倒计时秒数 COUNTDOWN_SECONDS=5 # 是否只在外部介质启动时启用 ONLY_EXTERNAL_BOOT=1 # 监听输入事件时,是否输出详细日志 DEBUG=0把参数从主脚本里抽出来的好处是,改时间、调行为都不需要动脚本逻辑,直接改配置然后重启服务就行。我在第一版的时候把这些值全部写死在脚本里,后来发现不同盒子的安卓启动速度不一样,5秒对某些盒子太紧张,10秒对另一些又太啰嗦,频繁改文件很麻烦,所以拆成了独立配置。
2.4 判断启动介质的几种方法
上面脚本里用了findmnt -n -o SOURCE /,拿根文件系统挂在哪个设备上,然后通过设备名判断。CoreELEC跑在外置U盘时,根设备通常是/dev/sda1或/dev/sdb1;跑在SD卡时一般是/dev/mmcblk1p1;跑在eMMC内置存储时则是/dev/mmcblk0p1。所以脚本里用/dev/sd*和/dev/mmcblk1*作为外部介质的特征。
但有个别奇葩设备不按套路出牌,U盘会被识别成/dev/mmcblk0(某些内置读卡器)。保险起见,可以加一个辅助判断,读取/proc/cmdline中的root参数:
cmdline=$(cat /proc/cmdline) echo "$cmdline" | grep -qE 'root=/dev/(sd[a-z]|mmcblk1)' && external=1两个条件只要命中了其中一个,就可以认为当前是从外部介质启动的。实测中,大多数盒子用第一种方法就能准确判断,第二种方法主要是给自己留个后路。
3. 遥控器按键监听与用户交互
3.1 Linux输入子系统下的按键读取
Linux内核把所有输入设备统一抽象成/dev/input/eventX节点,无论红外遥控器、蓝牙遥控器还是USB键盘,最终都会变成一个个input_event结构事件。我们要做的就是在倒计时循环里循环读取这些事件节点,一旦发现数据就认为用户按了键。
但这个方案有一个麻烦:不知道遥控器对应哪个event节点。最简单粗暴的办法是遍历所有/dev/input/event*文件,只要在1秒窗口内任何节点收到了数据,就判定为有按键。这个做法在纯电视盒子上很实用,因为除了遥控器,基本不会有其他输入设备。
下面是check_key_pressed函数的实现:
check_key_pressed() { for ev in /dev/input/event*; do [ -e "$ev" ] || continue if timeout 1 dd if="$ev" bs=24 count=1 2>/dev/null | grep -qE "."; then return 0 fi done return 1 }timeout 1 dd的意思是在1秒内等待设备返回24字节数据,这24字节正好是一个input_event结构体的标准大小(在64位系统上是24字节,32位系统是16字节,但bs取24一般也能覆盖)。有数据返回说明用户产生了输入事件,grep -qE "."能检测到哪怕一个字节。
这个实现虽然简单粗暴,实测下来非常有效。唯一要小心的是,如果盒子上插了鼠标,鼠标随便动一下也会被判定为“按键”,从而取消倒计时。如果遇到这种情况,就需要精确定位遥控器对应的event节点,后面会讲。
3.2 精确匹配遥控器输入设备
如果系统里存在多个输入设备,或者你不想让鼠标误触,可以通过/proc/bus/input/devices来查找设备名称。遥控器设备的名称通常包含“remote”、“ir”、“keypad”之类的关键词:
find_remote_event() { for dev in /sys/class/input/input*/; do name=$(cat "$dev/name" 2>/dev/null) case "$name" in *remote*|*Remote*|*ir*|*IR*|*keypad*|*gpio*) event_node="${dev}event*" echo "$dev" | sed 's/input[0-9]*/event&/' return 0 ;; esac done }这段脚本写得比较糙,实际使用时直接测试每个event节点,通过evtest工具查看事件输出,找到对应的那个节点后写死在配置文件里更稳定。比如settings.conf里加一项:
INPUT_DEVICE=/dev/input/event2然后check_key_pressed只监听这一个节点。哪个节点对应遥控器,可以通过SSH执行下面的命令来确认:
od -x /dev/input/event2启动命令后按几下遥控器,如果终端里冒出十六进制数据,说明找对了节点,按Ctrl+C退出。
3.3 倒计时提示的显示技巧
让倒计时文字显示在电视上,是最能提升体验的部分。但也是比较玄学的部分,因为不同盒子的显示链路差异很大。我在S905X3和S922X盒子上测试过几种方法:
- 写
/dev/console:部分核心启动参数配置较全的盒子,内核console直接映射到HDMI输出,能看到文字; - 写
/dev/tty0:如果系统默认启用了VT终端,tty0的内容会显示在屏幕上; - 写
/dev/tty1:类似tty0,但在某些设备上tty1才是真正的前台终端。
一个兼容性较好的写法是三个都写:
/usr/bin/echo "Press OK to CoreELEC, reboot to Android in ${countdown}s" > /dev/console 2>/dev/null /usr/bin/echo "Press OK to CoreELEC, reboot to Android in ${countdown}s" > /dev/tty0 2>/dev/null /usr/bin/echo "Press OK to CoreELEC, reboot to Android in ${countdown}s" > /dev/tty1 2>/dev/null如果你的盒子因为某些原因不显示任何字符,也能接受,毕竟倒计时这种交互本身只持续几秒,用户看到电视黑屏、盒子指示灯在闪,就知道系统在做选择。但如果能在屏幕上打出倒计时数字,整个体验确实会专业很多。
3.4 用LED状态做辅助提示
有的盒子机身带有LED指示灯,可以通过/sys/class/leds/下的节点控制闪烁节奏,做一个视觉反馈。比如倒计时期间让LED以1Hz频率闪烁,检测到按键后常亮,超时重启前快速闪三下。这个属于锦上添花,不同盒子的LED节点名称完全不一样,需要针对自己的设备调试。
我在自己的盒子上用了电源指示灯做反馈,命令大致是这样:
echo timer > /sys/class/leds/red:status/trigger echo 500 > /sys/class/leds/red:status/delay_on echo 500 > /sys/class/leds/red:status/delay_off如果设备路径不存在,这条命令会自动失败,不影响主流程。有兴趣的可以研究,没时间折腾的跳过也行,不影响核心功能。
4. 倒计时结束后自动进入安卓
4.1 重启进安卓的核心难题
很多人觉得倒计时脚本写完了,重启就是一句reboot的事,但实际上这里有一个陷阱:如果直接执行reboot,U-Boot重启后还是会优先检测U盘,结果又进CoreELEC,形成一个死循环。所以“从CoreELEC重启进安卓”的核心不是重启本身,而是如何告诉Bootloader下一次启动时不要走外部介质。
我把这个动作封装成一个函数boot_to_android(),不同硬件平台有不同的实现方式。至少需要满足以下条件的其中之一:
- 修改U-Boot环境变量,把启动介质切回eMMC;
- 写入一个标志文件,让Bootloader或安卓init进程在启动时检测并切换;
- 调用厂商预留的recovery或fastboot模式命令。
4.2 方案A:通过fw_setenv修改U-Boot环境变量
Amlogic平台在Linux用户空间提供了fw_setenv工具,用来读写U-Boot的环境变量区。CoreELEC镜像里有一部分设备自带这个工具,如果没有,可以通过opkg install fwenv安装from CoreELEC的仓库。
以常见的S905X3盒子为例:
fw_setenv bootfromusb 0 fw_setenv bootfromnand 1 reboot不同机型的变量名差异很大。有的用bootsource,有的用boot_device,有的是store_boot。最靠谱的做法是先在CoreELEC里执行fw_printenv,把所有环境变量列出来,然后找和启动介质相关的字段。
fw_printenv | grep -iE 'boot|usb|sd|nand|emmc'只要看到了类似bootfromusb=1这样的变量,思路就清晰了。把它改成0或清空,再重启,系统就会走eMMC里的安卓。
函数示例:
boot_to_android() { fw_setenv bootfromusb 0 2>/dev/null fw_setenv bootfromnand 1 2>/dev/null reboot }这个方案最干净,重启后安卓系统正常加载,不会残留任何额外文件。
4.3 方案B:安卓data分区标志文件
如果fw_setenv不可用,或者U-Boot环境变量区损坏,另一个相对通用的思路是往安卓的/data分区写一个标志文件,然后reboot,安卓系统启动时检测这个文件,如果存在就清理掉并继续正常启动。
具体实现分两步。第一步,CoreELEC这边找到安卓的data分区并挂载:
boot_to_android() { # 找到label为data或包含android_data的分区 data_dev=$(blkid | grep -iE 'LABEL="data"|PARTLABEL="data"' | cut -d: -f1) [ -n "$data_dev" ] || data_dev="/dev/block/mmcblk0p33" mount -o rw "$data_dev" /mnt 2>/dev/null touch /mnt/.force_android umount /mnt reboot }第二步,安卓侧需要一个init脚本在每次开机时检查/data/.force_android是否存在,存在就删除并正常进入系统。但这句话说起来容易,要落地就得修改安卓的boot.img或者/system分区,对普通玩家来说负担很大。所以这个方案我只在有root权限、并且能接受重刷系统的情况下推荐。
4.4 方案C:使用reboot参数
还有一类机器支持直接向reboot命令传参,比如reboot recovery会进入恢复模式,reboot bootloader会进入fastboot。这些都不能直接进安卓,但可以用来做一个间接动作:先进recovery,再从recovery启动安卓。
这个方案需要你在recovery模式下能通过脚本切换启动项,对普通用户来说比前两种更复杂,不展开讲。列在这里只是提醒大家,不要以为reboot除了重启就没有别的花样,有些厂商确实是靠reboot参数配合底层脚本来实现系统切换的。
综合来看,我建议优先研究fw_setenv方案。Amlogic盒子是CoreELEC的主要用户群体,U-Boot环境变量操作在社区里被验证过很多次,成功率最高。
4.5 为不同设备预留自定义命令接口
考虑到硬件差异,boot-select.sh里的boot_to_android函数不需要写死在主脚本里,可以拆到独立文件/storage/.config/boot-select/boot-actions.sh。我的做法是在主脚本末尾source它:
. /storage/.config/boot-select/settings.conf . /storage/.config/boot-select/boot-actions.shboot-actions.sh里可以自由实现自己的切换逻辑,比如有些盒子用dd写特定分区,有些盒子用/sys/class/amlogic/...节点,这些特殊逻辑都不需要动主脚本。这个设计让我在不同盒子上迁移插件时省了很多事。
5. 完整安装部署与实测调试
5.1 完整文件清单与代码汇总
为了方便读者直接照抄,我把完整文件整理出来。先确认目录结构:
/storage/.config/system.d/boot-select.service # systemd服务 /storage/.config/boot-select/settings.conf # 用户配置 /storage/.config/boot-select/boot-select.sh # 主脚本 /storage/.config/boot-select/boot-actions.sh # 重启动作boot-select.service在前面已经写好,settings.conf也写过了。主脚本boot-select.sh的完整版:
#!/bin/sh # 初始化 . /storage/.config/boot-select/settings.conf . /storage/.config/boot-select/boot-actions.sh # 判断是否为外部启动 root_dev=$(findmnt -n -o SOURCE /) case "$root_dev" in /dev/sd*|/dev/mmcblk1*) echo "[boot-select] External boot detected: $root_dev" ;; *) if [ "$ONLY_EXTERNAL_BOOT" = "1" ]; then echo "[boot-select] Internal boot, skip." exit 0 fi ;; esac # 按键检测函数 check_key_pressed() { for ev in /dev/input/event*; do [ -e "$ev" ] || continue if timeout 1 dd if="$ev" bs=24 count=1 2>/dev/null | grep -qE "."; then return 0 fi done return 1 } # 倒计时 countdown=$COUNTDOWN_SECONDS echo "[boot-select] Press OK to enter CoreELEC in ${countdown}s" > /dev/console echo "[boot-select] Press OK to enter CoreELEC in ${countdown}s" > /dev/tty1 2>/dev/null while [ $countdown -gt 0 ]; do echo "[boot-select] ${countdown} seconds left..." if check_key_pressed; then echo "[boot-select] Key pressed, stay in CoreELEC." exit 0 fi sleep 1 countdown=$((countdown - 1)) done echo "[boot-select] Timeout, reboot to Android." boot_to_androidboot-actions.sh先放一个最保守的实现:
#!/bin/sh boot_to_android() { # 方案A:fw_setenv切换启动介质(按自己设备修改) if command -v fw_setenv >/dev/null 2>&1; then fw_setenv bootfromusb 0 2>/dev/null fw_setenv bootfromnand 1 2>/dev/null reboot return fi # 兜底:直接重启(可能还会回到CoreELEC,需要继续调试) echo "[boot-select] No boot_to_android implementation, fallback reboot." reboot }5.2 启用服务并后台验证
文件都放好后,执行下面的命令:
systemctl daemon-reload systemctl enable boot-select.service systemctl start boot-select.service这时候服务已经开始运行,可以查看日志:
journalctl -u boot-select.service -f如果服务正常,日志里会打印External boot detected、倒计时提示等。如果没有任何输出,先检查脚本有没有执行权限、service文件路径是不是正确扫描到了。
我通常会把systemctl start放在最后,因为第一次运行时如果发现日志报错,还要改文件、reload,顺序不对容易让自己乱。
5.3 开机倒计时实测记录
把U盘插好,启动盒子。我在S905X3上的实际表现是:
- 第0秒:盒子通电,HDMI画面先是黑屏,随后出现引导信息;
- 第1-2秒:CoreELEC内核解压,系统初始化;
- 第3秒左右:systemd开始拉起服务,boot-select.service执行;
- 第4-9秒:倒计时运行,屏幕上显示提示文字;
- 第10秒:如果没有按键,执行boot_to_android,盒子重启;
- 再次重启时U-Boot读取修改后的环境变量,跳过U盘,进入eMMC里的安卓系统。
整个过程从上电到安卓启动完成,大约在25秒左右。如果按了遥控器OK键,则是另一个路径:倒计时中断,系统继续初始化,Kodi正常起来,整个过程大约在40秒左右,比直接进CoreELEC多出了一个倒计时的等待时间。
5.4 我的调试习惯
调这类系统级脚本,最怕的就是反复拔电重启。我的做法是先把参数调大,比如把COUNTDOWN_SECONDS改成30,然后SSH连上去手动执行boot-select.sh,观察输出是否符合预期,确认没有问题了再改成正式倒计时秒数、设置开机自启。
手动执行时要注意:脚本末尾如果触发了boot_to_android,会直接重启,所以想测试倒计时逻辑但又不想重启时,可以先注释掉boot_actions.sh里的reboot命令,改成echo "would reboot to android",这样就能安全地跑完整个流程。
6. 常见问题与排查技巧实录
6.1 问题速查表
我把实际使用中遇到的典型问题整理成了表格,方便快速对照排查:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 倒计时脚本没运行 | service文件没放到system.d目录 | 检查路径是否正确,systemctl daemon-reload |
| 脚本运行了但没显示倒计时文字 | 控制台没映射到HDMI | 改用Kodi notification,或忽略文字仅依赖日志 |
| 按遥控器没反应 | 监听event节点不对,或Kodi抢占了输入 | 用od检查遥控器对应event节点,写死INPUT_DEVICE |
| 倒计时结束重启后又回到CoreELEC | reboot前没有切换启动介质 | 检查fw_setenv变量是否生效,确认boot_actions.sh实现 |
| 倒计时期间鼠标动一下就取消了 | 监听了所有input设备 | 精确限制到遥控器event节点 |
| 开机自动进安卓正常,但手动按键进CoreELEC后无法回安卓 | boot_actions.sh里fw_setenv命令把变量改成了固定值 | 每次进入CoreELEC后改环境变量,而不是只改一次 |
| Kodi启动变慢 | 倒计时服务阻塞了Kodi启动流程 | 确认服务在倒计时结束后立即exit,检查TimeoutStartSec |
| systemd启动超时 | 脚本里某个命令卡住 | 给脚本内的wait命令加timeout,检查是否有阻塞读取 |
6.2 按键监听无效的深层排查
如果check_key_pressed始终没反应,大概率是权限或设备节点问题。CoreELEC默认root用户,权限上问题不大,主要怀疑设备节点不对。先用od -x /dev/input/eventX逐个测试,确定遥控器对应的节点后,再写一个单节点测试脚本:
timeout 5 dd if=/dev/input/event2 bs=24 count=1 | od -x如果5秒内没有输出,要么节点不对,要么遥控器事件走的是其他通道(比如CEC)。CEC遥控器输入在CoreELEC里比较特殊,它不一定会生成/dev/input/event节点,而是直接由Kodi的CEC模块处理。如果遇到CEC遥控器,systemd早期阶段确实很难截获事件,目前我还没有特别好的通用方案,只能在Kodi起来后再处理,或者建议用户用红外遥控器完成开机选择。
6.3 fw_setenv执行成功的验证方法
有些盒子的U-Boot环境变量区是只读的,fw_setenv执行时不报错,但实际没写进去。执行完fw_setenv后,马上再执行fw_printenv | grep bootfrom,看变量值有没有变化,同时还要注意有没有类似“Warning: environment area is read-only”的输出。
我遇到过一次很隐蔽的情况:fw_printenv显示变量已经改成bootfromusb=0了,但U-Boot重启后还是从U盘启动。后来发现这个盒子的U-Boot根本不读这个变量,它通过读取eMMC特定偏移的数据来判断启动介质。这种就只能参考该机型的第三方固件源码,找到真正的切换开关。
6.4 倒计时时间的合理设置
倒计时设多长,其实取决于两个因素:安卓系统从冷启动到可用的时间,以及用户对“等待”的耐心。我的建议是:
- 最低3秒,低于3秒用户来不及反应;
- 安全5-7秒,大多数场景够用;
- 如果用户每次开机都要思考一下是看片还是刷应用,就设10秒;
- 不要超过15秒,等待太久会很烦躁,而且倒计时期间Kodi不启动,系统一直黑屏,体验很差。
如果不想每次开机都做选择,可以考虑加一个“记住本次选择”的逻辑:在settings.conf里加一个MEMORY_MODE=1,当用户按键选择进入CoreELEC后,在/storage写一个标志文件,下次开机直接进入CoreELEC不弹倒计时;安卓侧如果正常启动了,就清除这个标志。这个功能虽然稍微复杂,但对日常使用频率高的人会友好很多。
7. 实际体验与扩展想法
写这个插件的初衷只是解决自己盒子的一个小痛点:不想每次开机都扑个空、还要卡Bootloader的时间线去按遥控器。把倒计时逻辑跑通之后,我发现这套“开机选择器”的思路还能扩展出不少玩法,比如在倒计时界面放几个不同选项:确认键进CoreELEC,音量键直接进recovery,关机键直接断电。按键监听已经在做,扩展不同事件对应的动作只是加判断的事。
另一个可以做的事情是把配置界面做成Kodi插件。虽然Kodi启动晚,但Kodi起来后可以提供一个设置菜单,让你修改倒计时秒数、选择监听设备、测试重启动作,这些配置直接写进settings.conf。Kodi插件和systemd服务彼此独立,通过文件共享配置,不破坏原有的启动逻辑。
如果你用的是支持外置存储启动的盒子,但我说的这套方案在你的机器上没有成功,建议先去CoreELEC官方论坛搜一下自己机型有没有现成的boot selector实现。多读几个机型的U-Boot环境变量配置,理解它们各自的启动逻辑,再回来改这个脚本,思路会清晰很多。这套方案本身不算完美,但至少提供一个思路:用Linux用户空间的systemd服务去弥补底层Bootloader选择的不足,可以规避很多刷机变砖的风险。