Skip to content

网络缓存:Android HTTP 缓存完整指南

目录

  1. 网络缓存概述
  2. HTTP 缓存策略
  3. OkHttp 缓存实现
  4. 多级缓存架构
  5. 图片缓存
  6. 数据库缓存
  7. 最佳实践
  8. 面试考点

1. 网络缓存概述

1.1 为什么要使用缓存

缓存的核心价值:
- 减少网络请求次数
- 降低服务器压力
- 提高响应速度
- 节省用户流量
- 支持离线访问

1.2 缓存层级

缓存层级结构:
┌─────────────────────────────────────┐
│          应用层缓存                │  ← 数据库、文件、内存
├─────────────────────────────────────┤
│          网络层缓存                │  ← OkHttp Cache
├─────────────────────────────────────┤
│          HTTP 协议缓存              │  ← 响应头控制
├─────────────────────────────────────┤
│          CDN 缓存                   │  ← 内容分发网络
└─────────────────────────────────────┘

1.3 缓存策略对比

策略优点缺点适用场景
无缓存数据最新每次都请求实时性要求高
强制缓存速度快,无网络请求可能数据过时静态资源
协商缓存平衡新旧需要服务器交互动态内容
多级缓存性能最优实现复杂大部分场景

2. HTTP 缓存策略

2.1 强制缓存(强缓存)

强制缓存通过响应头控制,完全跳过服务器请求:

Cache-Control: max-age=3600
Expires: Wed, 21 Oct 2024 07:28:00 GMT

Cache-Control 指令

kotlin
class CacheControlExample {
    // max-age: 资源缓存的秒数
    "Cache-Control: max-age=3600"  // 缓存 1 小时
    
    // s-maxage: 代理服务器的缓存时间
    "Cache-Control: s-maxage=7200"  // 代理缓存 2 小时
    
    // private: 只能被浏览器缓存,代理不能缓存
    "Cache-Control: private"
    
    // public: 任何地方都可以缓存
    "Cache-Control: public"
    
    // no-cache: 必须先验证才能使用缓存
    "Cache-Control: no-cache"
    
    // no-store: 完全不缓存
    "Cache-Control: no-store"
    
    // must-revalidate: 过期后必须验证
    "Cache-Control: must-revalidate"
    
    // immutable: 资源不会改变
    "Cache-Control: immutable"
}

Expires 响应头

Expires: Wed, 21 Oct 2024 07:28:00 GMT

说明:
- 指定资源过期的具体时间点
- 过时技术,优先级低于 Cache-Control
- 依赖客户端时间,可能不准确

2.2 协商缓存(弱缓存)

协商缓存通过验证缓存是否有效:

Etag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Tue, 10 Oct 2015 07:28:00 GMT

请求头:
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
If-Modified-Since: Tue, 10 Oct 2015 07:28:00 GMT

ETag 验证

kotlin
// 服务器返回 ETag
Response:
    ETag: "abc123"
    Cache-Control: max-age=0, must-revalidate

// 客户端再次请求携带 If-None-Match
Request:
    If-None-Match: "abc123"

// 服务器比较 ETag
// 如果相同返回 304 Not Modified
// 如果不同返回 200 和完整资源

Last-Modified 验证

kotlin
// 服务器返回最后修改时间
Response:
    Last-Modified: Wed, 21 Oct 2024 07:28:00 GMT
    Cache-Control: max-age=0

// 客户端再次请求
Request:
    If-Modified-Since: Wed, 21 Oct 2024 07:28:00 GMT

// 服务器比较时间
// 如果未修改返回 304
// 如果已修改返回 200

2.3 缓存优先级

缓存验证优先级:
1. Cache-Control (最高)
2. ETag
3. Last-Modified
4. Expires (最低)

304 响应返回情况:
- ETag 匹配 → 304
- Last-Modified 未变化 → 304
- 其他 → 200

2.4 HTTP 缓存流程

