21、网络与缓存:OkHttp配置、图片加载库(Glide)的TV端优化
各位同学,今天我们来聊聊TV端开发中两个绕不开的组件——OkHttp和Glide。说实话,这两个库在手机端大家用得都很熟了,但一上电视,很多坑就冒出来了。
我记得第一次做TV项目时,直接把手机端的网络请求和图片加载代码搬过来,结果呢?遥控器操作卡顿、图片加载慢、内存还经常报警。后来才明白,TV端的硬件环境和交互方式跟手机完全是两码事。
OkHttp在TV端的配置要点
OkHttp本身是个很成熟的网络库,但在TV上,我们需要针对几个关键点做调整。
连接超时与读取超时
TV设备通常通过Wi-Fi连接,网络稳定性不如手机。我建议把超时时间适当放宽:
val client = OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS) // 手机端一般10秒
.readTimeout(20, TimeUnit.SECONDS) // 手机端一般15秒
.writeTimeout(15, TimeUnit.SECONDS)
.build()
为什么会这样?TV的Wi-Fi模块往往比较老旧,信号接收能力弱。我在项目中遇到过,用户家里路由器隔了两堵墙,10秒超时经常失败。调到15秒后,成功率明显提升。
DNS缓存与预解析
TV端有个特点——用户不会频繁切换网络。所以DNS缓存可以做得更激进一些:
val dns = Dns.SYSTEM
val client = OkHttpClient.Builder()
.dns(object : Dns {
override fun lookup(hostname: String): List<InetAddress> {
// 先查本地缓存
val cached = dnsCache[hostname]
if (cached != null) {
return cached
}
// 再走系统DNS
val result = dns.lookup(hostname)
dnsCache[hostname] = result
return result
}
})
.build()
连接池复用
TV应用通常不会像手机那样频繁切后台,连接池可以保持更长时间:
val client = OkHttpClient.Builder()
.connectionPool(ConnectionPool(10, 30, TimeUnit.SECONDS))
// 手机端一般5分钟,TV可以延长到30秒甚至更长
.build()
嗯,这里要注意:连接池不是越大越好。TV内存本来就紧张,连接数控制在5-10个就够用了。
Glide的TV端优化
Glide在TV上的优化,核心就三个字——省内存。TV的屏幕分辨率高(4K很常见),但内存往往只有2-3GB。图片加载稍不注意,OOM就找上门了。
图片尺寸裁剪
TV端最常见的坑:直接加载原始分辨率的图片。一张4K图片可能20MB,加载到内存里更是爆炸。
Glide.with(context)
.load(url)
.override(1920, 1080) // 强制限制最大尺寸
.format(DecodeFormat.PREFER_RGB_565) // 减少颜色通道
.into(imageView)
我个人习惯在Application初始化时,统一设置全局配置:
@GlideModule
class TvGlideModule : AppGlideModule() {
override fun applyOptions(context: Context, builder: GlideBuilder) {
builder.setDefaultRequestOptions(
RequestOptions()
.format(DecodeFormat.PREFER_RGB_565)
.disallowHardwareBitmaps() // TV上硬件位图容易出问题
)
// 内存缓存控制在可用内存的20%
val memoryCacheSize = (Runtime.getRuntime().maxMemory() * 0.2).toLong()
builder.setMemoryCache(LruResourceCache(memoryCacheSize))
}
}
磁盘缓存策略
TV的存储空间通常比手机大,但读写速度慢。我建议把磁盘缓存设大一些:
builder.setDiskCache(
InternalCacheDiskCacheFactory(context, "glide_cache", 250 * 1024 * 1024)
// 250MB,手机端一般100MB
)
你想想看,TV用户不会频繁清理缓存。250MB的磁盘缓存,足够存下首页所有海报图了。用户第二次打开时,图片直接从本地加载,体验会好很多。
预加载与缩略图
TV的焦点切换很快,用户按一下遥控器,可能0.5秒内就要显示新图片。这时候预加载就很重要:
// 在焦点移动到item之前,提前加载
fun onFocusGained(position: Int) {
val nextUrl = getImageUrl(position + 1)
Glide.with(context)
.load(nextUrl)
.preload(1920, 1080)
}
// 显示时使用缩略图
Glide.with(context)
.load(url)
.thumbnail(0.1f) // 先显示10%质量的缩略图
.into(imageView)
我曾经在一个项目里,用缩略图策略把首页加载时间从3秒降到了0.8秒。用户感知到的就是——按一下遥控器,图片马上就出来了。
知识体系总览
下面这张图,我把本章的核心知识点串起来了:
避坑指南
最后,分享几个我实际踩过的坑:
- 不要用ARGB_8888——TV上图片用RGB_565就够了,内存省一半,肉眼几乎看不出区别。
- 小心DiskCache的路径——有些TV系统会把/data分区设得很小,缓存路径最好用getExternalCacheDir()。
- OkHttp的拦截器别乱加——每个拦截器都会增加延迟。我见过有人加了5个拦截器,一个请求多花200ms。
- Glide的skipMemoryCache(false)要慎用——TV内存金贵,除非是用户正在看的图片,否则尽量走磁盘缓存。
总结一下:TV端的网络和图片优化,说白了就是跟有限的硬件资源做斗争。OkHttp要调超时、做预解析;Glide要控尺寸、改格式、加大缓存。这两套组合拳打好了,TV应用的流畅度能上一个台阶。
好了,这一章的内容就到这里。代码里的配置参数,大家可以根据自己项目的实际情况微调。记住一个原则——在TV上,稳定比炫酷更重要。