☰
Android文件系统疑难杂症排查:从Ext4底层到权限层实战
2026/10/7 19:49:13 网站建设 项目流程

1. 问题复现与排查思路

Android设备上遇到文件系统问题,最让人头疼的不是报错本身,而是报错信息千奇百怪,表面现象和根本原因往往隔着好几层。我最近处理的一台测试机就出现了典型的综合症:App写入文件偶尔失败、/storage/emulated/0/Android/data/目录下部分应用文件读不到、df显示空间充足但实际写入却报No space left on device,重启后系统还时不时进入只读挂载状态。这些现象散落在不同应用、不同目录,看起来毫无关联,实际追下去全都指向Ext4文件系统层的几个深层问题。

先说结论:Android的存储架构是Linux内核 + Ext4文件系统 + FUSE/sdcardfs权限层 + 应用沙箱四层叠加的复合结构。绝大多数文件系统层面的“疑难杂症”,要么是Ext4本身的元数据或日志出了问题,要么是权限层和应用层之间的交互不匹配,要么是分区挂载参数和实际硬件能力之间产生了矛盾。排查时必须先确定问题究竟发生在哪一层,而不是在应用层反复试错。

这篇文章我按实际排查顺序来写,从最表层的现象定位到底层的Ext4机制,覆盖分区布局、挂载参数、日志系统、权限模型、空间计算、损坏恢复这几个关键层面。适合正在被Android存储问题折磨的开发、测试和运维同学参考,也适合想系统了解Android文件系统底层机制的人。

1.1 先从Android存储架构说起

Android的存储架构和桌面Linux有本质区别。桌面Linux上,/home下的文件基本就是普通目录,权限模型简单直接。Android为了在兼顾多用户隔离、应用沙箱、外部存储共享的同时保证性能,设计了一套分层结构:

  • 底层是物理分区,通常用Ext4格式化,挂载到/data、/system、/vendor等节点;
  • 中间层是sdcardfs或FUSE,负责把/data/media这个真实目录映射成/storage/emulated/0,并在此层实现按应用UID和用户ID的权限过滤;
  • 最上面是应用层,通过context.getExternalFilesDir()等方法拿到的是/storage/emulated/0/Android/data/<包名>/files这类路径,实际对应的物理位置在/data/media/0/Android/data/<包名>/files。

我遇到的大量问题,根源都在这个映射关系上。比如热词里频繁出现的/storage/emulated/0/Android/data/com.tencent.tmgp.sgame/files/pandora/路径,底层对应的就是/data/media/0/Android/data/com.tencent.tmgp.sgame/files/pandora/。如果/data/media所在分区的Ext4元数据出现异常(比如目录项损坏、inode分配冲突),就会出现文件存在但读不到、写入失败、目录消失等诡异现象,而且现象往往只影响某个特定目录树下的一小部分文件。

1.2 排查前的准备:工具和基线信息

动手排查前,先把这些信息收集齐,否则后面很多判断都会失去依据:

  1. 系统版本与内核版本(adb shell getprop ro.build.version.release、adb shell uname -a);
  2. 设备型号与存储芯片型号(adb shell cat /proc/partitions,必要时查硬件规格);
  3. 问题路径的挂载点(adb shell mount | grep ext4);
  4. 问题发生的时间点和当时的操作序列(这个最关键,能大大缩小排查范围);
  5. 应用层报错信息(logcat中与存储相关的行);
  6. 内核日志(adb shell dmesg,注意过滤存储相关关键词)。

工具方面,常规adb和shell命令就能完成80%的排查工作。如果需要深入Ext4层,还要准备e2fsck、debugfs、dumpfs这些工具,后续章节会详细讲用法。注意Android设备上默认不一定有这些工具,可能需要借助刷机包中的工具或者编译一个静态版推入设备。

2. 按现象分层定位:应用层、权限层还是文件系统层

问题定位的第一步不是急着看文件系统细节,而是先确定问题出在哪个抽象层。我习惯用一个简单的三步筛选法,能快速把问题归到正确的方向。

2.1 第一步:区分应用层错误与系统层错误

先在应用侧复现问题,同时抓取logcat。如果logcat里出现E/StorageManager、E/Vold、E/Ext4、I/FS相关日志,基本可以直接进入系统层排查。如果只有应用自身的IOException、SQLiteException,那大概率是应用层逻辑问题,但也不能完全排除底层原因——比如文件系统只读时,SQLite会报Permission denied或Read-only file system,这种就需要往底层走。

