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

Android后台视频缓存实现咨询:频道视频未播放预缓存方案

Hey there! 针对你在开发Android视频应用时遇到的「选中频道后批量缓存未播放视频」的需求,我整理了几个实用的实现方案和关键细节,帮你搞定这个功能:

核心思路梳理

首先明确我们要完成的核心流程:

  • 拿到目标频道下所有视频的源地址
  • 批量发起缓存请求,将视频资源写入本地缓存目录
  • 实时管理缓存状态(进度、成功/失败、空间占用)
  • 后续播放时优先读取本地缓存,无缓存再请求网络
具体实现方案

方案一:基于ExoPlayer的缓存扩展(推荐)

如果你的应用已经在用ExoPlayer播放视频,那这个方案最省心——ExoPlayer自带的缓存机制默认只处理已播放内容,但我们可以用它提供的CacheWriter类直接缓存未播放的视频,无需实际播放。

步骤说明

  1. 初始化全局缓存实例:先配置好缓存目录、大小限制和淘汰策略(比如用最近最少使用算法清理旧缓存)
  2. 单视频缓存逻辑:针对每个视频地址,创建CacheWriter并执行缓存任务
  3. 批量缓存频道视频:遍历频道下的所有视频地址,逐个发起缓存请求

代码示例(Kotlin)

// 全局单例的SimpleCache(建议放在Application里初始化)
private fun initVideoCache(context: Context): SimpleCache {
    val cacheDir = context.getExternalFilesDir(Environment.DIRECTORY_MOVIES) ?: context.filesDir
    val maxCacheSize = 100 * 1024 * 1024 // 100MB缓存上限
    val cacheEvictor = LeastRecentlyUsedCacheEvictor(maxCacheSize.toLong())
    return SimpleCache(cacheDir, cacheEvictor)
}

// 缓存单个视频的方法
fun cacheSingleVideo(cache: SimpleCache, videoUrl: String) {
    val httpDataSourceFactory = DefaultHttpDataSource.Factory()
    val cacheDataSourceFactory = CacheDataSource.Factory()
        .setCache(cache)
        .setUpstreamDataSourceFactory(httpDataSourceFactory)
        .setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)

    val mediaItem = MediaItem.fromUri(videoUrl)
    val cacheWriter = CacheWriter(
        cacheDataSourceFactory.createDataSource(),
        mediaItem.localConfiguration!!.uri,
        null,
        object : CacheWriter.ProgressListener {
            override fun onProgress(
                requestLength: Long,
                bytesCached: Long,
                newBytesCached: Long
            ) {
                // 实时更新缓存进度,可回调到UI层展示
                val progress = (bytesCached * 100 / requestLength).toInt()
                Log.d("VideoCache", "缓存进度:$progress%")
            }
        }
    )

    // 开启后台线程执行缓存,避免阻塞主线程
    Executors.newSingleThreadExecutor().execute {
        try {
            cacheWriter.cache()
            Log.d("VideoCache", "视频缓存完成:$videoUrl")
        } catch (e: Exception) {
            Log.e("VideoCache", "缓存失败:${e.message}")
        }
    }
}

// 批量缓存频道下的所有视频
fun cacheChannelVideos(cache: SimpleCache, videoUrls: List<String>) {
    videoUrls.forEach { url ->
        cacheSingleVideo(cache, url)
    }
}

关键注意点

  • 用WorkManager替代普通线程:如果需要应用退到后台仍能继续缓存,或者在WiFi/充电状态下自动执行,WorkManager是更好的选择,它能适配Android的后台限制
  • 网络状态判断:缓存前先检查网络类型,默认只在WiFi下执行(可让用户手动开启移动流量缓存)
  • 缓存状态持久化:用Room数据库记录每个视频的缓存状态(未开始/缓存中/已完成/失败),方便UI展示

方案二:自定义下载管理器(灵活度更高)

如果不想依赖ExoPlayer,也可以自己实现下载逻辑,用OkHttp或Retrofit发起请求,将视频流写入本地文件。

核心要点

  • 支持断点续传:通过Range请求头实现,避免网络中断后重新下载整个文件
  • 任务队列管理:控制同时下载的任务数量,避免网络拥堵
  • 文件命名规则:用视频ID作为文件名,避免重复或冲突

代码示例(OkHttp实现断点续传)

fun downloadVideo(videoUrl: String, saveDir: File, videoId: String) {
    val saveFile = File(saveDir, "$videoId.mp4")
    val downloadedBytes = saveFile.length()

    val client = OkHttpClient.Builder()
        .connectTimeout(10, TimeUnit.SECONDS)
        .build()

    val request = Request.Builder()
        .url(videoUrl)
        .addHeader("Range", "bytes=$downloadedBytes-") // 断点续传请求头
        .build()

    client.newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            Log.e("VideoDownload", "下载失败:${e.message}")
        }

        override fun onResponse(call: Call, response: Response) {
            if (!response.isSuccessful) {
                Log.e("VideoDownload", "服务器错误:${response.code}")
                return
            }

            val body = response.body ?: return
            // 以追加模式写入文件
            val outputStream = RandomAccessFile(saveFile, "rw").apply {
                seek(downloadedBytes)
            }.outputStream()

            try {
                body.byteStream().use { inputStream ->
                    inputStream.copyTo(outputStream)
                }
                Log.d("VideoDownload", "下载完成:$videoId")
            } catch (e: IOException) {
                Log.e("VideoDownload", "写入文件失败:${e.message}")
            }
        }
    })
}
优化与细节处理
  • 缓存清理策略:提供手动清理缓存的功能,或者自动清理超过7天未播放的缓存文件;也可以根据剩余存储空间自动调整缓存上限
  • 权限适配:Android 10+推荐使用应用私有目录存储缓存(无需额外权限),如果要存到公共目录,需要申请MANAGE_EXTERNAL_STORAGE权限
  • 缓存有效性检测:定期对比视频源的ETag或最后修改时间,若资源更新则重新缓存
  • 播放优先级:播放视频时,先检查本地缓存文件是否存在,存在则直接播放本地文件,否则请求网络

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:25:30