仅当本地缓存数据早于 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();
仅当本地缓存的数据早于 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();