实操时我常用这个命令并行抓取:

adb shell "logcat -v threadtime > /data/local/tmp/logcat.txt &" adb shell "dmesg -w > /data/local/tmp/dmesg.txt &"

然后复现问题,操作结束后杀掉这两个进程,把日志拉回本地慢慢分析。重点搜这些关键词:EXT4-fs error、I/O error、No space left、Read-only file system、Permission denied、Structure needs cleaning、Corruption。如果出现前半段这些关键词,问题基本锁定在Ext4层;如果只有应用层报错,先把文件系统层排除掉,再去查应用逻辑。

2.2 第二步:验证权限层是否正常

Android的权限层是套路最深的地方。Android 11以后强制分区存储(Scoped Storage),应用访问/storage/emulated/0/Android/data/下其他应用的目录被严格限制。这个限制不是Ext4文件系统层面的,而是FUSE层和应用框架层强加的。

判断方法很简单:如果同一个路径用adb shell的root或shell权限访问完全正常,但应用内访问失败,那问题大概率出在权限框架,而不是文件系统。比如热词里那个经典的报错——unable to chmod '/storage/emulated/0/android/data/com.xjs.ehviewer': operation not permitted——就是典型的权限层拦截,Ext4本身完全支持chmod操作,是上面的sdcardfs/FUSE层按Android的权限策略拒绝了应用对他人目录的权限修改。

这个场景下不要浪费时间在文件系统层折腾。正确做法是检查应用的目标SDK版本、是否申请了正确的权限、以及访问路径是否合规。Android 11以后,应用可以无额外权限访问自己包名目录,但访问其他应用的目录(即使是Android/data下的同级目录)默认是不允许的。

2.3 第三步:用挂载状态判断文件系统是否健康

最后看文件系统层。执行adb shell mount或adb shell cat /proc/mounts,检查关键分区是否以预期参数挂载。

adb shell "cat /proc/mounts | grep -E 'ext4|f2fs'"

输出示例:

/dev/block/mmcblk0p64 /system ext4 ro,seclabel,relatime,errors=panic 0 0 /dev/block/mmcblk0p65 /data ext4 rw,seclabel,nosuid,nodev,noatime,discard,resuid=0,resgid=0,errors=panic 0 0

重点关注这几个点:

  • /data分区的挂载选项是否为rw;
  • 是否带有errors=panic或errors=remount-ro;
  • 是否启用了discard(对应trim操作);
  • 如果分区变成ro,那基本就是Ext4检测到错误后自动降级成只读保护数据了。

如果你发现/data变成只读,先别急着重启或格式化。这通常是内置保护机制在起作用,直接重启往往会让问题恶化,甚至导致分区无法挂载。后面第4章会详细讲安全的处理流程。

3. Ext4核心机制与常见故障模式的对应关系

到了这个层面,就得真正理解Ext4的工作方式了。我对Ext4的理解是:它是一个以块(block)为最小存储单位、以inode管理文件元数据、以日志(journal)保证崩溃一致性的文件系统。Android上的绝大多数深层问题,都能归到这三个机制上。

3.1 块分配与空间耗尽的假象

Ext4的空间管理单位是块(默认4KB),块再聚合成块组(block group)。每个块组有独立的块位图(block bitmap)和inode位图(inode bitmap),分别记录哪些块被占用、哪些inode被分配。

有一个高频坑:df显示空间充足,但应用写入却报No space left on device。多数人第一反应是“系统统计错了”,实际上Ext4确实可能出现空间充足但无法写入的情况,原因通常是这两个:

  1. inode耗尽:块有剩余,但inode用完了,无法创建新文件。用df -i可以验证;
  2. 预留块耗尽:Ext4默认给root用户预留5%的块(mke2fs -m参数控制),普通应用进程没有root权限时无法使用这部分预留空间。如果非root用户已使用的是那95%的部分,就会报空间不足。

排查时两条命令并行验证:

adb shell "df -h /data" adb shell "df -i /data"

如果IUsed接近100%,那就是inode耗尽;如果Use%接近95%而IUsed%很低,则是预留块机制在起作用。

