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

Kotlin生成视频缩略图时出现video must not null空指针异常求助

解决内部存储视频缩略图生成时BitmapFactory.decodeStream返回null的问题

问题详情

  • 设备:实体机/虚拟机,API 29
  • 核心错误:生成视频缩略图时抛出NullPointerException,提示video must not be null
  • 错误栈:
W/System.err: java.lang.NullPointerException: video must not be null
W/System.err:     at com.checkme.newmobile.ui.reports.updateRvAdapter.onBindViewHolder(updateRvAdapter.kt:151)
W/System.err:     at com.checkme.newmobile.ui.reports.updateRvAdapter.onBindViewHolder(updateRvAdapter.kt:24)
W/System.err:     at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7254)
W/System.err:     at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7337)
W/System.err:     at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6194)
W/System.err:     at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6460)
W/System.err:     at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6300)
W/System.err:     at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6296)
W/System.err:     at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2330)
...
  • 视频存储路径:/data/user/0/project_name/files/file_name.mp4(已确认路径正确)
  • 问题代码片段:
try {
    val fileName = phaseMediaList[i].localPath.split("/")
    val f = File(context?.filesDir, fileName[fileName.size-1])
    val video = BitmapFactory.decodeStream(FileInputStream(f))
    if (video != null) {hasVideo = true}
    ...
} catch (e: Throwable) {
    e.printStackTrace()
}
  • 关键问题:BitmapFactory.decodeStream(FileInputStream(f))返回null,导致后续空指针异常

原因分析

BitmapFactory.decodeStream()仅支持解码图片格式的文件,无法直接解析视频流,因此读取视频文件时必然返回null,这是核心错误原因。

解决方案

在API 29及以上版本,推荐使用以下两种标准方式生成视频缩略图:

方式1:MediaMetadataRetriever(灵活度高,支持自定义帧位置)

try {
    val fileName = phaseMediaList[i].localPath.split("/")
    val f = File(context?.filesDir, fileName[fileName.size-1])
    
    // 先校验文件有效性
    if (f.exists() && f.length() > 0) {
        val retriever = MediaMetadataRetriever()
        retriever.setDataSource(f.absolutePath)
        // 获取视频第一帧作为缩略图,也可指定具体时间戳
        val thumbnail = retriever.getFrameAtTime()
        retriever.release() // 必须释放资源
        
        if (thumbnail != null) {
            hasVideo = true
            // 后续将缩略图设置到UI组件,如ImageView
        } else {
            // 无法生成缩略图的降级处理逻辑
        }
    } else {
        // 文件不存在或为空的处理逻辑
    }
} catch (e: Exception) {
    e.printStackTrace()
}

方式2:ThumbnailUtils(代码简洁,适合快速生成)

try {
    val fileName = phaseMediaList[i].localPath.split("/")
    val f = File(context?.filesDir, fileName[fileName.size-1])
    
    if (f.exists() && f.length() > 0) {
        // MINI_KIND:迷你缩略图;MICRO_KIND:微型缩略图;FULL_SCREEN_KIND:全屏缩略图
        val thumbnail = ThumbnailUtils.createVideoThumbnail(
            f.absolutePath,
            MediaStore.Video.Thumbnails.MINI_KIND
        )
        
        if (thumbnail != null) {
            hasVideo = true
            // 处理缩略图逻辑
        } else {
            // 降级处理
        }
    } else {
        // 文件异常处理
    }
} catch (e: Exception) {
    e.printStackTrace()
}

额外注意事项

  1. 文件校验:处理前必须检查文件是否存在且非空,避免因文件损坏、缺失导致异常
  2. 资源释放:使用MediaMetadataRetriever时务必调用release(),防止内存泄漏
  3. 线程优化:缩略图生成属于IO操作,建议放在子线程执行(如Coroutines),避免阻塞主线程

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 22:01:18