App在物理设备安装运行后崩溃:DISPLAY_NAME列值为空问题排查求助
解决三星Galaxy A30(Android 11)上App启动崩溃问题:DISPLAY_NAME column is null
从你提供的崩溃栈来看,核心问题是访问Content Uri时,DISPLAY_NAME字段返回null,导致ContentUriFileDescriptor抛出BadContentProviderException。这个问题在Android 11设备上出现,大概率和分区存储(Scoped Storage)的行为变化,或者三星设备自身的Content Provider实现有关。下面是具体的排查和解决步骤:
一、先明确崩溃触发的场景
崩溃发生在App启动阶段,说明你的App在启动时(比如Application.onCreate或主Activity的初始化逻辑中)调用了SAI相关的文件/APK处理代码,尝试读取某个Content Uri对应的文件名称,但该Uri在三星A30上无法返回有效的DISPLAY_NAME值。
二、针对性解决方案
1. 检查Content Uri的权限与有效性
- 如果你的App是通过文件选择器(比如
Intent.ACTION_OPEN_DOCUMENT)获取的Content Uri,务必在获取Uri后调用takePersistableUriPermission持久化权限。Android 11下,重启App后临时权限会失效,此时访问Uri可能返回null值:// 获取Uri后立即执行 ContentResolver resolver = getContentResolver(); int takeFlags = intent.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); resolver.takePersistableUriPermission(uri, takeFlags); - 确认启动时访问的Uri对应的文件是否还存在,比如用户是否删除了该文件,或者Uri是之前缓存的无效值。
2. 修改ContentUriFileDescriptor的容错逻辑
如果你的项目依赖了SAI的库,或者直接使用了ContentUriFileDescriptor类,需要修改其name()方法,增加对DISPLAY_NAME为null的处理,避免直接抛出异常:
public String name() throws BadContentProviderException { try (Cursor cursor = mContext.getContentResolver().query(mUri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null)) { if (cursor != null && cursor.moveToFirst()) { String displayName = cursor.getString(0); // 当DISPLAY_NAME为null时,从Uri路径提取文件名或使用默认值 if (displayName != null) { return displayName; } return extractFileNameFromUri(mUri) != null ? extractFileNameFromUri(mUri) : "unknown_file"; } } catch (Exception e) { throw new BadContentProviderException(e); } throw new BadContentProviderException("Failed to retrieve display name for content URI " + mUri); } // 从Uri路径中提取文件名的辅助方法 private String extractFileNameFromUri(Uri uri) { String path = uri.getPath(); if (path == null) return null; int lastSlashIndex = path.lastIndexOf('/'); return lastSlashIndex != -1 && lastSlashIndex < path.length() - 1 ? path.substring(lastSlashIndex + 1) : null; }
这段代码会在DISPLAY_NAME为空时,尝试从Uri的路径中提取文件名,或者返回一个默认名称,避免崩溃。
3. 适配Android 11的分区存储权限
- 确保你的App已经申请了
READ_EXTERNAL_STORAGE权限(targetSdk 31下,分区存储模式仍需要这个权限来访问媒体文件),并且在运行时请求用户授权:if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityResultContracts.RequestPermission requestPermission = new ActivityResultContracts.RequestPermission(); registerForActivityResult(requestPermission, isGranted -> { if (isGranted) { // 权限已授予,执行文件操作 } else { // 提示用户开启权限 } }).launch(Manifest.permission.READ_EXTERNAL_STORAGE); } - 对于非媒体文件,建议使用
Intent.ACTION_OPEN_DOCUMENT获取Uri,而不是直接访问文件路径,这是Android 10+推荐的方式。
4. 排查启动时的自动操作逻辑
检查App启动时是否有自动加载某个文件的逻辑,比如缓存的Uri。如果是,可以改为让用户手动触发该操作,或者在启动时先检查Uri的有效性,避免直接访问无效Uri导致崩溃。
三、测试建议
- 在三星A30上,手动触发文件选择和处理逻辑,看是否会出现同样的问题,排除启动时自动操作的异常场景。
- 在代码中增加日志,输出要访问的Content Uri,以及查询Cursor时的所有列值,确认DISPLAY_NAME是否确实为null,帮助定位问题根源。
内容的提问来源于stack exchange,提问作者Nomel
相关产品推荐
相关产品推荐

