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() }
额外注意事项
- 文件校验:处理前必须检查文件是否存在且非空,避免因文件损坏、缺失导致异常
- 资源释放:使用
MediaMetadataRetriever时务必调用release(),防止内存泄漏 - 线程优化:缩略图生成属于IO操作,建议放在子线程执行(如Coroutines),避免阻塞主线程
内容的提问来源于stack exchange,提问作者mixx
相关产品推荐
相关产品推荐

