仅当本地缓存数据早于 5 分钟或根本不存在时,才进行改造从服务器获取新数据

Make retrofit fetch new data from server only if localy cached data is older than 5 minutes or it doesn't exist at all

仅当本地缓存的数据早于 5 分钟或不存在时,我才必须让我的改装客户端从服务器获取新数据

class NewsServices {

    companion object{
        private const val CACHE_CONTROL_HEADER = "Cache-Control"
        private const val CACHE_CONTROL_NO_CACHE = "no-cache"
        private const val CACHE_SIZE = 5 * 1024 * 1024L
        private const val BASE_URL = "https://newsapi.org/"
        private const val SOURCE = "bbc-news"
        private const val SORTBY = "top"
        private const val APIKEY = "75702474c08c4c0c96c4081147233679"
    }

    fun getCall(application: Application): Call<NewsResponse> {
        val retrofit = retrofit(okHttp(httpCache(application )))
        val service = retrofit.create(NewsService::class.java)
        return service.getCurrentNews(SOURCE, SORTBY, APIKEY)
    }

    private fun retrofit(okHttpClient: OkHttpClient) = Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(okHttpClient)
            .build()

    private fun okHttp(cache: Cache): OkHttpClient {
        return OkHttpClient.Builder()
                .cache(cache)
                .addInterceptor(getLoggingInterceptor())
                .addNetworkInterceptor(CacheInterceptor())
                .build()
    }

    private fun httpCache(application: Application): Cache {
        return Cache(application.applicationContext.cacheDir, CACHE_SIZE)
    }

    private fun getLoggingInterceptor(): HttpLoggingInterceptor {
        val loggingInterceptor = HttpLoggingInterceptor()
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BASIC)
        return loggingInterceptor
    }

    class CacheInterceptor : Interceptor {

        override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
            val request = chain.request()
            request.newBuilder().header(CACHE_CONTROL_HEADER, "public, only-if-cached, max-stale=" + 5).build()
            return chain.proceed(request)


//            val request = chain.request()
//            val originalResponse = chain.proceed(request)
//
//            val shouldUseCache = request.header(CACHE_CONTROL_HEADER) != CACHE_CONTROL_NO_CACHE
//            if(!shouldUseCache) return originalResponse
//
//            val cacheControl = CacheControl.Builder()
//                    .maxAge(5, TimeUnit.MINUTES)
//                    .build()
//
//            return originalResponse.newBuilder()
//                    .header(CACHE_CONTROL_HEADER, cacheControl.toString())
//                    .build()
        }

    }

}

这是我目前拥有的。在日志中,我可以看到每次都向服务器发送了两个GET请求,我不确定他是否正在使用缓存。下面列出了日志。

2021-05-02 21:18:43.687 1302-1302/com.demo.factorynews W/emo.factorynew: Accessing hidden method Ljava/lang/invoke/MethodHandles$Lookup;-><init>(Ljava/lang/Class;I)V (greylist, reflection, allowed)
2021-05-02 21:18:43.731 1302-1562/com.demo.factorynews I/okhttp.OkHttpClient: --> GET https://newsapi.org/v1/articles?&source=bbc-news&sortBy=top&apiKey=75702474c08c4c0c96c4081147233679
2021-05-02 21:18:43.766 1302-1302/com.demo.factorynews W/Looper: Slow Looper main: Activity com.demo.factorynews/.MainActivity is 321ms late (wall=378ms running=0ms ClientTransaction{ callbacks=[android.app.servertransaction.LaunchActivityItem] lifecycleRequest=android.app.servertransaction.ResumeActivityItem }) because of 1 msg, msg 1 took 345ms (seq=2 h=android.app.ActivityThread$H w=110)
2021-05-02 21:18:43.768 1302-1302/com.demo.factorynews W/Looper: Slow Looper main: Activity com.demo.factorynews/.MainActivity is 700ms late (wall=0ms running=0ms ClientTransaction{ callbacks=[android.app.servertransaction.TopResumedActivityChangeItem] }) because of 2 msg, msg 1 took 345ms (seq=2 h=android.app.ActivityThread$H w=110), msg 2 took 378ms (seq=3 late=321ms h=android.app.ActivityThread$H w=159)
2021-05-02 21:18:43.857 1302-1610/com.demo.factorynews I/okhttp.OkHttpClient: --> GET https://newsapi.org/v1/articles?&source=bbc-news&sortBy=top&apiKey=75702474c08c4c0c96c4081147233679
2021-05-02 21:18:43.871 1302-1302/com.demo.factorynews E/ActivityInjector: get life cycle exception
    java.lang.ClassCastException: android.os.BinderProxy cannot be cast to android.app.servertransaction.ClientTransaction
        at android.app.ActivityInjector.checkAccessControl(ActivityInjector.java:24)
        at android.app.Activity.onResume(Activity.java:1859)
        at androidx.fragment.app.FragmentActivity.onResume(FragmentActivity.java:456)
        at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1453)
        at android.app.Activity.performResume(Activity.java:8050)
        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4287)
        at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4329)
        at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
        at android.app.ClientTransactionHandler.executeTransaction(ClientTransactionHandler.java:57)
        at android.app.ActivityThread.handleRelaunchActivityLocally(ActivityThread.java:5358)
        at android.app.ActivityThread.access00(ActivityThread.java:230)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2078)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:224)
        at android.app.ActivityThread.main(ActivityThread.java:7551)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:995)
2021-05-02 21:18:44.269 1302-1562/com.demo.factorynews I/okhttp.OkHttpClient: <-- 200 https://newsapi.org/v1/articles?&source=bbc-news&sortBy=top&apiKey=75702474c08c4c0c96c4081147233679 (537ms, unknown-length body)
2021-05-02 21:18:44.579 1302-1610/com.demo.factorynews I/okhttp.OkHttpClient: <-- 200 https://newsapi.org/v1/articles?&source=bbc-news&sortBy=top&apiKey=75702474c08c4c0c96c4081147233679 (722ms, unknown-length body)

只需将您的缓存保存在 room/sqlite/file 中,并将上次更新日期保存在共享首选项中。使用本地和远程数据源创建存储库 class。如果上次更新日期小于 5 分钟,则从本地数据源获取数据,否则从远程数据源获取。

或者您可以尝试使用 okhttp 功能:您需要这样的缓存拦截器:

public class CacheInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());

        CacheControl cacheControl = new CacheControl.Builder()
                .maxAge(5, TimeUnit.MINUTES) // 5 minutes cache
                .build();

        return response.newBuilder()
                .removeHeader("Pragma")
                .removeHeader("Cache-Control")
                .header("Cache-Control", cacheControl.toString())
                .build();
    }
}

像这样将带有缓存的拦截器添加到您的 OkHttpClient 中:

File httpCacheDirectory = new File(applicationContext.getCacheDir(), "http-cache");
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(httpCacheDirectory, cacheSize);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .addNetworkInterceptor(new CacheInterceptor())
            .cache(cache)
            .build();