请求流程:
┌──────────────┐
│  发起请求    │
└──────┬───────┘

       v
┌──────────────┐
│  检查缓存    │
└──────┬───────┘

       ├─────────────────────┐
       │                     │
       v                     v
缓存有效?                缓存无效?
       │                     │
       v                     v
┌──────────────┐     ┌──────────────┐
│  使用缓存    │     │  请求服务器  │
└──────────────┘     └──────┬───────┘

                            v
                       ┌──────────────┐
                       │  更新缓存    │
                       └──────────────┘

3. OkHttp 缓存实现

3.1 基础配置

kotlin
class OkHttpCacheManager(context: Context) {
    // 创建缓存目录
    private val cacheDir = File(context.cacheDir, "http_cache")
    
    // 创建缓存(100MB)
    private val cache = Cache(cacheDir, 100 * 1024 * 1024)
    
    // 配置 OkHttpClient
    private val client = OkHttpClient.Builder()
        .cache(cache)
        .connectTimeout(15, TimeUnit.SECONDS)
        .readTimeout(15, TimeUnit.SECONDS)
        .writeTimeout(15, TimeUnit.SECONDS)
        .build()
    
    // 获取缓存统计
    fun getCacheStats(): CacheStats {
        return cache.stats()
    }
    
    // 清理缓存
    fun clearCache() {
        cache.evictAll()
    }
    
    // 关闭缓存
    fun closeCache() {
        cache.close()
    }
}

3.2 配置缓存策略

kotlin
class CacheStrategyExample {
    private val client = OkHttpClient.Builder()
        .cache(Cache(cacheDir, 100 * 1024 * 1024))
        .build()
    
    // 1. 强制使用缓存(离线模式)
    fun forceCacheRequest() {
        val request = Request.Builder()
            .url("https://example.com/data")
            .cacheControl(CacheControl.FORCE_CACHE)
            .build()
        
        client.newCall(request).execute()
    }
    
    // 2. 不使用缓存(强制刷新)
    fun noCacheRequest() {
        val request = Request.Builder()
            .url("https://example.com/data")
            .cacheControl(CacheControl.FORCE_NETWORK)
            .build()
        
        client.newCall(request).execute()
    }
    
    // 3. 先尝试缓存,失败则使用网络
    fun cacheFirstRequest() {
        val cacheControl = CacheControl.Builder()
            .maxStale(30, TimeUnit.DAYS)  // 允许使用过期 30 天的缓存
            .build()
        
        val request = Request.Builder()
            .url("https://example.com/data")
            .cacheControl(cacheControl)
            .build()
        
        client.newCall(request).execute()
    }
    
    // 4. 先使用网络,失败则使用缓存
    fun networkFirstRequest() {
        val request = Request.Builder()
            .url("https://example.com/data")
            .cacheControl(CacheControl.Builder()
                .maxAge(0)  // 不使用强缓存
                .build())
            .build()
        
        client.newCall(request).execute()
    }
}

3.3 自定义缓存策略

kotlin
class CustomCacheInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val response = chain.proceed(request)
        
        // 根据响应类型设置缓存策略
        return when (response.code) {
            in 200..299 -> {
                // 成功响应,根据资源类型设置缓存
                val url = request.url.toString()
                
                if (url.endsWith(".css") || url.endsWith(".js") || 
                    url.endsWith(".png") || url.endsWith(".jpg")) {
                    // 静态资源缓存 7 天
                    response.toBuilder()
                        .header("Cache-Control", "max-age=604800")
                        .build()
                } else if (url.contains("api")) {
                    // API 响应不缓存或使用协商缓存
                    response.toBuilder()
                        .header("Cache-Control", "no-cache")
                        .build()
                } else {
                    response
                }
            }
            304 -> {
                // 协商缓存命中
                response
            }
            else -> {
                // 其他状态码不缓存
                response.toBuilder()
                    .header("Cache-Control", "no-store")
                    .build()
            }
        }
    }
}