还有一个经常被忽略的因素是孤儿文件。Ext4在日志中维护孤儿文件列表,崩溃后未完整删除的文件会暂时占着inode和块。e2fsck时会清掉这些孤儿并释放空间,但在此之前df读到的信息可能是过时的。

3.2 日志系统:为什么掉电后一切都会坏

Ext4的日志(journal,默认在/data分区内以inode形式存在,对应/proc/fs/ext4/<设备>/journal)采用的是JBD2机制。写数据时,元数据变更会先写入journal,再提交到主文件系统。这样即使中途掉电或系统崩溃,重启后通过journal重播(replay),就能恢复到崩溃前一致的状态。

但journal不是万能的。我遇到过的故障大致可以分成三类:

  • JBD2 IO错误:底层存储(eMMC/UFS)返回IO错误,journal写入失败。内核日志会出现JBD2: IO error,Ext4会进入只读模式保护数据。Android在这时配合errors=panic会直接触发panic重启;
  • Journal失效:journal超级块损坏,Ext4无法重播日志,挂载时报Journal checksum error,系统可能直接拒绝挂载,或者在e2fsck时提示需要重建journal;
  • 元数据与新数据不一致:文件内容本身没落盘,但inode元数据已经提交到了journal。重启后文件系统认为文件已经写入,但实际数据块是旧的或空的。这种错误很难自动检测,因为Ext4默认并不校验文件数据块的完整性(data=ordered模式下,数据会先于元数据提交,能降低这类风险,但无法完全消除)。

理解journal机制对排查很有帮助。比如你看到应用报文件内容不正确,第一反应可能是应用bug,但有没有考虑过是ext4日志重播时数据不一致导致的?特别是设备非正常关机后经常复现的问题,必须把这段机制纳入排查范围。

3.3 sync/flush与性能问题的根源

Android上还有一个被抱怨最多的体验问题:文件写入慢、卡顿。原因往往出在sync、fsync、fdatasync这些系统调用上。

进程调用fsync()时,内核会把该文件相关的脏页(dirty pages)刷到磁盘,并等待IO完成。如果没有正确使用(或者使用过于频繁),会出现两种极端:要么数据安全性和持久性不佳,要么性能极差。热词里出现 “root文件系统、sync、vfs” 和 “线上服务器cpu使用达到100%了” 就属于这个范畴——大量进程同时执行fsync,IO队列拥塞,CPU等待IO占比飙升。

在Ext4上定位这类问题,有三个关键参数:

  1. /proc/sys/vm/dirty_ratio和/proc/sys/vm/dirty_background_ratio:控制脏页堆积多少开始主动刷盘;
  2. /sys/block/<设备>/queue/scheduler:IO调度策略(Android上常见cfq、mq-deadline、none);
  3. /proc/fs/ext4/<设备>/options:挂载选项,data=ordered/writeback对同步语义影响巨大。

data=ordered是Android的常见选择,保证数据先落盘再提交元数据日志,安全性好但性能略降;data=writeback性能更好但牺牲一定的崩溃一致性。如果应用对单文件写入频繁且对性能敏感,要综合考虑挂载选项和应用侧的刷盘策略,而不是盲目改文件系统参数。

4. 实操案例一:文件系统只读与Ext4错误恢复

我手上这台测试机最严重的问题是/data分区开机一段时间后变成只读,logcat和dmesg里出现了EXT4-fs error (device mmcblk0p65): ext4_find_entry: ...之类的报错。这种场景下,很多新手第一反应是重启,结果重启后直接进不了系统。我来说说正确的处理顺序。

4.1 只读模式出现后的第一反应:不要慌,先收集现场

检测到/data只读时,先别重启,立刻抓取现场信息:

adb shell "cat /proc/mounts | grep /data" adb shell "dmesg | grep -E 'ext4|JBD2|I/O' | tail -200" adb shell "cat /proc/fs/ext4/mmcblk0p65/options" # 设备名按实际输出替换

这时Ext4通常已经把错误原因写进了内核日志,比如是哪类操作触发错误、错误码是什么、涉及哪个inode或块组。历史上常见错误码有:

  • -EIO:底层块设备IO失败,需要关注存储芯片健康状态;
  • -ENOSPC:空间或inode耗尽;
  • -EROFS:挂载为只读后继续尝试写入;
  • -EUCLEAN(Structure needs cleaning):文件系统元数据不一致,需要e2fsck清理。

