Android绑定应用Provider时触发ANR的问题排查与解决求助
这个ANR发生在应用启动阶段的ContentProvider初始化流程,还没执行到你应用自身的业务代码,所以堆栈里不会出现你写的自有代码,属于系统初始化FileProvider组件时触发的IO超时。
常见触发原因
- 外置存储状态异常:用户设备的外置SD卡损坏、处于挂载/卸载过程中、IO性能极低,调用
getExternalFilesDirs时系统需要验证外置目录的存在性,卡在内核层的accessnative调用超时 - FileProvider配置包含外置存储路径:你在AndroidManifest中为FileProvider配置的
<paths>节点包含<external-files-path>、<external-path>这类对应外置存储的路径,FileProvider初始化时会主动在主线程调用getExternalFilesDirs解析路径,直接触发IO操作 - 应用首次启动/系统升级后首次启动:该阶段系统需要为应用重建外置存储目录,首次访问目录的IO耗时会大幅提升,刚好卡在主线程的FileProvider初始化流程就会触发ANR
- 系统IO调度优先级低:如果应用是从后台缓存状态恢复启动,系统给应用的IO调度优先级低,目录访问的IO操作会被其他高优先级进程抢占,直接卡住超时
解决方案
- 移除不必要的外置存储路径配置:如果你的业务不需要通过FileProvider共享外置存储的文件,直接删除Manifest中FileProvider配置里所有涉及外置存储的路径节点,只保留内置存储相关的
<files-path>、<cache-path>即可,从根源避免初始化时访问外置存储 - 自定义FileProvider实现延迟初始化:如果确实需要用到外置存储的共享路径,自定义继承自FileProvider的子类,重写
attachInfo方法,将路径解析逻辑放到子线程执行,避免阻塞主线程,示例代码如下:
import android.content.Context; import android.content.pm.ProviderInfo; import androidx.annotation.NonNull; import androidx.core.content.FileProvider; public class LazyInitFileProvider extends FileProvider { @Override public void attachInfo(@NonNull Context context, @NonNull ProviderInfo info) { // 跳过默认的主线程同步路径解析逻辑 super.attachInfo(context, info, false); // 子线程异步初始化路径策略 new Thread(() -> { try { FileProvider.getPathStrategy(context, info.authority); } catch (Exception ignore) { // 捕获异常避免初始化失败崩溃 } }).start(); } }
修改AndroidManifest中FileProvider的android:name属性为你自定义的LazyInitFileProvider全类名即可。
- 提前预加载外置目录:如果你的应用已经获取了存储权限,可在闪屏页的子线程提前调用一次
ContextCompat.getExternalFilesDirs(context, null),提前触发系统创建和验证外置目录,避免FileProvider初始化时才首次访问产生高耗时 - 适配分区存储规则:如果你的应用targetSdk>=30,确保FileProvider配置的路径都符合分区存储访问规则,不要访问未授权的外置存储路径;如果targetSdk<=30,可开启
android:requestLegacyExternalStorage="true"降低分区存储带来的额外访问耗时。
内容的提问来源于stack exchange,提问作者t0m
相关产品推荐
相关产品推荐