// 使用自定义拦截器
val client = OkHttpClient.Builder()
    .addNetworkInterceptor(CustomCacheInterceptor())
    .cache(Cache(cacheDir, 100 * 1024 * 1024))
    .build()

3.4 拦截器实现缓存逻辑

kotlin
class OfflineFirstInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        
        // 检查网络连接
        val isOnline = isConnected()
        
        // 离线模式:强制使用缓存
        if (!isOnline) {
            val offlineRequest = request.newBuilder()
                .cacheControl(CacheControl.FORCE_CACHE)
                .build()
            
            return chain.proceed(offlineRequest)
        }
        
        // 在线模式:先网络后缓存
        val onlineRequest = request.newBuilder()
            .cacheControl(CacheControl.Builder()
                .maxStale(Integer.MAX_VALUE)  // 允许过期缓存
                .build())
            .build()
        
        return chain.proceed(onlineRequest)
    }
    
    private fun isConnected(): Boolean {
        val connectivityManager = 
            ContextCompat.getSystemService(context, ConnectivityManager::class.java)
        val network = connectivityManager?.activeNetwork
        return network != null
    }
}

3.5 响应缓存控制

kotlin
class ResponseCacheControlInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val response = chain.proceed(chain.request())
        
        return response.newBuilder()
            .removeHeader("Pragma")
            .removeHeader("Expires")
            .removeHeader("Cache-Control")
            .header("Cache-Control", getCacheControl(response))
            .build()
    }
    
    private fun getCacheControl(response: Response): String {
        val url = response.request.url.toString()
        
        return when {
            url.endsWith(Regex("\\.(css|js|png|jpg|jpeg|gif|svg|woff2?)$")) -> {
                "public, max-age=31536000, immutable"  // 1 年
            }
            url.contains("/api/v1/") -> {
                "private, max-age=300"  // 5 分钟
            }
            url.contains("/api/v2/") -> {
                "private, max-age=60"  // 1 分钟
            }
            else -> {
                "no-cache"
            }
        }
    }
}

4. 多级缓存架构

4.1 三级缓存设计

kotlin
class MultiLevelCache {
    // 一级缓存:内存(最快)
    private val memoryCache = object : LruCache<String, Any>(10 * 1024 * 1024) {
        override fun sizeOf(key: String, value: Any): Int {
            return value.toString().length
        }
    }
    
    // 二级缓存:磁盘(OkHttp Cache)
    private val diskCache: Cache
    
    // 三级缓存:数据库(持久化)
    private val dbCache = SQLiteOpenHelper(...)
    
    init {
        diskCache = Cache(File(context.cacheDir, "http_cache"), 50 * 1024 * 1024)
    }
    
    // 读取数据(从快到慢)
    suspend fun getData(key: String): Data? {
        // 1. 检查内存缓存
        var data = memoryCache.get(key) as? Data
        if (data != null) {
            Log.d("Cache", "Hit memory cache: $key")
            return data
        }
        
        // 2. 检查磁盘缓存
        data = readFromDiskCache(key)
        if (data != null) {
            Log.d("Cache", "Hit disk cache: $key")
            memoryCache.put(key, data)  // 回填内存
            return data
        }
        
        // 3. 检查数据库缓存
        data = readFromDbCache(key)
        if (data != null) {
            Log.d("Cache", "Hit db cache: $key")
            memoryCache.put(key, data)
            writeToDiskCache(key, data)
            return data
        }
        
        // 4. 网络请求
        data = fetchFromNetwork(key)
        if (data != null) {
            writeToAllCaches(key, data)
        }
        
        return data
    }
    
    private fun writeToAllCaches(key: String, data: Data) {
        memoryCache.put(key, data)
        writeToDiskCache(key, data)
        writeToDbCache(key, data)
    }
    
