用户反馈里有一组很高频的话:”重启了没用””关机再开就好了”。这两句放在一起并不矛盾,因为这两个动作重置的范围本来就不重合。为压缩开机耗时,通用实现里存在一条快速启动路径,”重启”与完全断电后的冷开机走的初始化流程可以不一样,哪些东西被重建、哪些被原样保留,取决于厂商实现与项目配置。

要澄清的是范围:这里只讲 Android 与 AOSP 层面的通用机制,不讨论具体机型和具体版本的行为差异。工程上真正需要的不是”哪个更干净”的结论,而是一张对应表——某个症状属于哪一层,该用哪个动作,以及怎么在日志里确认这次到底是走了哪条路径。

重启与冷开机两条启动路径的重置范围差异

一、两条路径的差别在”电源域”,不在”界面刷新”

一个容易忽略的事实是:在通用实现里,点”重启”同样会重新加载内核、重新拉起 init、zygote、system_server 和全部应用进程。所以”重启只是刷新桌面”这种说法不准确,它确实重建了整个用户态。差别在另外两处:一是重启不断开全部电源域,二是快速启动路径会压缩或跳过一部分上电阶段的自检。

环节 重启(热路径) 完全断电后冷开机
Bootloader 与上电时序 通常走快速路径,部分硬件训练/枚举被跳过或复用上次结果 重新走上电初始化与硬件自检
内核与驱动 重新加载,内存里的状态归零 同样重新加载
zygote / system_server 进程重建;预加载的类与资源重新映射 进程重建,行为与前者基本相同
ART 编译产物、boot image 不重新生成,直接复用磁盘上的持久化文件 同样复用,不因断电而重建
用户数据分区挂载与检查 重新挂载;是否触发一致性检查由条件决定,不由路径决定 重新挂载,条件同上
传感器协处理 / 常驻外设 MCU 多数不受 Android 重启影响,可能只被重新初始化或干脆保持运行 断电后回到默认状态,重新上电时重新校准
基带与通信子系统 有独立固件,可能被单独复位而非整机断电 随整机断电重新加载
电源管理、充电与电量计寄存器 保持通电,异常值不会被清零 重新上电后复位
崩溃与保留区日志 部分保留区设计成跨热重启存活 断电后不再保留

这张表里的”多数 / 可能 / 由条件决定”不是回避,而是这块行为本身在通用实现里就是可配置的,落到某台机器上要以它实际暴露的信息为准。关键的判断只有一句:重启重新加载的是进程,不是电源。凡是症状来自”不断电的那部分”,重启注定无效——这正好解释了用户那句”重启没用,关机有用”。

系统更新是这条线上一个特殊分支。采用 A/B 分区方案时,更新的收尾动作发生在切槽位的那一次重启上:写系统分区的新镜像、切换活动槽位、必要时触发数据迁移与首次启动后的后台优化。它既不是普通重启也不是普通冷开机,处置设备问题时要先把”刚做过系统更新的开机”单独标出来,因为这段时间的异常多数与新槽位的首启收尾有关。

二、”重启一下就好了”,好在哪儿

重启之所以对一大类问题有效,是因为它一次性做了下面这些事:

  1. 进程重建。所有应用与系统服务的堆内存、文件描述符、线程数、未释放的唤醒锁全部归零,泄漏和句柄堆积被清掉。
  2. 连接栈重连。Wi-Fi、蜂窝、蓝牙、VPN 的 socket 全部关闭重建,NAT 老化造成的”看着连着其实不通”、链路半死状态被打破。
  3. 调度与超时任务重排。未完成作业被重新调度,到点任务重新触发,卡在队列里的任务不再占着通道。
  4. 系统服务的内存态复位。窗口层级、通知栏、输入法与剪贴板这些主要活在内存里的状态被重建,界面错乱类症状通常在这一步消失。
  5. 进程内配置与解析缓存失效。应用重新启动时会重读配置、重新解析域名,过期副本被丢掉。
  6. 卡死或被冻结的服务被重新拉起,异常终止的进程退出码被记录,便于事后判断。

这几条覆盖的症状面很宽,而代价只有几十秒,这就是”重启”作为第一手处置的地位。但要清楚它有效的原因不是”深度清理”,而是”便宜地重建了软件层”,所以它天然管不到电源域和持久化脏数据。

三、被跳过的清理去了哪:只有断电才消失的几类现象

反过来说,有四类问题在重启路径下原样保留,用户会明确感到”这次没好用”。

  • 不断电部件的状态。传感器协处理里的队列堆积、时间戳基准漂移、触控与指纹模组的固件卡滞、通信子系统的状态机异常,这些不随 Android 用户态重建而复位。重新上电后它们会回到默认配置并跑一遍校准,比如陀螺仪零偏、触控基线这类量,本来就是在上电阶段重新标定的。这是”只有关机再开机才恢复”最典型的一层。
  • 持久化的脏数据。被写坏的缓存文件、写了一半的下载文件、损坏到一半的键值配置、数据库的残留日志文件——进程重建之后读的还是同一份坏文件,症状完全不变。这类要靠应用自己的校验失败重建逻辑处理,兜底才是清缓存与清数据。重启在这里的价值仅仅是”让校验代码再跑一次”。
  • 开机自启把坏配置又拉回来。开机广播的接收者、常驻服务、企业设备管理代理、自升级守护进程会在启动后重新加载同一份配置。如果故障根因是配置内容错,重启等于让它再错一次,而且错得更规律。
  • 磁盘上的编译与安装态产物。ART 的 boot image、按应用生成的优化产物、安装包校验缓存都是持久化的,既不因重启重新生成,也不因断电重建,只有对应的维护任务或数据清理才会动它们。

