关于OkHttp缓存post请求的问题
来源:互联网 发布:众力网络安装电话 编辑:程序博客网 时间:2024/06/05 11:56
现有这样一个要求,使用Retorfit+okhttp需要在有网的时候能够连接服务器,读取相关信息;在没网络断开的时候需要读取Okhttp的缓存来达到离线的效果。
基于上述的需求,可以使用Okhttp的拦截器来实现:
//设置缓存目录File cacheFile = new File(BaseApplication.getContext().getCacheDir(), "cache");Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); //100Mb//配置okhttpOkHttpClient okHttpClient = new OkHttpClient.Builder() .readTimeout(READ_TIME_OUT, TimeUnit.MILLISECONDS) .connectTimeout(CONNECT_TIME_OUT, TimeUnit.MILLISECONDS) .addNetworkInterceptor(mRewriteCacheControlInterceptor) .addInterceptor(headerInterceptor) .addInterceptor(logInterceptor) .cache(cache) //设置缓存 .build(); Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").serializeNulls().create();retrofit = new Retrofit.Builder() .client(okHttpClient)// .addConverterFactory(ScalarsConverterFactory.create()) .addConverterFactory(GsonConverterFactory.create(gson)) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .baseUrl(BASE_URL) .build();
//配置拦截器private final Interceptor mRewriteCacheControlInterceptor = new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); String cacheControl = request.cacheControl().toString(); if (!NetWorkUtils.isNetConnected(BaseApplication.getContext())) { request = request.newBuilder() .cacheControl(TextUtils.isEmpty(cacheControl)? CacheControl.FORCE_CACHE:CacheControl.FORCE_NETWORK) .build(); } Response originalResponse = chain.proceed(request); if (NetWorkUtils.isNetConnected(BaseApplication.getContext())) { //有网的时候连接服务器请求,缓存一天 return originalResponse.newBuilder() .header("Cache-Control", "public, max-age=" + MAX_AGE) .removeHeader("Pragma") .build(); } else { //网络断开时读取缓存 return originalResponse.newBuilder() .header("Cache-Control", "public, only-if-cached, max-stale=" + CACHE_STALE_SEC) .removeHeader("Pragma") .build(); } } };
通过以上就可以实现缓存。
但是,在使用该拦截器去执行POST请求的时候,会发现,即使在log中看到了读取缓存,但是实际上缓存目录里什么都没有。实际上是因为get请求一般较为持久,而post需要携带参数,会经常改动,所以没必要缓存,这个机制从Okhttp的源码里也可以看到:
//Cache类的put方法private CacheRequest put(Response response) throws IOException { String requestMethod = response.request().method(); if (HttpMethod.invalidatesCache(response.request().method())) { try { remove(response.request()); } catch (IOException ignored) { } return null; } //如果请求方式不用get,就直接跳过了 if (!requestMethod.equals("GET")) { return null; } //省略代码 }
但是,有些情况下确实需要去缓存post请求,要怎么去实现呢?
- Sqlite
网络正常时缓存响应信息到数据库,在没有网络的时候读出数据。 - DiskLruCache
通过文件缓存到本地。
这里是通过Sqlite来实现,至于如果通过DiskLruCache可以参考手动缓存Retrofit+OkHttp响应体,不再局限于Get请求缓存 。
通过Sqlite,需要缓存的基本信息有URL,Params, Response(链接,参数,响应结果)。当然可能还会缓存一些期限时长之类的字段,可以用于清理过期的缓存等。这里以基本的信息来实现。
//创建数据库和表public class MyDBHelper extends SQLiteOpenHelper{ private static final String DB_NAME = "test.db"; private static final int DB_VERSION = 1; static final String CACHE_TABLE = "cache"; public MyDBHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } @Override public void onCreate(SQLiteDatabase db) { String sql = "create table if not exists " + CACHE_TABLE + " (url text, params text, response text)"; db.execSQL(sql); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { String sql = "DROP TABLE IF EXISTS " + CACHE_TABLE; db.execSQL(sql); onCreate(db); }}
另外,还需创建一个数据库管理类CacheDao,用于增删该改查:
public class CacheDao { private static volatile CacheDao cacheDao; private MyDBHelper helper; private SQLiteDatabase database; private CacheDao(Context context){ helper = new MyDBHelper(context.getApplicationContext()); database = helper.getWritableDatabase(); } public static CacheDao getInstance(Context context) { if (cacheDao == null) { synchronized (CacheDao.class) { if (cacheDao == null) { cacheDao = new CacheDao(context); } } } return cacheDao; } //查 public String queryResponse(String urlKey, String params) { return null; } //增 public void insertResponse(String urlKey, String params, String value) { } //改 public void updateResponse(String urlKey, String params, String value) { } //删 public void deleteResponse(String urlKey, String params) { }}
数据库创建完成后,关键是要和Okhttp的请求拦截串联起来使用:
private final Interceptor mRewriteCacheControlInterceptor = new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); String url = request.url().toString(); //获取请求URL Buffer buffer = new Buffer(); request.body().writeTo(buffer); String params = buffer.readString(Charset.forName("UTF-8")); //获取请求参数 Response response; if (NetWorkUtils.isNetConnected(BaseApplication.getContext())) { int maxAge = 60 * 60*24; //如果网络正常,执行请求。 Response originalResponse = chain.proceed(request); //获取MediaType,用于重新构建ResponseBody MediaType type = originalResponse.body().contentType(); //获取body字节即响应,用于存入数据库和重新构建ResponseBody byte[] bs = originalResponse.body().bytes(); response = originalResponse.newBuilder() .removeHeader("Pragma") .removeHeader("Cache-Control") .header("Cache-Control", "public, max-age=" + maxAge) //重新构建body,原因在于body只能调用一次,之后就关闭了。 .body(ResponseBody.create(type, bs)) .build(); //将响应插入数据库 cacheDao.insertResponse(url, params, new String(bs, "GB2312")); } else { //没有网络的时候,由于Okhttp没有缓存post请求,所以不要调用chain.proceed(request),会导致连接不上服务器而抛出异常(504) String b = cacheDao.queryResponse(url, params); //读出响应 Log.d("OkHttp", "request:" + url); Log.d("OkHttp", "request method:" + request.method()); Log.d("OkHttp", "response body:" + b); int maxStale = 60 * 60 * 24 * 28; //构建一个新的response响应结果 response = new Response.Builder() .removeHeader("Pragma") .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale) .body(ResponseBody.create(MediaType.parse("application/json"), b.getBytes())) .request(request) .protocol(Protocol.HTTP_1_1) .code(200) .build(); } return response; } };
由于在没有网络的时候不调用chain.proceed(request),所以拦截器就在此中断,直接将response结果返回。
通过上述方式,即可实现post请求结果的缓存。
1 0
- 关于OkHttp缓存post请求的问题
- 关于Okhttp的post请求数据
- okhttp的post请求
- OkHttp post请求编码问题
- OkHttp的post网络请求
- OKhttp的get请求和post请求
- 简述OkHttp的使用:get、post请求
- OkHttp的POST、GET请求方式
- Okhttp和Retrofit的get,post请求
- Okhttp的post和get请求数据
- OkHttp的get/post同步异步请求
- OkHttp get post请求的封装
- 关于retrofit/okhttp 网络差 重复请求的问题
- OKhttp post请求
- okHttp post&get请求
- OkHttp Post请求方式
- Okhttp POST请求
- OkHttp getand post 请求
- SSL1222 矩形(并查集)
- 利用jQuery给表格添加分页效果
- 暗号编辑【代码056852】
- Create a mosaic out of several input videos
- linux下使用 du查看某个文件或目录占用磁盘空间的大小方法
- 关于OkHttp缓存post请求的问题
- lx007菲波那切数列(使用递归)
- 跟小博老师一起学JSP ——简介与工作原理
- C#串口通信
- Cow Bowling POJ
- Android 蓝牙开发之搜索、配对、连接、通信大全
- Android proguard混淆
- 数据结构实验之链表一:顺序建立链表
- TCP/UDP客户端服务器实现通信