手动缓存Retrofit+OkHttp响应体,不再局限于Get请求缓存

来源:互联网 发布:bms核心算法 编辑:程序博客网 时间:2024/06/06 20:58

手动缓存Retrofit+OkHttp响应体,不再局限于Get请求缓存

标签: OkHttp缓存RESTfulRetrofitPost
 4799人阅读 评论(7) 收藏 举报
 分类:

目录(?)[+]

转载请标明出处: 
http://blog.csdn.net/iamzgx/article/details/51764848 
本文出自:【iGoach的博客】

概括

这篇博客是接着上一篇博客学会Retrofit+OkHttp+RxAndroid三剑客的使用,让自己紧跟Android潮流的步伐,没看过的,建议看完上一篇再来看这篇。在上一篇博客中仅仅是简单的讲解了OkHttp的缓存问题,主要是通过http协议里面的control-cache控制缓存,而且是仅仅只能是Get请求才能缓存,如果Post请求OkHttp会让response返回null,同时报504错误,也就是没缓存。okhttp为什么要这样做呢?通过查看缓存的文件,我们可以发现,OkHttp缓存的是整个http请求的信息,所以这就和http协议有关系了。在RESTful API里面,我们把Get请求理解为从服务端查询数据,Post请求理解为更新服务端数据,而http协议里面缓存通常只适用于idempotent request,也就是Get请求,为什么只适应Get请求?我们都知道Get请求url结合提交参数是唯一标示,而Post请求的参数是在http的body体里面,是可变的,无法成为唯一的标示。但是,我们在项目中基本上每一个接口都要提交基本参数,一般用的都是Post请求。Get请求还不太安全,请求的路径大小还有限制。既然OkHttp有限制。那么我们可以自己手动缓存。

android的缓存处理

既然要手动缓存,那么我们就要来看看Android里面手动缓存有哪些。主要有两种方式,一种是sqlite缓存,一种是文件缓存。

  • sqlite缓存 
    目前有很多第三方sqlite框架,比如可以结合GreenDao来做缓存,一个缓存对应一个表。把url路经,下载时间,过期时间等信息都存放到数据库。然后把url做为请求的唯一标示,在有网的情况下,判断当前请求url缓存是否存在,存在就要移除数据库里面的缓存,然后缓存新的缓存,在没有网络的情况下,判断缓存是否过期,然后进行数据库操作。从这里我们可以看出,数据库操作还是比较频繁的,一不留神,就会出现应用性能问题,ANR问题,指针问题。而且android数据库是放在/data/data/<包名>/databases/目录下,它会占用应用内存的,一但缓存很多的话,就要及时去清理缓存,很麻烦。

  • 文件缓存 
    为什么说文件缓存更好呢?如果SD存在的话,我们可以把缓存放在SD的/data/data/<包名>/cache目录下,不存在SD的话,再放在/data/data/<包名>下面。即使内存再多,也不会影响应用的内置应用空间。文件缓存一般都会通过DiskLruCache实现,DiskLruCache是硬盘缓存,即使应用进程结束了,缓存还是存在的。当应用卸载时,改目录的数据也会清除掉,不会留下残余数据。DiskLruCache缓存,没有什么过期时间之说,只要它存在文件里面,我们就可以随时去读取它。下面我们就用DiskLruCache对Retrofit+OkHttp的响应体进行缓存。这里我们只缓存json数据。

DiskLruCache的使用方法

获取DiskLruCache对象

public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
  • 1
  • 1

