天天看點

retrofit2.0增加先讀緩存後請求網絡的功能

由于前段時間更新了retrofit版本到了2.1,導緻原來寫的緩存架構出現了點問題,後來在github上看到一個smartCache緩存架構

而其版本是2.0beta版和正式版有點差距,于是就自己更改了一下架構代碼,現在分享給大家

retrofit2.0增加先讀緩存後請求網絡的功能

項目隻有6個類

AndroidExecutor:線程池裡面封裝了一個handler為了post資料到主線程種

CachingSystem:接口裡面就兩方法存緩存以及擷取緩存

BasicCaching:CachingSystem對應的實作類,實作了存取緩存方法,使用的是LruCache以及DiskLruCache算法對其進行緩存

SmartCall:自定義的一個回調接口,要使用這個架構就必須使用這個回調而不是retrofit對應的Call

SmartUtils:工具類,主要是用來把網絡資料轉化成byte資料,資料轉成byte

SmartCallFactory:關鍵類,所有的邏輯都在裡面

主要講解一下SmartCallFactory這個類

public class SmartCallFactory extends CallAdapter.Factory {
    //緩存類
    private final CachingSystem cachingSystem;
    //線程池
    private final Executor asyncExecutor;

    public SmartCallFactory(CachingSystem cachingSystem){
        this.cachingSystem = cachingSystem;
        this.asyncExecutor = new AndroidExecutor();
    }

    public SmartCallFactory(CachingSystem cachingSystem, Executor executor){
        this.cachingSystem = cachingSystem;
        this.asyncExecutor = executor;
    }

    @Override
    public CallAdapter<SmartCall<?>> get(final Type returnType, final Annotation[] annotations,
                                         final Retrofit retrofit) {

        TypeToken<?> token = TypeToken.of(returnType);
        //如果不是對應的SmartCall則不會執行
        if (token.getRawType() != SmartCall.class) {
            return null;
        }

        if (!(returnType instanceof ParameterizedType)) {
            //必須有實體類
            throw new IllegalStateException(
                    "SmartCall must have generic type (e.g., SmartCall<ResponseBody>)");
        }

        //獲得SmartCall<T>中的類型
        final Type responseType = ((ParameterizedType) returnType).getActualTypeArguments()[0];
        final Executor callbackExecutor = asyncExecutor;

        return new CallAdapter<SmartCall<?>>() {
            @Override
            public Type responseType() {
                return responseType;
            }

            @Override
            public <R> SmartCall<R> adapt(Call<R> call) {

                return new SmartCallImpl<>(callbackExecutor, call, responseType(), annotations,
                        retrofit, cachingSystem);
            }
        };
    }

    /**
     * 自定義一個回調執行個體
     * 所有的邏輯都是在這裡面
     * @param <T>
     */
    static class SmartCallImpl<T> implements SmartCall<T>{
        private final Executor callbackExecutor;
        private final Call<T> baseCall;
        private final Type responseType;
        private final Annotation[] annotations;
        private final Retrofit retrofit;
        private final CachingSystem cachingSystem;
        private final Request request;

        public SmartCallImpl(Executor callbackExecutor, Call<T> baseCall, Type responseType,
                             Annotation[] annotations, Retrofit retrofit, CachingSystem cachingSystem){
            this.callbackExecutor = callbackExecutor;
            this.baseCall = baseCall;
            this.responseType = responseType;
            this.annotations = annotations;
            this.retrofit = retrofit;
            this.cachingSystem = cachingSystem;

            // This one is a hack but should create a valid Response (which can later be cloned)
            this.request = buildRequestFromCall();
        }

        /***
         * 建構一個新的請求
         * 這裡使用的反射機制
         * * @return A valid Request (that contains query parameters, right method and endpoint)
         */
        private Request buildRequestFromCall(){
            try {
                Field argsField = baseCall.getClass().getDeclaredField("args");
                argsField.setAccessible(true);
                Object[] args = (Object[]) argsField.get(baseCall);
                //retrofit2.0更改了字段(1.0+)requestFactory-->(2.0+)serviceMethod
                Field serviceMethodField = baseCall.getClass().getDeclaredField("serviceMethod");
                serviceMethodField.setAccessible(true);
                Object requestFactory = serviceMethodField.get(baseCall);
                //retrofit2.0更改了方法(1.0+)create-->(2.0+)toRequest
                Method createMethod = requestFactory.getClass().getDeclaredMethod("toRequest", Object[].class);
                createMethod.setAccessible(true);
                return (Request) createMethod.invoke(requestFactory, new Object[]{args});
            }catch(Exception exc){
//                Log.e("buildRequestFromCall"+exc.toString());
                return null;
            }
        }

