分层次的回答突出 技术深度 、架构思维 和 实战优化,从基础实现到高阶优化:
一、核心技术方案(基础回答)
如何实现视频边下边播?
1. **网络请求**:使用 HTTP Range 请求(Header: `Range: bytes=0-1024`)支持断点下载
2. **本地缓存**:通过 RandomAccessFile 实现视频分片写入,确保可随机访问
3. **播放器集成**:ExoPlayer 的 CacheDataSource 优先读取本地缓存,无缝切换在线/离线播放
4. **进度同步**:监听下载进度,实时更新播放器可播放范围
代码示例:
// 关键代码片段
val dataSourceFactory = CacheDataSource.Factory()
.setCache(SimpleCache(cacheDir, LeastRecentlyUsedCacheEvictor(512 * 1024 * 1024)))
.setUpstreamDataSourceFactory(HttpDataSource.Factory())
val player = ExoPlayer.Builder(context)
.setMediaSourceFactory(DefaultMediaSourceFactory(dataSourceFactory))
.build()
player.setMediaItem(MediaItem.fromUri(videoUrl))
二、高阶优化方案(进阶回答)
如何优化大视频文件的边下边播体验?
1. **分块下载**:
- 将文件分成 2MB/块的多个分片
- 多线程并发下载(但需限制线程数,避免 OOM)
2. **智能预加载**:
- 根据网速动态调整预加载窗口(如 5s->15s)
- 优先下载当前播放位置附近的片段
3. **缓存管理**:
- 使用 LRU 策略自动清理旧缓存
- 记录已下载的字节范围,避免重复下载
4. **容错机制**:
- 弱网环境下自动降低下载速度
- 下载失败时指数退避重试(1s, 2s, 4s...)
架构图:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 播放器控制 │ ←→ │ 缓存管理器 │ ←→ │ 分块下载引擎 │
└─────────────┘ └─────────────┘ └─────────────┘
↓ ↓ ↓
┌───────────────────────────────────────────────────┐
│ 本地视频缓存文件 (MP4) │
└───────────────────────────────────────────────────┘
三、实战问题解决(突出经验)
遇到过哪些技术难点?如何解决的?
1. **问题**:播放卡顿
- **解决**:增加预加载缓冲区,根据网速动态调整分块大小(4G/WiFi 用 2MB,弱网用 512KB)
2. **问题**:内存溢出
- **解决**:使用环形缓冲区 + 内存复用(避免频繁创建 byte[])
3. **问题**:断点续传失败
- **解决**:通过 SQLite 记录已下载的字节范围,重启时校验文件完整性
代码示例(预加载策略):
player.addListener(object : Player.Listener {
override fun onPlaybackStateChanged(state: Int) {
if (state == Player.STATE_READY) {
val bufferEnd = player.bufferedPosition
val playPosition = player.currentPosition
if (bufferEnd - playPosition < PRELOAD_THRESHOLD) {
downloader.prioritizeDownload(playPosition, playPosition + 30_000) // 预加载30s
}
}
}
})
四、性能指标(量化成果)
你的方案相比直接播放有什么优势?
1. 首帧加载时间:从 3.2s → 1.1s(降低65%)
2. 卡顿次数:平均每次播放从 4.3次 → 0.7次
3. 流量节省:重复播放相同视频时节省90%流量
4. 内存占用:稳定在 45MB 以内(无 OOM)
五、面试避坑指南
-
必问问题:
-
"Range请求的原理是什么?"
→ 回答 HTTP 206 Partial Content 状态码和 Content-Range 头部
-
-
致命错误:
-
忘记处理视频文件碎片化问题(下载中途退出导致文件损坏)
-
未考虑线程安全问题(多线程写入同一文件)
-
-
加分回答:
-
提到 Android 11 的 Scoped Storage 适配
-
讨论 ExoPlayer 的 ProgressiveMediaSource 与 DashMediaSource 选择
-
六、终极模板
请设计一个抖音的预加载方案
我们的方案分为三个层级:
1. **网络层**:
- 基于 OkHttp 拦截器实现分块下载
- 根据用户历史行为预测下一个视频并静默预下载
2. **缓存层**:
- 两级缓存:内存缓存最近3个视频(LRU),磁盘缓存最多50个视频
- 使用 mmap 加速文件读写
3. **播放层**:
- ExoPlayer 配置多数据源回退策略:内存 → 磁盘 → 网络
- 动态码率切换:根据实时网速选择 480P/720P/1080P
技术指标:
- 用户滑动到新视频时,90%概率已缓存完成
- 弱网环境下卡顿率低于5%