不能直接通过new的方法创建,要通过调用DiskLruCache.open()这个方法获取,有四个参数,File指的是缓存的存储路径,一般优先存储于SD卡的 /sdcard/Android/data/<包名>/cache 路径下,如果SD卡不存在,再存在/data/data/<包名>/cache 这个路径下,判断代码如下

   private File getDiskCacheDir(Context context, String uniqueName)    {        String cachePath;        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())                || !Environment.isExternalStorageRemovable())        {            //如果SD卡存在通过getExternalCacheDir()获取路径,            cachePath = context.getExternalCacheDir().getPath();        } else        {             //如果SD卡不存在通过getCacheDir()获取路径,            cachePath = context.getCacheDir().getPath();        }        //放在路径 /.../data/<application package>/cache/uniqueName        return new File(cachePath + File.separator + uniqueName);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

appVersion指的是版本号,可以指应用的版本号,valueCount指的就是一个key对应多少个文件,一般我们指定1个文件,一对一使得后面更好获取。maxSize指的是缓存的最大大小,一般传入5M或者10M就够了。

写入缓存

首先我们先获取一个DiskLruCache.Editor对象,代码如下

    public DiskLruCache.Editor editor(String key)    {        try        {            key = Utils.hashKeyForDisk(key);            //wirte DIRTY            DiskLruCache.Editor edit = mDiskLruCache.edit(key);            //edit maybe null :the entry is editing            if (edit == null)            {                Log.w(TAG, "the entry spcified key:" + key + " is editing by other . ");            }            return edit;        } catch (IOException e)        {            e.printStackTrace();        }        return null;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

首先进行的是Utils.hashKeyForDisk(key),也就是通过MD5生成唯一的请求标示,这样就可以通过key来获取DiskLruCache.Editor实例。获取到实例后就可以获取到OutputStream,然后通过BufferedWriter写入,如下代码

    public void put(String key, String value)    {        DiskLruCache.Editor edit = null;        BufferedWriter bw = null;        try        {            edit = editor(key);            if (edit == null) return;            OutputStream os = edit.newOutputStream(0);            bw = new BufferedWriter(new OutputStreamWriter(os));            bw.write(value);            edit.commit();//write CLEAN        } catch (IOException e)        {            e.printStackTrace();            try            {                //s                edit.abort();//write REMOVE            } catch (IOException e1)            {                e1.printStackTrace();            }        } finally        {            try            {                if (bw != null)                    bw.close();            } catch (IOException e)            {                e.printStackTrace();            }        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

读取缓存

首先是通过key获取DiskLruCache.Snapshot实例,然后得到InputStream,如下代码

    public InputStream get(String key)    {        try        {            DiskLruCache.Snapshot snapshot = mDiskLruCache.get(Utils.hashKeyForDisk(key));            if (snapshot == null) //not find entry , or entry.readable = false            {                Log.e(TAG, "not find entry , or entry.readable = false");                return null;            }            //write READ            return snapshot.getInputStream(0);        } catch (IOException e)        {            e.printStackTrace();            return null;        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

然后就是InputStreamReader读取,如下代码

    public String getAsString(String key) {        InputStream inputStream = null;        inputStream = get(key);        if (inputStream == null) return null;        String str = null;        try {            str = Util.readFully(new InputStreamReader(inputStream, Util.UTF_8));        } catch (IOException e) {            e.printStackTrace();            try {                inputStream.close();            } catch (IOException e1) {                e1.printStackTrace();            }        }        return str;    }    static String readFully(Reader reader) throws IOException    {        try        {            StringWriter writer = new StringWriter();            char[] buffer = new char[1024];            int count;            while ((count = reader.read(buffer)) != -1)            {                writer.write(buffer, 0, count);            }            return writer.toString();        } finally        {            reader.close();        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

然后就是删除操作

    public boolean remove(String key)    {        try        {            key = Utils.hashKeyForDisk(key);            return mDiskLruCache.remove(key);        } catch (IOException e)        {            e.printStackTrace();        }        return false;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

直接remove掉就ok了。

DiskLruCache的封装

从Github里面搜索DiskLruCache,可以看到鸿洋大神的base-diskcache框架,它主要是把diskcache封装成和AsimpleCache框架一样,挺好用的。 
使用方法如下(来源于base-diskcache框架)

存put(String key, Bitmap bitmap)put(String key, byte[] value)put(String key, String value)put(String key, JSONObject jsonObject)put(String key, JSONArray jsonArray)put(String key, Serializable value)put(String key, Drawable value)editor(String key).newOutputStream(0);//原有的方式String getAsString(String key);JSONObject getAsJson(String key)JSONArray getAsJSONArray(String key)<T> T getAsSerializable(String key)Bitmap getAsBitmap(String key)byte[] getAsBytes(String key)Drawable getAsDrawable(String key)InputStream get(String key);//原有的用法
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

这里我只是保存响应的json,只用到

put(String key, String value)
  • 1
  • 1

String getAsString(String key);
  • 1
  • 1

两个方法,至于key使用请求参数生成的MD5做为唯一的标示。

下面就使用这个DiskLruCache封装进行手动缓存,DiskLruCache的源码和封装代码可以去鸿洋的github上下载。

HRetrofitNetHelper代码的修改

基于上一篇博客的HRetrofitNetHelper对象。进行代码修改,修改点如下

  • 去除OkHttp的cache缓存配置
  • 去除mUrlInterceptor的拦截器
  • 改在call的onresponse里面进行操作
  • enqueueCall方法配置成链式编程配置

然后再贴上全部的代码,注意几个修改点就好了。

public class HRetrofitNetHelper implements HttpLoggingInterceptor.Logger {    public static HRetrofitNetHelper mInstance;    public Retrofit mRetrofit;    public OkHttpClient mOkHttpClient;    public HttpLoggingInterceptor mHttpLogInterceptor;    private BasicParamsInterceptor mBaseParamsInterceptor;    private  Context mContext;    public  Gson mGson;    //DiskLruCache封装的帮助类,    private  DiskLruCacheHelper diskLruCacheHelper;    public static final String BASE_URL = "http://192.168.1.102:8080/GoachWeb/";    private Action1<String> onNextAction;    private HRetrofitNetHelper(Context context){        this.mContext = context ;        createSubscriberByAction();        mGson = new GsonBuilder()                .setDateFormat("yyyy-MM-dd HH:mm:ss")                .create();        mHttpLogInterceptor = new HttpLoggingInterceptor(this);        mHttpLogInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);        Map<String,String> tempParams = getBaseParams();        mBaseParamsInterceptor = new BasicParamsInterceptor.Builder()                .addParamsMap(tempParams)                .build();        try {        //创建DiskLruCacheHelper 对象            diskLruCacheHelper = new DiskLruCacheHelper(mContext);        } catch (IOException e) {            e.printStackTrace();        }        //这里去除了缓存配置和mUrlInterceptor的配置        mOkHttpClient = new OkHttpClient.Builder()                .connectTimeout(12, TimeUnit.SECONDS)                .writeTimeout(20, TimeUnit.SECONDS)                .readTimeout(20, TimeUnit.SECONDS)                .retryOnConnectionFailure(true)                .addInterceptor(mHttpLogInterceptor)                .addInterceptor(mBaseParamsInterceptor)                .build();        mRetrofit = new Retrofit.Builder()                .baseUrl(BASE_URL)                .addConverterFactory(GsonConverterFactory.create(mGson))                .client(mOkHttpClient)                .build();    }    public static HRetrofitNetHelper getInstance(Context context){        if(mInstance==null){            synchronized (HRetrofitNetHelper.class){                if(mInstance==null){                    mInstance =  new HRetrofitNetHelper(context);                }            }        }        return mInstance;    }    public <T> T getAPIService(Class<T> service) {        return mRetrofit.create(service);    }    /*这里改成链式编程,默认是不缓存。在不缓存的情况下,只需配置Call<BaseResp<D>>实例,也就是调用上面getAPIService方法获取的实例。然后就是retrofitCallBack回调接口,如果需要缓存的情况,那么就要再配置isCache为true,然后配置Type(主要是Gson解析泛型会报错Java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to,所以需再传递这个参数进行解析),最后调用start方法进行请求*/    public static final class enqueueCall{        boolean isCache;        Type clazz;        Call call;        RetrofitCallBack retrofitCallBack;        HRetrofitNetHelper mRetrofitNetHelper;        private Context mContext;        public  Gson mGson;        private DiskLruCacheHelper diskLruCacheHelper;        public enqueueCall(HRetrofitNetHelper retrofitNetHelper){            isCache = false;            this.mRetrofitNetHelper = retrofitNetHelper;            this.mContext = retrofitNetHelper.mContext;            this.mGson = retrofitNetHelper.mGson;            this.diskLruCacheHelper = retrofitNetHelper.diskLruCacheHelper;        }        public <D> enqueueCall call(Call<BaseResp<D>> call){            this.call = call ;            return this;        }        public enqueueCall clazz(Type clazz){            this.clazz = clazz ;            return this;        }        public <D> enqueueCall retrofitCallBack(RetrofitCallBack<D> retrofitCallBack){            this.retrofitCallBack = retrofitCallBack ;            return this;        }        public enqueueCall isCache(boolean isCache){            this.isCache = isCache ;            return this;        }        public <D> enqueueCall start(){            call.enqueue(new Callback<BaseResp<D>>() {                @Override                public void onResponse(Call<BaseResp<D>> call, Response<BaseResp<D>> response) {                    //获取请求Request                     Request request = call.request();                    //获取请求的url                    String requestUrl = call.request().url().toString();                    //去获取返回数据                    BaseResp<D> resp = response.body() ;                    //去获取RequestBody                    RequestBody requestBody = request.body();                    //缓存格式为utf-8                    Charset charset = Charset.forName("UTF-8");                    //去获取要保存的key                     String key="";                     //如果是Post请求,要通过Buffer去读取body体里面的参数                    if(method.equals("POST")){                        MediaType contentType = requestBody.contentType();                        if (contentType != null) {                            charset = contentType.charset(Charset.forName("UTF-8"));                        }                        Buffer buffer = new Buffer();                        try {                            requestBody.writeTo(buffer);                        } catch (IOException e) {                            e.printStackTrace();                        }                        key = buffer.readString(charset);                        buffer.close();                    }else{                    //如果不是Post请求,比如Get请求,那么久通过url做为唯一标识                        key = requestUrl;                    }                    Log.d("zgx","response==========key"+key);                    //处理特殊接口,如果是登录接口进行弹框提示                    if(!TextUtils.isEmpty(requestUrl)){                        if(requestUrl.contains("LoginDataServlet")) {                            if (Looper.myLooper() == null) {                                Looper.prepare();                            }                            mRetrofitNetHelper.createObservable("现在请求的是登录接口");                        }                    }                    //分为有网和没网的情况下                    //如果有网                    if(NetUtil.checkNetwork(mContext)!=NetUtil.NO_NETWORK){                        //如果返回数据为null                        if(resp==null){                        //回调失败接口                            if(retrofitCallBack!=null)                                retrofitCallBack.onFailure("暂无数据");                        }else{                        //如果是接口返回2000或者2001或者2002,进行弹框提示                            if (resp.getResultCode() == 2000 || resp.getResultCode() == 2001 || resp.getResultCode() == 2002) {                                Toast.makeText(mContext,"code====="+resp.getResultCode(),Toast.LENGTH_SHORT).show();                            }                            //如果接口返回200,并且http请求code返回200,说明请求成功                            if (resp.getResultCode() == 200&&response.code()==200) {                                if(retrofitCallBack!=null){                                    //需要缓存数据                                    String cacheResponse = mGson.toJson(resp); //判断下当前是否存在key缓存的数据,如果存在移除掉,                                   if(!TextUtils.isEmpty(key)&&!TextUtils.isEmpty(diskLruCacheHelper.getAsString(key)))                                        diskLruCacheHelper.remove(key); //当需要缓存的数据不为空的时候,并且需要缓存的时候,通过diskLruCacheHelper进行缓存                                   if(!TextUtils.isEmpty(key)&&!TextUtils.isEmpty(cacheResponse)&&isCache){                                        Log.d("zgx","response========cacheResponse"+cacheResponse);                                        diskLruCacheHelper.put(key,cacheResponse);                                    }                                    //然后就是回调成功接口                                    retrofitCallBack.onSuccess(resp);                                }                            } else {                            //这个是请求失败,那么就回调失败接口                                // ToastMaker.makeToast(mContext, resp.errMsg, Toast.LENGTH_SHORT);                                if(retrofitCallBack!=null)                                    retrofitCallBack.onFailure(resp.getErrMsg());                            }                        }                        return;                    }                    //没有网络的情况下,去获取key对应的缓存                    String json = diskLruCacheHelper.getAsString(key);                    //如果缓存不存在,那么久回调失败接口                    if(json==null){                        Toast.makeText(mContext, "没有缓存!", Toast.LENGTH_SHORT).show();                        if(retrofitCallBack!=null){                            retrofitCallBack.onFailure("没有缓存!");                        }                    }else{                    //判断是否配置clazz,一定要先配置,要不然Gson解析出错                        if(clazz==null){                            throw new IllegalArgumentException("请先配置clazz");                        }                        //解析缓存数据,然后进行回调成功接口                        resp = mGson.fromJson(json,clazz);                        if(retrofitCallBack!=null){                            retrofitCallBack.onSuccess(resp);                        }                    }                }                @Override                public void onFailure(Call<BaseResp<D>> call, Throwable t) {                    //   ToastMaker.makeToast(mContext, "网络错误,请重试!", Toast.LENGTH_SHORT);                    if(retrofitCallBack!=null){                        retrofitCallBack.onFailure(t.toString());                    }                }            });            return this;        }    }    //.....省略,和上篇博客代码一样    //这里我们改成通过diskLruCacheHelper封装的类进行删除缓存    public void clearCache() throws IOException {        diskLruCacheHelper.delete();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210

主要修改的地方,上面基本上都注释到了,这里没有做缓存的过期时间,有网的情况下,还是保持数据的实时性,没网的情况下才会去读取缓存。

API修改为Post请求

ILoginService.class

public interface ILoginService {    @FormUrlEncoded    @POST("LoginDataServlet")    Call<BaseResp<RegisterBean>> userLogin(@Field("username") String username, @Field("password") String password);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

INewsService.class

public interface INewsService {    @FormUrlEncoded    @POST("NewsDataServlet")    Call<BaseResp<News<NewItem>>> userNews(@Field("userId") String userId);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

这里主要是测试这两个接口

请求修改为链式编程

登录请求修改代码如下

首先实现回调接口

//传入成功回调的BaseResp<T>的泛型T为RegisterBeanimplements HRetrofitNetHelper.RetrofitCallBack<RegisterBean>
  • 1
  • 2
  • 1
  • 2

然后是Call请求配置

final Call<BaseResp<RegisterBean>> repos = loginService.userLogin(username,password);      new HRetrofitNetHelper       .enqueueCall(HRetrofitNetHelper.getInstance(this))       .call(repos)//repos指的是retrofitNetHelper.getAPIService返回的API       .retrofitCallBack(this)//配置回调接口       .isCache(true)//设置需要缓存       .clazz(new TypeToken<BaseResp<RegisterBean>>(){}.getType())//Gson解析缓存需要       .start();//真正开始发起请求
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

然后实现两个回调方法

     @Override    public void onSuccess(BaseResp<RegisterBean> baseResp) {        Date date = baseResp.getResponseTime();        if(baseResp.getData().getErrorCode()==1){            Toast.makeText(getBaseContext(),"登录成功",Toast.LENGTH_SHORT).show();        }else {            Toast.makeText(getBaseContext(),"用户不存在",Toast.LENGTH_SHORT).show();        }        mDialog.dismiss();    }    @Override    public void onFailure(String error) {        Log.d("zgx","onFailure======"+error);        mDialog.dismiss();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

如果新闻页也要缓存,那么代码同理修改如下。

    private void loadData(){        INewsService newService = retrofitNetHelper.getAPIService(INewsService.class);        Log.d("zgx","mUserId====="+mUserId);        final Call<BaseResp<News<NewItem>>> repos = newService.userNews(mUserId);        new HRetrofitNetHelper.enqueueCall(HRetrofitNetHelper.getInstance(this))                .call(repos)                .retrofitCallBack(this)                .isCache(true)                .clazz(new TypeToken<BaseResp<News<NewItem>>>(){}.getType())                .start();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这样就缓存了登录接口的数据和新闻页面的数据。 
下面就来测试下,只缓存登录接口。测试结果为有网的情况下,根据上面代码知道登录成功会弹出登录成功的Toast,并且会生成缓存文件,没有网络的情况下会去读取缓存,并且还是会弹出Toast提示,登录失败不弹。效果如下

这里写图片描述

接下来我们再看下没有缓存的效果,代码只要修改不配置

HRetrofitNetHelper.enqueueCall(HRetrofitNetHelper.getInstance(this))                            .call(repos)                            .retrofitCallBack(this)                            .start();
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

然后就来看效果,有网的情况下应该为登录成功,没网的情况下,提示没有缓存,效果如下

这里写图片描述

Get请求效果同理。同样可以得到这样的效果,感兴趣的可以去试下。

最后配置3个权限

 <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

总体感觉Retrofit+OkHttp框架用起来还是很方便的。特别是响应式编程,用的特别爽。还有就是Retrofit的源码设计的特别完美。不过在这里,用RxAndroid用的还是比较少,相信以后会用的越来越多,而且现在谷歌的agera响应式编程也出来了。


原创粉丝点击