        public void enqueueWithCache(final Callback<T> callback) {
            Runnable enqueueRunnable = new Runnable() {
                @Override
                public void run() {
                    /* 讀取緩存 */
                    byte[] data = cachingSystem.getFromCache(buildRequest());
                    if(data != null) {
                        //獲得緩存資料
                        final T convertedData = SmartUtils.bytesToResponse(retrofit, responseType, annotations,
                                data);
                        Runnable cacheCallbackRunnable = new Runnable() {
                            @Override
                            public void run() {
                                //存在資料直接回調給調用者,
                                callback.onResponse(baseCall, Response.success(convertedData));
                            }
                        };
                        callbackExecutor.execute(cacheCallbackRunnable);
                    }

                    /* 運作網絡請求 */
                    baseCall.enqueue(new Callback<T>() {
                        @Override
                        public void onResponse(final Call<T> call,final Response<T> response) {
                            Runnable responseRunnable = new Runnable() {
                                @Override
                                public void run() {
                                    if (response.isSuccessful()) {
                                        //儲存資料
                                        byte[] rawData = SmartUtils.responseToBytes(retrofit, response.body(),
                                                responseType(), annotations);
                                        cachingSystem.addInCache(response, rawData);
                                    }
                                    //再一次回調給調用者
                                    callback.onResponse(call, response);
                                }
                            };
                            // Run it on the proper thread
                            callbackExecutor.execute(responseRunnable);
                        }

                        @Override
                        public void onFailure(final Call<T> call, final Throwable t) {
                            Runnable failureRunnable = new Runnable() {
                                @Override
                                public void run() {
                                    callback.onFailure(call,t);
                                }
                            };
                            callbackExecutor.execute(failureRunnable);
                        }

                    });

                }
            };
            Thread enqueueThread = new Thread(enqueueRunnable);
            enqueueThread.start();
        }

        @Override
        public void enqueue(final Callback<T> callback) {
            if(buildRequest().method().equals("GET")){
                //隻對GET請求有用
                enqueueWithCache(callback);
            }else{
                //其他的請求和retrofit一樣
                baseCall.enqueue(new Callback<T>() {
                    @Override
                    public void onResponse(final Call<T> call, final Response<T> response) {
                        callbackExecutor.execute(new Runnable() {
                            @Override
                            public void run() {
                                callback.onResponse(call,response);
                            }
                        });
                    }

                    @Override
                    public void onFailure(final Call<T> call, final Throwable t) {
                        callbackExecutor.execute(new Runnable() {
                            @Override
                            public void run() {
                                callback.onFailure(call,t);
                            }
                        });
                    }
                });
            }
        }

        @Override
        public Type responseType() {
            return responseType;
        }

        @Override
        public Request buildRequest() {
            return request.newBuilder().build();
        }

        @Override
        public SmartCall<T> clone() {
            return new SmartCallImpl<>(callbackExecutor, baseCall.clone(), responseType(),
                    annotations, retrofit, cachingSystem);
        }

        @Override
        public Response<T> execute() throws IOException {
            return baseCall.execute();
        }

        @Override
        public void cancel() {
            baseCall.cancel();
        }
    }
}      

裡面的我加入詳細注釋,應該很容易看懂

主要就是在自定義的SmartCallFactory的call執行個體中自己改寫了請求回調的方式,先看緩存中是否有資料,

有直接回調給調用者,請求資料成功後也會回調給調用者,而對應的使用方法為

Retrofit client = new Retrofit.Builder()
        .baseUrl(HOST)
        .client(okHttpClient)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .addCallAdapterFactory(smartFactory)
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .build();
mGuDong = client.create(MeoHttp.class);      

建構retrofit時應增加一個自定義的CallAdapterFactory

接口調用則為

/**
 * 獲得圖檔清單
 */
@GET("tnfs/api/list")
SmartCall<ImageListBean> getImageList();
      

使用自定義的SmartCall

其他的使用方法和retrofit一緻

demo位址:github記得star給星,如若有問題請留言或者在github上提issue

繼續閱讀