    // 写入数据(同步到所有缓存)
    suspend fun putData(key: String, data: Data) {
        writeToAllCaches(key, data)
    }
    
    // 清除缓存
    fun clearCache(level: CacheLevel) {
        when (level) {
            CacheLevel.MEMORY -> memoryCache.evictAll()
            CacheLevel.DISK -> diskCache.evictAll()
            CacheLevel.DB -> clearDbCache()
            CacheLevel.ALL -> {
                memoryCache.evictAll()
                diskCache.evictAll()
                clearDbCache()
            }
        }
    }
    
    enum class CacheLevel {
        MEMORY, DISK, DB, ALL
    }
}

4.2 缓存管理器

kotlin
class CacheManager(context: Context) {
    private val memoryCache = MemoryCache()
    private val diskCache = DiskCache(context)
    private val dbCache = DatabaseCache(context)
    
    // 配置
    data class CacheConfig(
        val memorySize: Long = 10 * 1024 * 1024,  // 10MB
        val diskSize: Long = 50 * 1024 * 1024,    // 50MB
        val dbEnabled: Boolean = true,
        val ttl: Long = 24 * 60 * 60 * 1000       // 24 小时
    )
    
    // 获取数据
    suspend fun <T> get(key: String, loader: suspend () -> T?): T? {
        // 1. 内存缓存
        var data = memoryCache.get<T>(key)
        if (data != null && !isExpired(key)) {
            return data
        }
        
        // 2. 磁盘缓存
        data = diskCache.get<T>(key)
        if (data != null && !isExpired(key)) {
            memoryCache.put(key, data)
            return data
        }
        
        // 3. 数据库缓存
        data = dbCache.get<T>(key)
        if (data != null && !isExpired(key)) {
            memoryCache.put(key, data)
            diskCache.put(key, data)
            return data
        }
        
        // 4. 网络加载
        data = loader()
        if (data != null) {
            saveToAllCaches(key, data)
        }
        
        return data
    }
    
    private fun <T> saveToAllCaches(key: String, data: T) {
        memoryCache.put(key, data)
        diskCache.put(key, data)
        if (config.dbEnabled) {
            dbCache.put(key, data)
        }
    }
    
    private fun isExpired(key: String): Boolean {
        val timestamp = getTimestamp(key)
        return System.currentTimeMillis() - timestamp > config.ttl
    }
}

4.3 缓存失效策略

kotlin
class CacheInvalidationStrategy {
    // 1. TTL(Time To Live)
    private val expirationMap = ConcurrentHashMap<String, Long>()
    
    fun setExpiration(key: String, ttl: Long) {
        expirationMap[key] = System.currentTimeMillis() + ttl
    }
    
    fun isExpired(key: String): Boolean {
        return expirationMap[key] ?: 0 < System.currentTimeMillis()
    }
    
    // 2. LRU(Least Recently Used)
    private val lruCache = object : LruCache<String, Any>(100) {
        override fun sizeOf(key: String, value: Any): Int = 1
    }
    
    // 3. LFU(Least Frequently Used)
    private val frequencyMap = ConcurrentHashMap<String, AtomicInteger>()
    
    fun access(key: String) {
        frequencyMap.computeIfAbsent(key) { AtomicInteger(0) }
            .incrementAndGet()
    }
    
    fun getLeastFrequent(): String? {
        return frequencyMap.minByOrNull { it.value }?.key
    }
    
    // 4. 手动失效
    fun invalidate(key: String) {
        memoryCache.remove(key)
        diskCache.remove(key)
        dbCache.remove(key)
    }
    
    // 5. 批量失效
    fun invalidateAll(prefix: String) {
        keys.filter { it.startsWith(prefix) }.forEach {
            invalidate(it)
        }
    }
}

5. 图片缓存

5.1 Glide 缓存