把dmesg里EXT4-fs error前后20行的上下文都记录下来,重点是触发错误的文件路径或inode编号,这能帮你判断错误是偶发的坏块还是全局性的元数据损坏。

4.2 正确进入恢复模式并执行e2fsck

确认无法在线恢复后,需要把设备重启到能卸载/data的状态。Android里通常用fastboot或recovery模式来做:

  1. 关机,进入fastboot或recovery模式;
  2. 按键进入bootloader,执行fastboot boot <recovery.img>或直接进入已安装的recovery;
  3. 在recovery中用adb shell执行e2fsck。

但e2fsck之前,必须先确认要修复的设备节点:

adb shell "ls -l /dev/block/by-name/ | grep -E 'userdata|data'"

得到节点后(比如/dev/block/by-name/userdata),执行只读模式的检查:

adb shell "e2fsck -fn /dev/block/by-name/userdata"

只加-f(强制检查)和-n(非交互,只报告不修复),先看看到底有多少问题、什么性质的问题。如果这里报错数量很少,有时后续加-p参数自动修复就能搞定。

如果-fn模式下大范围输出Inode ... has ...之类的校验结论有问题,执行自动修复:

adb shell "e2fsck -fy /dev/block/by-name/userdata"

-y是自动回答“yes”,适合无人值守。不过要注意:大的文件系统(几十GB)完整修复可能需要很长时间,中途不要中断,否则可能造成更严重的损坏。

4.3 修复后的验证与预防

修复完成后,重新挂载验证:

adb shell "mount -t ext4 /dev/block/by-name/userdata /data" adb shell "touch /data/test_write && echo ok"

并再次执行dmesg | grep -i ext4确认没有新的错误。同时检查/data分区的可用空间和inode数量:

adb shell "df -h /data && df -i /data"

如果分区空间长期在90%以上,而且排查中发现过inode耗尽的问题,就可以确定根因是“写入量太大但文件都太小导致inode耗尽”。这种场景的预防方案有两个方向:一是清理明显无用的文件,二是考虑重新分区时给该分区更大的inode比例(mke2fs -i),但这需要改分区,成本较高。

e2fsck修复完之后,还有一件事容易被忽略:检查journal是否正常。如果修复日志里出现了Journal recovery提示,修复后要确认journal可重放:

adb shell "dumpe2fs -h /dev/block/by-name/userdata | grep -i journal"

看到Journal inode: 8一类输出即为正常,如果显示Journal features: needs_recovery或类似状态,说明还有未完成的恢复。

5. 实操案例二:应用私有目录文件异常与权限/挂载的纠缠

第二个案例对应的就是热词里反复出现的那种场景:应用访问/storage/emulated/0/Android/data/<包名>/files下的文件时,要么读不到,要么报权限错误,要么能创建但重启后消失。这个问题的排查路径跟前面案例完全不同,重点在FUSE层和路径映射。

5.1 分清FUSE层的可能性与Ext4层验证

先验证文件在物理层是否存在。用root权限直接检查/data/media/0/Android/data/<包名>/files的真实内容:

adb shell "su -c 'ls -la /data/media/0/Android/data/com.example.app/files/'"

如果物理路径下文件完整、权限正确,就说明Ext4层没问题,问题出在FUSE/sdcardfs的映射或App的访问路径上。此时继续深挖FUSE层。

如果物理路径本身也找不到文件,那有可能是Ext4层的目录项或文件名编码问题。Ext4默认支持大小写敏感文件名,文件名长度上限255字节。如果应用创建的文件名包含超长字符(尤其中文、emoji),目录项索引可能异常。验证方法是用debugfs检查具体目录内容:

adb shell "debugfs -R 'ls -l /data/media/0/Android/data/com.example.app/files/' /dev/block/by-name/userdata"

debugfs能直接读取磁盘上的目录项,绕过已挂载文件系统的缓存和过滤。如果这里能看到文件但普通ls看不到,那更说明权限层的问题。

5.2 深入FUSE层:权限模型的真相

Android的FUSE层(也就是用户态的/system/bin/sdcard守护进程)负责将/data/media映射为/storage/emulated/0。它通过一组permission policy来控制谁可以访问什么。核心逻辑如下:

  • 应用的访问受其UID(AppId)和GID(如sdcard_r、sdcard_rw、everybody)约束;
  • /storage/emulated/0/Android/data/是一个特殊目录:每个应用可以自由读写自己包名的子目录,但没有权限访问同级其他应用的目录;
  • 通过FUSE层返回的EACCES或EPERM,反映的是Android的访问控制策略,而不是底层文件系统不支持该操作。

