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

Android中如何存储JSON数据实现离线访问?解决API离线加载差异

Android中实现JSON数据缓存以支持离线访问的方案

嘿,我来帮你搞定这个离线缓存JSON的问题!首先得搞清楚为什么Pixabay的API能离线显示——大概率是他们的服务端响应里设置了合适的HTTP缓存头(比如Cache-Control),你的网络库(比如OkHttp或者系统默认的HttpURLConnection)自动帮你缓存了响应;而你的自研API没配置这些头,所以默认不会缓存,离线自然加载不了。

下面给你几种靠谱的实现方案,你可以根据自己的需求选:

1. 利用OkHttp的内置缓存机制(推荐)

如果你的项目用OkHttp做网络请求,这是最省心的方案,它自带HTTP缓存支持,只需要简单配置就能实现离线缓存。

步骤:

  • 首先创建缓存目录,一般用App的内部缓存目录(不用申请权限)
  • 配置OkHttpClient,添加缓存拦截器,设置缓存大小和缓存策略
  • 在请求时根据网络状态设置缓存控制逻辑

代码示例(Kotlin):

// 创建缓存目录,大小设为100MB
val cacheDir = File(context.cacheDir, "http_cache")
val cache = Cache(cacheDir, 100 * 1024 * 1024) // 100MB

// 配置OkHttpClient
val okHttpClient = OkHttpClient.Builder()
    .cache(cache)
    .addInterceptor { chain ->
        var request = chain.request()
        // 根据网络状态设置缓存策略
        request = if (isNetworkAvailable(context)) {
            // 在线时,允许读取缓存,但强制请求服务器验证缓存是否新鲜
            request.newBuilder().header("Cache-Control", "public, max-age=60").build()
        } else {
            // 离线时,允许读取最多7天前的缓存
            request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=604800").build()
        }
        chain.proceed(request)
    }
    .build()

// 网络状态判断方法(需要权限:ACCESS_NETWORK_STATE)
fun isNetworkAvailable(context: Context): Boolean {
    val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    val networkInfo = connectivityManager.activeNetworkInfo
    return networkInfo != null && networkInfo.isConnected
}

如果你的自研API能修改,也可以在服务端设置Cache-Control头(比如Cache-Control: public, max-age=3600),这样客户端不用写拦截器,OkHttp会自动按照服务端的规则缓存。

2. 手动将JSON存储到本地文件

如果你需要更灵活的缓存逻辑(比如自定义缓存键、过期时间),可以手动把JSON字符串写到本地文件里。

代码示例(Kotlin):

保存JSON到缓存文件:

fun saveJsonToCache(context: Context, cacheKey: String, jsonString: String) {
    try {
        val cacheFile = File(context.cacheDir, cacheKey)
        val outputStream = FileOutputStream(cacheFile)
        outputStream.write(jsonString.toByteArray())
        outputStream.close()
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

从缓存文件读取JSON:

fun loadJsonFromCache(context: Context, cacheKey: String): String? {
    return try {
        val cacheFile = File(context.cacheDir, cacheKey)
        if (!cacheFile.exists()) return null
        // 可以在这里添加过期判断,比如检查文件最后修改时间
        val lastModified = cacheFile.lastModified()
        val currentTime = System.currentTimeMillis()
        if (currentTime - lastModified > 7 * 24 * 60 * 60 * 1000) { // 超过7天就过期
            cacheFile.delete()
            return null
        }
        val inputStream = FileInputStream(cacheFile)
        val jsonString = inputStream.bufferedReader().use { it.readText() }
        inputStream.close()
        jsonString
    } catch (e: IOException) {
        e.printStackTrace()
        null
    }
}

这里的cacheKey可以用请求的URL加上参数拼接,确保每个请求的缓存唯一。

3. 使用Room数据库存储JSON(适合需要结构化查询的场景)

如果你的JSON数据需要后续查询、筛选,或者想更规范地管理缓存,可以用Room数据库存储JSON字符串,同时记录缓存的key和过期时间。

步骤:

  • 创建实体类,包含缓存key、JSON字符串、过期时间
  • 编写Dao接口,定义插入和查询方法
  • 创建Room数据库类

代码示例(Kotlin):

实体类:
@Entity(tableName = "json_cache")
data class JsonCache(
    @PrimaryKey val cacheKey: String,
    val jsonContent: String,
    val expireTime: Long // 过期时间戳(毫秒)
)
Dao接口:
@Dao
interface JsonCacheDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertCache(jsonCache: JsonCache)

    @Query("SELECT jsonContent FROM json_cache WHERE cacheKey = :key AND expireTime > :currentTime")
    suspend fun getValidCache(key: String, currentTime: Long): String?
}
数据库类:
@Database(entities = [JsonCache::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun jsonCacheDao(): JsonCacheDao

    companion object {
        // 单例实现
        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getInstance(context: Context): AppDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "app_database"
                ).build()
                INSTANCE = instance
                instance
            }
        }
    }
}

使用示例:

// 保存缓存
suspend fun saveJsonToRoom(context: Context, cacheKey: String, jsonString: String, expireDays: Int = 7) {
    val expireTime = System.currentTimeMillis() + expireDays * 24 * 60 * 60 * 1000
    val jsonCache = JsonCache(cacheKey, jsonString, expireTime)
    AppDatabase.getInstance(context).jsonCacheDao().insertCache(jsonCache)
}

// 读取缓存
suspend fun loadJsonFromRoom(context: Context, cacheKey: String): String? {
    return AppDatabase.getInstance(context).jsonCacheDao().getValidCache(cacheKey, System.currentTimeMillis())
}

一些注意事项

  • 缓存过期逻辑:不管用哪种方案,都要记得设置合理的过期时间,避免用户看到过时的数据
  • 缓存清理:定期清理过期的缓存文件或数据库记录,防止占用过多存储空间
  • 权限问题:如果使用外部存储缓存,需要在AndroidManifest.xml中添加WRITE_EXTERNAL_STORAGE权限(Android 6.0以上还要动态申请),不过优先用内部缓存目录,不需要权限
  • 网络状态判断:离线时优先读取缓存,在线时可以选择更新缓存或者直接用缓存

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 08:01:22