kotlin
class GlideCacheManager {
    // 配置 Glide
    class GlideModule : AppGlideModule() {
        override fun applyOptions(context: Context, builder: GlideBuilder) {
            builder.setMemoryCache(MemoryCache())
            builder.setDiskCache(DiskCacheFactory())
        }
        
        override fun isManifestPlaceholder(): Boolean = false
    }
    
    // 加载图片(自动缓存)
    fun loadImage(context: Context, url: String, imageView: ImageView) {
        Glide.with(context)
            .load(url)
            .centerCrop()
            .diskCacheStrategy(DiskCacheStrategy.ALL)  // 缓存原始和转换后
            .memoryCacheStrategy(MemoryCacheStrategy.ALL)
            .into(imageView)
    }
    
    // 配置缓存大小
    fun configureCacheSize(context: Context) {
        val glide = Glide.with(context)
        
        // 内存缓存:1/6 的可用内存
        val maxMemory = Runtime.getRuntime().maxMemory() / 1024
        val memoryCacheSize = maxMemory / 6
        
        glide.memoryCache = LruResourceCache(memoryCacheSize)
    }
    
    // 清理缓存
    fun clearCache(context: Context) {
        Glide.with(context).apply {
            clearMemory()
            
            // 清理磁盘缓存
            context.externalCacheDir?.listFiles { 
                it.name.startsWith("glide") 
            }?.forEach {
                it.deleteRecursively()
            }
        }
    }
    
    // 获取缓存统计
    fun getCacheStats(context: Context): CacheStats {
        val glide = Glide.with(context)
        return CacheStats(
            memoryCache.size.toLong(),
            memoryCache.size.toLong() * 100,  // 估算大小
            diskCache.size.toLong(),
            diskCache.size.toLong() * 100
        )
    }
}

5.2 Picasso 缓存

kotlin
class PicassoCacheManager {
    // 配置 Picasso
    fun configurePicasso(context: Context) {
        val picasso = Picasso.Builder(context)
            .memoryCache(MemoryCache())
            .diskCache(DiskLruCache(context, 100 * 1024 * 1024))  // 100MB
            .build()
        
        Picasso.setSingletonInstance(picasso)
    }
    
    // 加载图片
    fun loadImage(context: Context, url: String, imageView: ImageView) {
        Picasso.get()
            .load(url)
            .memoryPolicy(MemoryPolicy.NO_STORE, MemoryPolicy.CACHE)
            .into(imageView)
    }
    
    // 清理缓存
    fun clearCache(context: Context) {
        Picasso.get().lruCache().clear()
        Picasso.get().diskLruCache().delete()
    }
}

5.3 Coil 缓存(Kotlin 优先)

kotlin
class CoilCacheManager {
    // 配置 Coil
    fun configureCoil(context: Context) {
        ImageLoader.Builder(context)
            .memoryCache {
                MemoryCache.Builder(context)
                    .maxSizePercent(0.25)  // 25% 可用内存
                    .build()
            }
            .diskCache {
                DiskCache.Builder(context)
                    .maxSizeBytes(100 * 1024 * 1024)  // 100MB
                    .build()
            }
            .build()
    }
    
    // Compose 中加载
    @Composable
    fun ImageLoader(
        data: String,
        contentDescription: String? = null
    ) {
        Image(
            painter = rememberAsyncImagePainter(data),
            contentDescription = contentDescription,
            modifier = Modifier.fillMaxSize(),
            contentScale = ContentScale.Crop
        )
    }
    
    // 清理缓存
    fun clearCache(context: Context) {
        ImageLoader(context).memoryCache.clear()
        ImageLoader(context).diskCache.clear()
    }
}

6. 数据库缓存

6.1 Room 缓存

kotlin
class RoomCacheManager(context: Context) {
    private val database = Room.databaseBuilder(
        context,
        AppDatabase::class.java,
        "cache_database"
    )
    .build()
    
    // 定义缓存 DAO
    @Dao
    interface CacheDao {
        @Query("SELECT * FROM cached_data WHERE key = :key")
        suspend fun getData(key: String): CachedData?
        
