为何部分Android 11 TV设备调用ContentResolver.query抛出CursorWindowAllocationException?
以下是出现问题的代码片段:
Uri uri ... if (uri.getScheme() != null && (uri.getScheme().equals("content") || uri.getScheme().equals("file"))) { try { cursor = getActivity().getContentResolver().query(uri, null, null, null, null); ... } catch (IllegalArgumentException ex) { ... } }
这段代码在多数设备上运行正常,但部分Android TV设备会抛出如下异常:
Stack trace: android.database.CursorWindowAllocationException: Could not create CursorWindow from Parcel due to error -12, process fd count=1607 at android.database.CursorWindow.nativeCreateFromParcel(Native Method) at android.database.CursorWindow.<init>(CursorWindow.java:167) at android.database.CursorWindow.<init>(CursorWindow.java:45) at android.database.CursorWindow$1.createFromParcel(CursorWindow.java:716) at android.database.CursorWindow$1.createFromParcel(CursorWindow.java:714) at android.database.BulkCursorDescriptor.readFromParcel(BulkCursorDescriptor.java:75) at android.database.BulkCursorDescriptor$1.createFromParcel(BulkCursorDescriptor.java:34) at android.database.BulkCursorDescriptor$1.createFromParcel(BulkCursorDescriptor.java:30) at android.content.ContentProviderProxy.query(ContentProviderNative.java:475) at android.content.ContentResolver.query(ContentResolver.java:1183) at android.content.ContentResolver.query(ContentResolver.java:1115) at android.content.ContentResolver.query(ContentResolver.java:1071)
已知信息:
- 所有
CursorWindowAllocationException异常报告均来自Android TV设备 - 涉及设备均运行Android 11系统
- 无法在Android 11 TV模拟器上复现该问题
暂不确定getActivity().getContentResolver().query是持续抛出异常还是偶发触发。异常错误码固定为-12,但process fd count数值会变化,例如:
"Could not create CursorWindow from Parcel due to error -12, process fd count=1607"
问题原因分析
文件描述符(FD)耗尽
异常中的process fd count=1607是核心线索。Android系统对单进程可打开的FD数量有上限,当进程打开的FD(包括文件、数据库连接、IPC管道等)超过阈值时,会导致资源分配失败。错误码-12对应Linux系统的资源不足错误,结合FD计数来看,大概率是FD耗尽导致无法创建CursorWindow所需的内存或IPC资源。Android TV系统定制差异
模拟器无法复现,说明问题和真实TV设备的硬件/系统定制有关:- 部分TV厂商可能修改了系统的FD上限,比标准手机系统更低;
- TV设备后台运行的系统服务、进程占用的FD更多,留给第三方应用的余量更少;
- Android 11在TV端的Content Provider实现可能存在IPC层面的FD泄漏,频繁调用查询会导致FD累积。
Cursor资源未正确回收
代码未展示Cursor的关闭逻辑,如果cursor使用后未在finally块或通过try-with-resources语法关闭,会导致Cursor持有的FD无法释放,长期积累后耗尽进程的FD配额,在频繁查询场景下更容易触发。Content URI的资源占用特性
针对content或filescheme的URI查询,尤其是访问媒体库等系统Content Provider时,可能会打开额外的文件描述符(比如媒体文件本身的FD),若这些资源未被正确回收,会加速FD耗尽。
解决方案建议
- 确保Cursor正确关闭:使用try-with-resources语法自动管理Cursor资源,或在
finally块中显式关闭:try (Cursor cursor = getActivity().getContentResolver().query(uri, null, null, null, null)) { // 处理cursor数据 } catch (IllegalArgumentException | CursorWindowAllocationException ex) { // 异常处理 } - 减少重复查询:避免频繁执行相同的Content Provider查询,考虑缓存查询结果;
- 监控FD使用:在测试环境中用
adb shell lsof -p <进程ID>查看FD占用情况,定位泄漏点; - 捕获异常并降级处理:在catch块中添加对
CursorWindowAllocationException的捕获,避免应用崩溃,必要时可尝试释放部分资源后重试; - TV设备适配优化:若确认是TV系统FD上限较低,优化应用资源使用,减少同时打开的文件、数据库连接数量。
内容的提问来源于stack exchange,提问作者Hong

