SDK23+下外置SD卡文件Content URI生成与视频打开失败问题
嘿,我来帮你搞定这个问题!你遇到的执行失败,核心原因是 FileProvider默认只支持访问应用私有目录、以及外部存储的公共目录(比如系统默认的Download、DCIM),完全没法直接碰外置SD卡(可移动存储)的目录。那个/storage/9C33-6BBD/属于二级外部存储,不在FileProvider预设的路径范围内,所以调用FileProvider.getUriForFile()肯定会出问题。
下面分Android版本给你对应的解决方案:
方案1:Android 10及以上(API 29+)用MediaStore获取URI
从Android 10开始的Scoped Storage机制,推荐通过MediaStore来查询文件的内容URI,这样既符合系统规范,又能正常访问外置SD卡的文件:
// 用异步任务避免阻塞主线程 new AsyncTask<Void, Void, Uri>() { @Override protected Uri doInBackground(Void... voids) { String targetFilePath = "/storage/9C33-6BBD/Downloads/video.mp4"; // 查询需要的字段 String[] projection = {MediaStore.Video.Media._ID}; // 按文件路径匹配 String selection = MediaStore.Video.Media.DATA + "=?"; String[] selectionArgs = {targetFilePath}; Cursor cursor = context.getContentResolver().query( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, null ); Uri videoUri = null; if (cursor != null && cursor.moveToFirst()) { long videoId = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID)); videoUri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, videoId); cursor.close(); } return videoUri; } @Override protected void onPostExecute(Uri uri) { if (uri != null) { Intent viewIntent = new Intent(Intent.ACTION_VIEW); viewIntent.setDataAndType(uri, "video/mp4"); viewIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); context.startActivity(viewIntent); } else { Toast.makeText(context, "找不到目标视频或无访问权限", Toast.LENGTH_SHORT).show(); } } }.execute();
方案2:Android 6.0-9.0(API 23-28)用存储访问框架(SAF)
这个版本里FileProvider依旧不支持外置SD卡,最稳妥的方式是通过SAF让用户主动授权访问目标文件:
第一步:启动文件选择器
private static final int REQUEST_CODE_PICK_VIDEO = 1001; // 启动SAF选择视频文件 Intent pickIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT); pickIntent.addCategory(Intent.CATEGORY_OPENABLE); pickIntent.setType("video/mp4"); // 可选:引导用户直接到SD卡的Download目录 Uri initialDirUri = Uri.parse("content://com.android.externalstorage.documents/document/9C33-6BBD%3ADownloads"); pickIntent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialDirUri); startActivityForResult(pickIntent, REQUEST_CODE_PICK_VIDEO);
第二步:处理选择结果
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_PICK_VIDEO && resultCode == RESULT_OK) { if (data != null) { Uri videoUri = data.getData(); // 可选:持久化权限,后续不用再让用户授权 getContentResolver().takePersistableUriPermission(videoUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); Intent viewIntent = new Intent(Intent.ACTION_VIEW); viewIntent.setDataAndType(videoUri, "video/mp4"); viewIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(viewIntent); } } }
额外注意事项
- 不管哪个版本,都需要申请
READ_EXTERNAL_STORAGE权限,API 23+还要记得动态申请; - Android 11+(API 30)如果想直接硬编码路径访问外置SD卡,还得申请
MANAGE_EXTERNAL_STORAGE权限,但这个权限需要在应用商店说明用途,一般不推荐用; - 尽量别硬编码SD卡路径,不同设备的挂载路径可能不一样,优先用系统API或SAF动态获取。
内容的提问来源于stack exchange,提问作者Binoy Babu
相关产品推荐
相关产品推荐