        @Insert(onConflict = OnConflictStrategy.REPLACE)
        suspend fun saveData(data: CachedData)
        
        @Query("DELETE FROM cached_data WHERE key = :key")
        suspend fun deleteData(key: String)
        
        @Query("SELECT * FROM cached_data WHERE expire_time < :currentTime")
        suspend fun getExpiredData(currentTime: Long): List<CachedData>
    }
    
    // 缓存实体
    @Entity(tableName = "cached_data")
    data class CachedData(
        @PrimaryKey val key: String,
        val value: String,
        val type: String,
        val expireTime: Long,
        val createdAt: Long = System.currentTimeMillis()
    )
    
    // 保存缓存
    suspend fun saveCache(key: String, data: Any, ttl: Long = 24 * 60 * 60 * 1000) {
        val cachedData = CachedData(
            key = key,
            value = Gson().toJson(data),
            type = data::class.java.simpleName,
            expireTime = System.currentTimeMillis() + ttl
        )
        database.cacheDao().saveData(cachedData)
    }
    
    // 读取缓存
    suspend fun <T> getCache(key: String, type: Class<T>): T? {
        val cached = database.cacheDao().getData(key)
        return if (cached != null && cached.expireTime > System.currentTimeMillis()) {
            Gson().fromJson(cached.value, type)
        } else {
            // 清理过期缓存
            database.cacheDao().deleteData(key)
            null
        }
    }
    
    // 清理过期缓存
    suspend fun clearExpiredCache() {
        val expired = database.cacheDao().getExpiredData(System.currentTimeMillis())
        expired.forEach {
            database.cacheDao().deleteData(it.key)
        }
    }
}

6.2 网络 + 数据库缓存策略

kotlin
class NetworkWithDbCache(private val api: ApiService, private val db: RoomDatabase) {
    
    // 优先数据库,失败则网络
    suspend fun <T> getDataWithCache(
        key: String,
        networkCall: suspend () -> T,
        ttl: Long = 24 * 60 * 60 * 1000
    ): T? {
        // 1. 尝试从数据库读取
        var data = db.getCache<T>(key, networkCall::class.java)
        
        if (data != null) {
            Log.d("Cache", "Hit database cache: $key")
            return data
        }
        
        // 2. 网络请求
        try {
            data = networkCall()
            if (data != null) {
                db.saveCache(key, data, ttl)
            }
        } catch (e: Exception) {
            Log.e("Cache", "Network failed, returning null", e)
        }
        
        return data
    }
    
    // 先网络,失败则数据库
    suspend fun <T> getNetworkFirst(
        key: String,
        networkCall: suspend () -> T,
        useCacheOnError: Boolean = true
    ): T? {
        try {
            val data = networkCall()
            if (data != null) {
                db.saveCache(key, data)
            }
            return data
        } catch (e: Exception) {
            if (useCacheOnError) {
                return db.getCache<T>(key, networkCall::class.java)
            }
            throw e
        }
    }
    
    // 刷新缓存
    suspend fun <T> refreshCache(
        key: String,
        networkCall: suspend () -> T
    ): T? {
        val data = networkCall()
        if (data != null) {
            db.saveCache(key, data)
        }
        return data
    }
}

7. 最佳实践

7.1 架构设计

kotlin
// Repository 模式
class DataRepository(
    private val apiService: ApiService,
    private val cacheManager: CacheManager
) {
    suspend fun getData(key: String): Data? {
        return cacheManager.get(key) {
            apiService.fetch(key)
        }
    }
}

// UseCase 模式
class GetDataUseCase(
    private val repository: DataRepository
) {
    suspend operator fun invoke(key: String): Data? {
        return repository.getData(key)
    }
}

7.2 监控和统计

kotlin
class CacheMonitor {
    private val cacheHitCounter = AtomicInteger(0)
    private val cacheMissCounter = AtomicInteger(0)
    