还有一条要提醒:长按电源键触发的强制重启,通常不走有序关停流程。有序关停里做的事——数据落盘、状态持久化、未完成事务提交——被整段跳过,第二类”脏数据”的风险反而上升。所以”卡死了就长按”是必要的逃生手段,但不能当成常规维护动作。同样地,”关机后静置一小段时间再开机”之所以有时更管用,是因为电容上的余电需要一个释放过程,这一点在不同硬件设计上的时间常数不同,具体机型表现不做断言。

四、做 App 的人怎么问、怎么在日志里区分

问用户之前先承认一件事:”重启一下”是个含混表达,它至少对应四种操作。要问到可判定的粒度:

  1. 你做的是哪一种:设置里点重启;先关机再按电源键开机;长按电源键强制重启;还是电量耗尽后自动关机再充电开机。
  2. 关机之后等了多久才再开机。
  3. 问题是每次都稳定复现,还是从某一次之后一直存在。
  4. 除了你的应用,同一台机器上别的应用有没有一起异常(有,则优先怀疑系统层或外设层)。
  5. 出问题之前有没有做过系统更新或应用更新。
  6. 有没有连接过外部配件、有没有进过水或高温环境。

日志侧要能回答”这次开机走的是哪条路径”,可采的线索如下(字段名与取值随厂商实现差异很大,取不到属正常,不要当错误处理):

# 开机原因线索:bootloader 写入的属性,命名各家不同
adb shell getprop | grep -iE 'bootreason|boot_reason|bootmode'

# 内核已运行时长:两条路径都会归零,只能证明"确实重起过"
adb shell cat /proc/uptime

# 各服务的启动时刻,用来对齐初始化顺序
adb shell getprop | grep 'ro\.boottime\.'

# 开机进度事件,看关键阶段是否被压缩
adb logcat -b events -d | grep boot_progress

在自己应用里,更实用的是给每次开机打一个可聚合的标识:进程首次启动时记录”当前墙钟减去系统开机后运行时长”,把这个值作为 bootId 带上后续所有上报。服务端按 bootId 聚合,就能区分”同一次开机里的重复反馈”和”用户重启之后出现的新现象”——后者才说明上一轮处置无效。

// elapsedRealtime 是自开机以来的时长,应用侧无需特殊权限即可读取
val bootKey = System.currentTimeMillis() - SystemClock.elapsedRealtime()

要注意这个近似的边界:该时长不计入深度睡眠,设备长待机后它与墙钟会出现分钟级以上偏差,直接比较会漂移。稳妥做法是只做日级对齐,或者与系统暴露的开机时间戳做一次校正;宁可标识粗一点,也不要让同一个 bootId 在跨天后变成两个。另外,是否”上次正常退出”不要靠猜,应用自己在关停回调里写一条退出记录,下次启动时看有没有这条尾巴,就能区分用户主动杀、系统回收和异常退出这三种情况。

五、批量终端的处置顺序:先分级,再选动作,清数据放最后

企业设备(含 MDM 纳管的巡检、外勤终端)里,”重启一下”会变成规模动作,所以要有阶梯,而不是让一线随手选。

  • 第一级,应用层自救:强停应用、清应用缓存、重新登录。适用症状只在本应用内、别的应用正常。前置确认:未上报数据已落盘。
  • 第二级,重启。覆盖第二节那六类,是最便宜的有效手段。
  • 第三级,关机静置后冷开机。用于第二级两次无效、且症状指向外设或电源域(传感器读数异常、指纹触控失灵、连不上网但信号格正常)。
  • 第四级,重推配置或重装应用。用于症状跟着应用版本或策略走的情况。
  • 第五级,清数据。代价是本地未上报内容、离线包、授权状态全部丢掉,必须先做数据保全检查。
  • 第六级,恢复出厂或重刷,留给前五级都无效且能复现的设备,同时把它当成硬件排查的入口而不是万能收尾。

三条纪律比阶梯本身更重要:同一手段最多试两次,两次无效就升级,不要靠重复操作碰运气;每一级的操作时间、动作与结果都要留痕,攒下的样本才能判断”这类设备在这个版本上集中在哪一层”;批量执行必须分批加随机抖动限流,同一时刻全量重启会造出开机风暴——服务端登录与同步瞬时峰值、管理通道队列堵塞、低温低电条件下冷启动失败率上升,这些后果比原本要修的小故障更麻烦。

设备分级也要写进规则:全员离线类直接走第二级再到第三级;关键功能不可用类先做远程日志采集再动设备(重启会丢掉内存里的现场);单纯体验劣化类优先在应用层解决,不该占用运维工时。

六、洹拓视角:把”重启有没有用”变成能回答的问题

我们的做法是三件事。第一,把 bootId、开机原因线索、上次退出方式做成应用内诊断页的一段可读文本,一线不用连电脑抓日志也能报出关键事实;取不到的字段就写”该机型未提供”,不要留空让人以为没实现。第二,把用户话术标准化,把第四节那六个问题放进工单模板,让”重启过”必须落到具体操作和等待时间上,这一条能把无效沟通砍掉大半。第三,把第五节的阶梯写进运维 SOP,明确”什么症状对应哪一级、每级最多两次、清数据前必须做数据保全检查”。

有一条判断要说清楚:重启的价值来自它便宜,不来自它彻底。因此”每周固定关机一次做深度清洁”这类通用建议在机制上站不住——没有对应症状时它不针对任何一层,只增加一次首启后台任务的开销。我们只按症状所在层级选动作。真正的成本不在单个用户多等几十秒,而在企业侧把”重启没用”当成设备报废的理由,或者把”清数据”当成常规手段,那才是把可定位的问题变成不可挽回的丢数据事故。