You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Q下将录制视频存入图库:如何正确生成关联URI?

Let's break down the problems you're facing and fix them one by one:

1. Resolve the Unknown URI Error

The UnsupportedOperationException: Unknown URI happens because MediaStore.VOLUME_EXTERNAL_PRIMARY might not be fully compatible across all devices. Instead, use a version-agnostic approach to get the correct media collection URI:

Uri collection;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    // For Android 10+, use the standard external volume (more widely compatible)
    collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
} else {
    // For Android 9 and below, use the legacy external content URI
    collection = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
}

2. Ensure You Have the Right Permissions

  • Android 10+: No need for WRITE_EXTERNAL_STORAGE permission when using MediaStore, but you must request CAMERA and RECORD_AUDIO permissions dynamically.
  • Android 9 and below: You need to request WRITE_EXTERNAL_STORAGE (alongside camera/audio permissions) dynamically before saving to public directories.

Your recorded video is stored in the app's private directory (getExternalFilesDir(null)), which the gallery app can't access. You need to copy this file to a public MediaStore directory and properly register it so the gallery detects it.

Here's a complete, tested method to handle this:

private Uri saveVideoToGallery(Context context, File recordedVideoFile) {
    String videoFileName = "video_" + System.currentTimeMillis() + ".mp4";
    ContentValues values = new ContentValues();
    
    // Set target directory (use "DCIM/YourFolder" if you prefer that location)
    values.put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/YourCustomFolder");
    values.put(MediaStore.Video.Media.TITLE, videoFileName);
    values.put(MediaStore.Video.Media.DISPLAY_NAME, videoFileName);
    values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
    values.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
    values.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis());
    
    // Mark file as pending while writing (Android 10+)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        values.put(MediaStore.Video.Media.IS_PENDING, 1);
    }

    ContentResolver resolver = context.getContentResolver();
    Uri collection;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
    } else {
        collection = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
    }

    // Insert new entry into MediaStore
    Uri savedUri = resolver.insert(collection, values);
    if (savedUri == null) {
        Log.e("VideoSave", "Failed to create MediaStore entry");
        return null;
    }

    // Copy recorded video to the MediaStore URI
    try (OutputStream out = resolver.openOutputStream(savedUri);
         InputStream in = new FileInputStream(recordedVideoFile)) {
        
        byte[] buffer = new byte[8192];
        int bytesRead;
        while ((bytesRead = in.read(buffer)) != -1) {
            out.write(buffer, 0, bytesRead);
        }

        // Mark file as ready for system to scan into gallery
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            values.clear();
            values.put(MediaStore.Video.Media.IS_PENDING, 0);
            resolver.update(savedUri, values, null, null);
        }

        // Force media scanner to detect the new file immediately
        MediaScannerConnection.scanFile(
            context,
            new String[]{savedUri.toString()},
            new String[]{"video/mp4"},
            null
        );

        return savedUri;

    } catch (IOException e) {
        // Clean up if copy fails
        resolver.delete(savedUri, null, null);
        e.printStackTrace();
        return null;
    }
}

How to Use This Method

After stopping your MediaRecorder, call this method with the recorded file from your getMediaFile() function:

// After stopping recording
File recordedFile = getMediaFile(context);
Uri galleryUri = saveVideoToGallery(context, recordedFile);

if (galleryUri != null) {
    // Success! galleryUri links directly to your video in the gallery
    // Use this URI to share or access the video later
} else {
    // Handle save failure
}

4. Optional: Record Directly to MediaStore (Avoid Copying)

For better efficiency, configure MediaRecorder to record directly to a MediaStore URI, skipping the private directory copy step:

// Call this before starting your MediaRecorder
private Uri prepareDirectRecordingToGallery(Context context) throws IOException {
    String videoFileName = "video_" + System.currentTimeMillis() + ".mp4";
    ContentValues values = new ContentValues();
    values.put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/YourCustomFolder");
    values.put(MediaStore.Video.Media.TITLE, videoFileName);
    values.put(MediaStore.Video.Media.DISPLAY_NAME, videoFileName);
    values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
    values.put(MediaStore.Video.Media.IS_PENDING, 1);

    ContentResolver resolver = context.getContentResolver();
    Uri collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
    Uri savedUri = resolver.insert(collection, values);
    
    if (savedUri == null) {
        throw new IOException("Failed to create MediaStore entry");
    }

    // Get file descriptor for MediaStore URI and attach to MediaRecorder
    ParcelFileDescriptor pfd = resolver.openFileDescriptor(savedUri, "w");
    mediaRecorder.setOutputFile(pfd.getFileDescriptor());
    
    return savedUri;
}

// After stopping recording:
private void finalizeDirectRecording(Context context, Uri savedUri) {
    try {
        // Mark file as ready
        ContentValues values = new ContentValues();
        values.put(MediaStore.Video.Media.IS_PENDING, 0);
        context.getContentResolver().update(savedUri, values, null, null);
        
        // Trigger media scan
        MediaScannerConnection.scanFile(context, new String[]{savedUri.toString()}, null, null);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

5. Why Uri.fromFile(f) Doesn't Work

On Android 7.0+, Uri.fromFile() throws a FileUriExposedException because file URIs can't be safely shared with other apps. Even if you bypass this, files in your app's private directory are not accessible to the gallery—only MediaStore URIs make files visible to apps like the gallery.

内容的提问来源于stack exchange,提问作者AK. Bab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 09:07:31