    fun recordHit() {
        cacheHitCounter.incrementAndGet()
    }
    
    fun recordMiss() {
        cacheMissCounter.incrementAndGet()
    }
    
    fun getHitRate(): Double {
        val total = cacheHitCounter.get() + cacheMissCounter.get()
        return if (total > 0) {
            cacheHitCounter.get().toDouble() / total * 100
        } else {
            0.0
        }
    }
    
    fun getStats(): CacheStats {
        return CacheStats(
            hits = cacheHitCounter.get(),
            misses = cacheMissCounter.get(),
            hitRate = getHitRate()
        )
    }
}

7.3 缓存预热

kotlin
class CacheWarmer {
    suspend fun warmUpEssentialData() {
        // 应用启动时预加载重要数据
        listOf("config", "user_profile", "home_banner").forEach { key ->
            viewModelScope.launch {
                cacheManager.get(key) {
                    apiService.fetch(key)
                }
            }
        }
    }
}

8. 面试考点

考点 1:HTTP 缓存响应头

问题: 常见的 HTTP 缓存响应头有哪些?

答案:

  • Cache-Control: 控制缓存行为(max-age, no-cache, no-store 等)
  • Expires: 缓存过期时间
  • ETag: 资源标识符
  • Last-Modified: 资源最后修改时间

考点 2:304 响应

问题: 什么情况下会返回 304?

答案:

  • 客户端携带 If-None-Match 或 If-Modified-Since
  • 服务器验证资源未修改
  • 返回 304 Not Modified,客户端使用本地缓存

考点 3:OkHttp 缓存配置

问题: 如何配置 OkHttp 缓存?

答案:

kotlin
val cache = Cache(cacheDir, 100 * 1024 * 1024)
val client = OkHttpClient.Builder()
    .cache(cache)
    .build()

考点 4:多级缓存

问题: 什么是多级缓存?

答案:

  • 一级:内存缓存(LruCache)
  • 二级:磁盘缓存(OkHttp Cache)
  • 三级:数据库缓存(Room)

考点 5:缓存策略

问题: 常见的缓存策略有哪些?

答案:

  • Cache-First: 优先使用缓存
  • Network-First: 优先使用网络
  • Stale-While-Revalidate: 返回缓存同时更新
  • Offline-First: 离线优先

考点 6:图片缓存

问题: Glide 如何配置缓存?

答案:

kotlin
.diskCacheStrategy(DiskCacheStrategy.ALL)
.memoryCacheStrategy(MemoryCacheStrategy.ALL)

考点 7:缓存失效

问题: 如何实现缓存失效?

答案:

  • TTL(过期时间)
  • LRU(最近最少使用)
  • 手动失效
  • 版本控制

考点 8:协商缓存 vs 强缓存

问题: 协商缓存和强缓存的区别?

答案:

  • 强缓存:直接使用本地缓存,不请求服务器
  • 协商缓存:请求服务器验证缓存是否有效

考点 9:离线模式

问题: 如何实现离线访问?

答案:

  • 强制使用缓存(CacheControl.FORCE_CACHE)
  • 检查网络连接状态
  • 数据本地持久化

考点 10:缓存监控

问题: 如何监控缓存效果?

答案:

  • 记录命中率
  • 监控缓存大小
  • 分析缓存分布

总结

网络缓存是提升应用性能的关键:

✅ HTTP 缓存策略控制
✅ OkHttp 磁盘缓存
✅ 多级缓存架构
✅ 图片缓存优化
✅ 数据库持久化缓存

使用建议:

  • 静态资源使用长缓存
  • API 响应使用协商缓存
  • 重要数据使用数据库缓存
  • 图片使用专门的图片库

学习路径: HTTP 缓存 → OkHttp 缓存 → 多级缓存 → 完整缓存方案


本文档涵盖网络缓存的核心知识点,建议配合实际项目练习以加深理解。