遇到operation not permitted时,第一件事就是确认调用方身份和访问目标的归属关系:

# 查看应用UID adb shell "dumpsys package com.example.app | grep userId" # 查看目标目录的所有者 adb shell "ls -ln /storage/emulated/0/Android/data/com.other.app/files/"

如果App A试图写App B的Android/data目录,被FUSE拦截是Android的设计行为(Android 11+尤其严格),不是文件系统bug。此时向右走路的正确解法是调整应用架构,使用MediaStore或SAF(Storage Access Framework),而不能靠改文件系统权限绕过。

5.3 挂载参数与setuid/setgid位的影响

还有一些隐蔽问题出在挂载参数本身。查看/data的挂载选项,确认有nosuid,nodev,noexec,这是Android的默认安全配置。有些App会把可执行文件放在自己的私有目录里尝试执行,但挂载选项禁止了/data分区上的可执行位,会导致Permission denied或Exec format error。这同样不是Ext4的问题。

我遇到过一种坑是:应用借用FileProvider共享文件给其他应用(热词里的content://com.tencent.wework.fileprovider/...就是一个典型),然后通过ContentResolver打开文件流。如果目标文件在Ext4上有不正确的SELinux标签,即使普通权限检查通过,SELinux也会拒绝访问并记入avc: denied日志。

排查SELinux问题的方法:

adb shell "dmesg | grep avc | tail -20"

如果看到avc: denied { read } for pid=... scontext=u:r:untrusted_app:s0:c... tcontext=u:object_r:app_data_file:s0...,就是SELinux策略拦截。可以临时用setenforce 0验证(仅限测试机),确定后调整SELinux策略或应用数据目录的标签。

5.4 自建文件存储时的映射与路径建议

最后说一点开发侧的建议。如果是你自己的App,文件路径设计上就要避开后面这些坑:

  • 不要硬编码/storage/emulated/0/Android/data/<包名>/,改用context.getExternalFilesDir()或getExternalFilesDirs();
  • 需要跨应用共享文件时,用FileProvider或MediaStore,不要在Android/data下手动修改权限;
  • 文件很大或很多时,考虑改用getExternalCacheDir()并建立自己的清理策略,避免把外部存储的Android/data目录当成不受限的沙箱;
  • 有些机型厂商还会对/sdcard路径做额外的映射或限制,适配时务必用官方API而不是拼接字符串路径。

6. 实操案例三:性能问题与Ext4的调优方向

文件系统层面的性能问题排查起来比较抽象,但思路是清晰的。先确认瓶颈是IO层、文件系统层还是进程调度层,再对症下药。

6.1 定位IO阻塞的大致位置

性能劣化时,先抓整体IO状态:

adb shell "top -n 1 -b | head -30" adb shell "cat /proc/diskstats | grep -E 'mmcblk|sda'" adb shell "iostat -x 1"

如果发现iowait很高,或者top里有进程长时间处于D(不可中断睡眠)状态,基本可以确定IO层是瓶颈。D状态的进程通常正卡在等底层块设备返回,很多情况下是Ext4在刷盘或journal提交。

再利用perf或简单的trace工具确认:

adb shell "echo 'file fs/ext4/* +p' > /sys/kernel/debug/dynamic_debug/control" # 需要root且有debugfs

打开Ext4的动态调试信息后,重新触发操作,dmesg里能看到大量ext4_da_write_begin、ext4_da_get_block_prep等调用点。如果这些日志密集出现在卡顿前后,就说明问题在Ext4的写路径上。

6.2 识别过量fsync与日志提交开销

常见的一个坑是应用在UI线程里频繁调用FileOutputStream.flush()+FileDescriptor.sync(),每个拿出来都会触发一次journal提交。主线程执行时,一旦底层eMMC/UFS写入速度慢,UI就会卡顿。实测中,几只fsync会让原本几毫秒的写入变成几百毫秒。

检查应用是否在过度刷盘,可以用strace:

adb shell "strace -p <pid> -e trace=fsync,fdatasync,sync_file_range -c"

跑一段时间后结束,统计fsync调用次数和耗时。如果次数异常多或者单次耗时超过100ms,那问题就在应用侧的同步策略。

正确的策略是:普通数据写入用FileOutputStream的buffer,最后一次性flush+sync;需要保证即时持久化的关键数据(如数据库事务提交)才单独做SQLiteDatabase的事务管理,不要每次插入都调用sync。

6.3 碎片化、trim与GC的相互作用

ext4的碎片问题不如老式FAT32严重,但Android上eMMC/UFS的垃圾回收(GC)机制与文件系统碎片会互相影响。文件系统碎片化后,GC的效率降低,写入放大(write amplification)上升,性能随之劣化。

运维上能做的:定期执行fstrim。

adb shell "fstrim -v /data"

Android系统本身会在空闲时自动执行fstrim(通常在DeviceStorageMonitorService或Vold中实现),但如果你发现设备长时间不休眠、一直满载运行,自动trim可能没机会执行。手动执行一次可以看到类似/data: 8.5 GiB (9123456789 bytes) trimmed的输出,之后大块连续写入的性能会明显改善。

6.4 修改挂载参数的实际经验

如果不是硬件原因,而是挂载参数需要调整,可以按设备类型来选:

场景推荐挂载选项理由
日常使用/追求稳定性data=ordered,noatime,nodiratime顺序提交保证一致性,减少访问时间更新IO
大量小文件随机写入data=writeback,delallocwriteback配合延迟分配能提升小文件写入性能,但崩溃一致性稍弱
空间紧张但性能优先考虑关闭discard并手动定期trim每次删除时执行discard会带来额外开销,但关闭后要注意定期trim防止GC退化

修改挂载参数通常要在fstab或内核cmdline中改,需要root或自定义内核/ramdisk。测试机改完后务必跑一轮完整的功能回归,因为data=writeback下如果发生异常掉电,文件内容出问题的概率确实更高。

7. 常见问题速查与实践心得

最后把日常支持中遇到的高频问题整理成一张速查表,再补几条个人经验。

7.1 问题现象与排查方向速查表

现象优先排查方向关键验证命令
应用写入报No space left on deviceinode是否耗尽、预留块是否用光df -i /data、df -h /data
分区只读挂载Ext4错误触发自动只读dmesg | grep -i ext4
文件存在但应用读不到FUSE权限/Scoped Storage限制su -c ls /data/media/0/...
operation not permittedFUSE策略/SElinuxdmesg | grep avc
掉电后文件损坏或丢失Journal重放/未正常卸载e2fsck -fn、dumpe2fs
App内卡顿明显fsync/IO阻塞strace -e trace=fsync
开机特别慢挂载时journal恢复或fsckdmesg | grep -i recovery
文件大小与实际占用不符Ext4延迟分配/reserved blocksdu与ls -l对比、stat

7.2 独家排障技巧:把不确定性转变成确定性

我自己踩过几次坑之后,慢慢形成了几个习惯,分享出来:

  1. 任何只读挂载都不立即重启。优先看dmesg,能在线导出就导出,重启后现场就没了。
  2. 对/data分区做定期健康检查。开发测试机上可以每周跑一次e2fsck -fn,上线设备至少要做fstrim和空间告警。
  3. 写应用时不要假设/data一定可靠。合适的抽象是:把SharedPreferences和数据库做事务管理,把大文件写入尽量用缓冲区并最后sync,把日志和缓存目录单独清理。
  4. 权限问题先确认目标文件归属再定位。好多“文件系统bug”其实是App试图偷看别人数据,被系统的权限设计拦住了。
  5. 保存基线快照。同一型号设备上,如果某种问题反复出现,保存一个正常的/proc/mounts、dumpe2fs输出作为基线,后续对比定位能快很多。
  6. 熟悉stat命令的含义。看到文件权限、UID/GID、大小、block数都对上了,才能说问题不在文件系统层。
  7. 动态调试信息非常耗性能。打开ext4的dynamic_debug后不要长时间挂着,定位完立刻关掉,否则会影响测试机的性能验证结果。

在实战中,我见过最多的情况是“看起来像文件系统问题、其实是上层问题”和“看起来像设备问题、其实是应用问题”两类。只要把Android存储的分层架构想明白,每一层该看哪些日志、有哪些工具、有哪些约束,排障速度就完全不一样了。希望这篇日志能帮你少走几次弯路,特别是别在权限层问题上白白去修文件系统。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询