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应用启动时,可以预解析几个核心域名。比如首页推荐、用户中心这些接口的域名,提前把IP解析好,能省下几百毫秒的加载时间。

连接池复用

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上不要使用硬件位图(Hardware Bitmap)。我在小米电视上踩过这个坑,硬件位图在SurfaceView切换时会导致闪屏。用RGB_565格式虽然颜色精度差一点,但内存占用直接减半。

磁盘缓存策略

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秒。用户感知到的就是——按一下遥控器,图片马上就出来了。

知识体系总览

下面这张图,我把本章的核心知识点串起来了:

TV端网络与缓存优化知识体系 OkHttp 网络层 Glide 图片加载 超时配置:connect 15s / read 20s DNS缓存 + 预解析核心域名 连接池复用:5-10个连接 尺寸裁剪:override(1920,1080) RGB_565格式 + 禁用硬件位图 磁盘缓存250MB + 预加载 核心目标:省内存、快响应、稳运行

避坑指南

最后,分享几个我实际踩过的坑:

  • 不要用ARGB_8888——TV上图片用RGB_565就够了,内存省一半,肉眼几乎看不出区别。
  • 小心DiskCache的路径——有些TV系统会把/data分区设得很小,缓存路径最好用getExternalCacheDir()。
  • OkHttp的拦截器别乱加——每个拦截器都会增加延迟。我见过有人加了5个拦截器,一个请求多花200ms。
  • Glide的skipMemoryCache(false)要慎用——TV内存金贵,除非是用户正在看的图片,否则尽量走磁盘缓存。

总结一下:TV端的网络和图片优化,说白了就是跟有限的硬件资源做斗争。OkHttp要调超时、做预解析;Glide要控尺寸、改格式、加大缓存。这两套组合拳打好了,TV应用的流畅度能上一个台阶。

好了,这一章的内容就到这里。代码里的配置参数,大家可以根据自己项目的实际情况微调。记住一个原则——在TV上,稳定比炫酷更重要。

公众号:蓝海资料掘金营,微信deep3321