Android最新设备调用默认相机录视频始终返回取消结果问题
Android 10+ 相机录制视频返回RESULT_CANCELED(结果码0)问题修复
问题原因
在Android 10及以上版本,受Scoped Storage和系统相机应用行为变更影响:
- 仅通过
Intent.FLAG_GRANT_READ_URI_PERMISSION/FLAG_GRANT_WRITE_URI_PERMISSION设置权限,无法保证所有相机应用能正确获取Uri读写权限; - 部分相机应用在接收
EXTRA_OUTPUT时,若权限不足会直接返回RESULT_CANCELED,且不会返回Intent数据; - 旧版
startActivityForResult的结果处理逻辑依赖返回的Intent数据,但新系统下设置EXTRA_OUTPUT后,相机应用不会将Uri放入返回Intent中。
修复方案
方案1:完善FileProvider权限授予(适配私有目录)
针对你当前使用应用私有目录的实现,修改权限授予逻辑,明确给相机应用授予Uri读写权限:
private void dispatchTakeVideoIntent() { Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); PackageManager pm = mActivity.getPackageManager(); ComponentName cameraComponent = takeVideoIntent.resolveActivity(pm); if (cameraComponent != null) { File mediaFile = null; try { mediaFile = createVideoFile(); } catch (IOException ex) { ex.printStackTrace(); return; } if (mediaFile != null) { Uri uriFile = FileProvider.getUriForFile(mActivity, "com.tekfirst.practical.fileprovider", mediaFile); // 移除原flags,改用明确的权限授予 mActivity.grantUriPermission(cameraComponent.getPackageName(), uriFile, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); takeVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, uriFile); takeVideoIntent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 60); takeVideoIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); startActivityForResult(takeVideoIntent, REQUEST_CODE_CAMERA); } } }
同时确保FileProvider配置正确:
- AndroidManifest.xml中声明Provider:
<provider android:name="androidx.core.content.FileProvider" android:authorities="com.tekfirst.practical.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
- res/xml/file_paths.xml配置应用私有目录:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="video_files" path="Pictures/" /> </paths>
方案2:改用MediaStore插入视频(适配Scoped Storage)
若方案1仍无效,可通过MediaStore获取公共媒体库的Uri,让相机应用直接写入媒体库,避免权限问题:
private Uri createMediaStoreVideoUri() { ContentValues values = new ContentValues(); String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); String title = "VIDEO_" + bookedOutRA.getId() + "_" + timeStamp; values.put(MediaStore.Video.Media.TITLE, title); values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Android 10+ 使用相对路径 values.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES + "/" + bookedOutRA.getId()); } else { // 旧版本指定绝对路径 File storageDir = new File(mActivity.getExternalFilesDir(Environment.DIRECTORY_MOVIES), String.valueOf(bookedOutRA.getId())); if (!storageDir.exists()) storageDir.mkdirs(); values.put(MediaStore.Video.Media.DATA, new File(storageDir, title + ".mp4").getAbsolutePath()); } return mActivity.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values); } private void dispatchTakeVideoIntent() { Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); if (takeVideoIntent.resolveActivity(mActivity.getPackageManager()) != null) { Uri videoUri = createMediaStoreVideoUri(); if (videoUri != null) { takeVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, videoUri); takeVideoIntent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 60); takeVideoIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); startActivityForResult(takeVideoIntent, REQUEST_CODE_CAMERA); } } }
结果处理逻辑优化
无论哪种方案,都不要依赖返回的Intent数据,直接使用预先保存的文件路径或Uri:
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_CAMERA) { if (resultCode == Activity.RESULT_OK) { // 方案1:使用保存的getmCurrentPath if (!TextUtils.isEmpty(getmCurrentPath)) { File videoFile = new File(getmCurrentPath); if (videoFile.exists() && videoFile.length() > 0) { // 处理录制完成的视频文件 } } // 方案2:使用createMediaStoreVideoUri返回的videoUri // 可通过ContentResolver查询获取文件信息 } else if (resultCode == Activity.RESULT_CANCELED) { // 清理空文件 if (!TextUtils.isEmpty(getmCurrentPath)) { new File(getmCurrentPath).delete(); } } } }
内容的提问来源于stack exchange,提问作者Zain -ul-eman
相关产品推荐
相关产品推荐

