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

调用ThumbnailUtils.createVideoThumbnail获取视频Bitmap返回null问题求助

解决调用ThumbnailUtils.createVideoThumbnail返回null Bitmap的问题

你踩的这个坑其实挺常见的——ThumbnailUtils.createVideoThumbnail()的旧版重载方法根本不支持直接传入content://开头的Uri!它原本是为本地文件的绝对路径设计的,而你从图库或相机拿到的恰恰是content类型的Uri,自然返回null了。

下面给你两个可行的解决方案,优先推荐第一种,兼容性拉满:

方案一:用MediaMetadataRetriever获取缩略图(推荐)

这个类是Android官方提供的媒体元数据检索工具,不管是content Uri还是文件Uri都能轻松处理,而且在各个Android版本上兼容性更好,还能灵活指定获取视频某一帧的时间戳。

直接上代码:

private Bitmap getVideoThumbnail(Uri videoUri) {
    MediaMetadataRetriever retriever = new MediaMetadataRetriever();
    Bitmap thumbnail = null;
    try {
        // 传入content Uri和ContentResolver,设置数据源
        retriever.setDataSource(getApplicationContext(), videoUri);
        // 获取视频第一帧作为缩略图,也可以传具体时间戳,比如retriever.getFrameAtTime(1000000)(单位:微秒)
        thumbnail = retriever.getFrameAtTime();
    } catch (Exception e) {
        // 捕获可能的异常,比如视频文件损坏、权限不足等
        e.printStackTrace();
    } finally {
        // 记得释放资源,避免内存泄漏
        retriever.release();
    }
    return thumbnail;
}

在你的onActivityResult里直接调用这个方法就行:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_VIDEO_CAPTURE && resultCode == RESULT_OK) {
        Uri videoUri = data.getData();
        Bitmap thumbnail = getVideoThumbnail(videoUri);
        // 接下来处理你的缩略图即可
    }
}

方案二:先将content Uri转为文件路径(仅适合低版本或特殊权限场景)

如果你一定要用ThumbnailUtils,那得先把content Uri转换成本地文件的绝对路径,但要注意:Android 10(API 29)及以上因为Scoped Storage的限制,这种方法大概率会失效,除非你申请了MANAGE_EXTERNAL_STORAGE权限(但这个权限很难通过Google Play审核)。

转换Uri到路径的代码示例:

private String getRealPathFromUri(Uri uri) {
    String filePath = null;
    // 处理MediaProvider的文档类型Uri
    if (DocumentsContract.isDocumentUri(this, uri)) {
        String docId = DocumentsContract.getDocumentId(uri);
        if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
            // 拆分docId,格式一般是"video:76"这种
            String id = docId.split(":")[1];
            String selection = MediaStore.Video.Media._ID + "=?";
            String[] selectionArgs = {id};
            filePath = getColumnValue(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, selection, selectionArgs);
        }
    } else if ("content".equalsIgnoreCase(uri.getScheme())) {
        // 处理普通的content Uri
        filePath = getColumnValue(uri, null, null);
    }
    return filePath;
}

// 从ContentResolver中查询获取文件路径
private String getColumnValue(Uri uri, String selection, String[] selectionArgs) {
    String column = MediaStore.Video.Media.DATA;
    String[] projection = {column};
    // 使用try-with-resources自动关闭Cursor,避免内存泄漏
    try (Cursor cursor = getContentResolver().query(uri, projection, selection, selectionArgs, null)) {
        if (cursor != null && cursor.moveToFirst()) {
            int columnIndex = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(columnIndex);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

然后在onActivityResult里调用:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_VIDEO_CAPTURE && resultCode == RESULT_OK) {
        Uri videoUri = data.getData();
        String videoPath = getRealPathFromUri(videoUri);
        if (videoPath != null) {
            Bitmap thumbnail = ThumbnailUtils.createVideoThumbnail(videoPath, MediaStore.Video.Thumbnails.MINI_KIND);
            // 处理缩略图
        }
    }
}

最后再提醒一句:别忘了权限问题!从图库选视频需要READ_EXTERNAL_STORAGE(Android 13+是READ_MEDIA_VIDEO),相机拍摄需要CAMERA权限,确保你已经在Manifest里声明并动态申请了这些权限。

内容的提问来源于stack exchange,提问作者guillefix

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:26:33