rxjava2+retrofit2+okhttp+rxCache+rxlifecycle2构建通用网络请求

用到的jar包版本
    compile "com.squareup.okhttp3:okhttp:3.9.0"
    compile "com.squareup.okhttp3:logging-interceptor:3.9.0"
    compile "com.squareup.retrofit2:retrofit:2.3.0"
    compile "com.squareup.retrofit2:converter-gson:2.3.0"
    compile "com.squareup.retrofit2:adapter-rxjava2:2.3.0"
    compile "io.reactivex.rxjava2:rxjava:2.0.6"
    compile "io.reactivex.rxjava2:rxandroid:2.0.6"
    compile "com.trello.rxlifecycle2:rxlifecycle:2.2.1"
    compile "com.trello.rxlifecycle2:rxlifecycle-components:2.2.1"
    compile "com.github.VictorAlbertos.RxCache:runtime:1.8.1-2.x"
    compile 'com.github.VictorAlbertos.Jolyglot:gson:0.0.3'

话不多说直接上代码

  1. 管理类
public class ApiManager {
   private static ApiManager apiManager;
   public GankApi gankApi;
   public GankApiCacheProvider gankApiCacheProvider;

   public static ApiManager getInstance() {
       if (apiManager == null) {
           synchronized (ApiManager.class) {
               if (apiManager == null) {
                   apiManager = new ApiManager();
               }
           }
       }
       return apiManager;
   }

   private ApiManager() {
       OkHttpClient okHttpClient = newOkHttpClient();
       gankApi = new Retrofit.Builder()
               .client(okHttpClient)
               .addConverterFactory(GsonConverterFactory.create())
               .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
               .baseUrl(ApiConfig.BASE_GANK_API_URL)
               .build()
               .create(GankApi.class);
       File file = FsUtils.createOnNotFound(new File(App.getInstance().getCacheDir(), "RxCache"));
       RxCache rxCache = new RxCache.Builder()
               .setMaxMBPersistenceCache(50)
               .persistence(file, new GsonTSpeaker());//解析缓存的一个类
       gankApiCacheProvider = rxCache.using(GankApiCacheProvider.class);
   }

   private OkHttpClient newOkHttpClient() {
       OkHttpClient.Builder builder = new OkHttpClient.Builder()
               .cookieJar(new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(App.getContext())))//cookice设置
               .connectTimeout(10, TimeUnit.SECONDS)
               .readTimeout(20, TimeUnit.SECONDS)
               .writeTimeout(20, TimeUnit.SECONDS)
               .retryOnConnectionFailure(true)
               .addInterceptor(new GzipRequestInterceptor())//使用gizp压缩数据,可以减少流量,也可以不加
               .addInterceptor(new HeaderInterceptor());//头部信息拦截器,也可以不加
       if (BuildConfig.DEBUG) {//请求日志
           HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
           loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
           builder.addInterceptor(loggingInterceptor);
           StethoUtils.addNetworkInterceptor(builder);
       }
       return builder.build();
   }
}

import com.google.gson.Gson;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import io.victoralbertos.jolyglot.JolyglotGenerics;
import io.victoralbertos.jolyglot.Types;

/**
 * Created by Xiong Ke on 2017/8/23.
 */

public class GsonTSpeaker implements JolyglotGenerics {

    private final Gson gson;

    public GsonTSpeaker(Gson gson) {
        this.gson = gson;
    }

    public GsonTSpeaker() {
        this.gson = new Gson();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toJson(Object src) {
        return gson.toJson(src);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toJson(Object src, Type typeOfSrc) {
        return gson.toJson(src, typeOfSrc);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  T fromJson(String json, Class classOfT) throws RuntimeException {
        Type genType = classOfT.getGenericSuperclass();
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        return gson.fromJson(json, params[0]);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  T fromJson(String json, Type typeOfT) throws RuntimeException {
        Type[] params = ((ParameterizedType) typeOfT).getActualTypeArguments();
        return gson.fromJson(json, params[0]);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  T fromJson(File file, Class classOfT) throws RuntimeException {
        BufferedReader reader = null;

        try {
            reader = new BufferedReader(new FileReader(file.getAbsoluteFile()));

            Type genType = classOfT.getGenericSuperclass();
            Type[] params = ((ParameterizedType) genType).getActualTypeArguments();

            T object = gson.fromJson(reader, params[0]);
            reader.close();
            return object;
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException i) {
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public  T fromJson(File file, Type typeOfT) throws RuntimeException {
        BufferedReader reader = null;

        try {
            reader = new BufferedReader(new FileReader(file.getAbsoluteFile()));
            Type[] params = ((ParameterizedType) typeOfT).getActualTypeArguments();
            T object = gson.fromJson(reader, params[0]);
            reader.close();
            return object;
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException i) {
                }
            }
        }
    }

    @Override
    public GenericArrayType arrayOf(Type componentType) {
        return Types.arrayOf(componentType);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ParameterizedType newParameterizedType(Type rawType, Type... typeArguments) {
        return Types.newParameterizedType(rawType, typeArguments);
    }
}
package com.xk.gvido.app.model.net.interceptor;

import android.support.annotation.NonNull;

import java.io.IOException;

import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okio.Buffer;
import okio.BufferedSink;
import okio.GzipSink;
import okio.Okio;

/**
 * Created by Xiong Ke on 2017/8/23.
 */

public class GzipRequestInterceptor implements Interceptor {
    @Override
    public Response intercept(@NonNull Chain chain) throws IOException {
        Request originalRequest = chain.request();
        if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) {
            return chain.proceed(originalRequest);
        }
        Request compressedRequest = originalRequest.newBuilder()
                .header("Content-Encoding", "gzip")
                .method(originalRequest.method(), forceContentLength(gzip(originalRequest.body())))
                .build();
        return chain.proceed(compressedRequest);
    }

    private RequestBody forceContentLength(final RequestBody requestBody) throws IOException {
        final Buffer buffer = new Buffer();
        requestBody.writeTo(buffer);
        return new RequestBody() {
            @Override
            public MediaType contentType() {
                return requestBody.contentType();
            }

            @Override
            public long contentLength() {
                return buffer.size();
            }

            @Override
            public void writeTo(@NonNull BufferedSink sink) throws IOException {
                sink.write(buffer.snapshot());
            }
        };
    }


    private RequestBody gzip(final RequestBody body) {
        return new RequestBody() {
            @Override
            public MediaType contentType() {
                return body.contentType();
            }

            @Override
            public long contentLength() {
                return -1; // We don't know the compressed length in advance!
            }

            @Override
            public void writeTo(@NonNull BufferedSink sink) throws IOException {
                BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
                body.writeTo(gzipSink);
                gzipSink.close();
            }
        };
    }
}

提几个我遇到过的错误的心得体会:

a. 项目cookice丢失
    OkHttpClient 多次创建导致cookice丢失,一个项目只能实例化一个OkHttpClient
b. 提取缓存是报了一个转换异常
    日志我就不贴了,加入GsonTSpeaker转换一下就行,(只针对json,别的数据格式不支持)
c. 缓存创建目录设置在app内部缓存里面,防止无法创建目录引起异常
d. rxcache主要就是防止多次刷新请求服务器,浪费资源


  1. 错误处理类
public class ApiError {
    //HTTP错误
    private static final int UNAUTHORIZED = 401;
    private static final int FORBIDDEN = 403;
    private static final int NOT_FOUND = 404;
    private static final int REQUEST_TIMEOUT = 408;
    private static final int INTERNAL_SERVER_ERROR = 500;
    private static final int BAD_GATEWAY = 502;
    private static final int SERVICE_UNAVAILABLE = 503;
    private static final int GATEWAY_TIMEOUT = 504;

    //解析错误
    private static final int PARSE_ERROR = 1000;
    //网络错误
    private static final int NETWORK_ERROR = 1001;
    //协议出错
    private static final int HTTP_ERROR = 1002;
    //服务器出错
    private static final int RESULT_ERROR = 1003;
    //未知错误
    public static final int UNKNOWN = 1004;
    //緩存错误
    private static final int CACHE_ERROR = 1005;

    public static ApiException handleThrowable(Throwable e) {
        ApiException ex;
        if (e instanceof HttpException) {             //HTTP错误
            HttpException httpException = (HttpException) e;
            ex = new ApiException(e, HTTP_ERROR);
            switch (httpException.code()) {
                case UNAUTHORIZED:
                case FORBIDDEN:
                case NOT_FOUND:
                case REQUEST_TIMEOUT:
                    ex.setCode(REQUEST_TIMEOUT);
                    ex.setDisplayMessage(StringUtils.getString(R.string.error_network_request_timeout));
                case GATEWAY_TIMEOUT:
                case INTERNAL_SERVER_ERROR:
                case BAD_GATEWAY:
                case SERVICE_UNAVAILABLE:
                default:
                    ex.setDisplayMessage(StringUtils.getString(R.string.error_network_network_error));
                    break;
            }
            return ex;
        } else if (e instanceof JsonParseException
                || e instanceof JSONException
                || e instanceof ParseException) {
            ex = new ApiException(e, PARSE_ERROR);
            ex.setDisplayMessage(StringUtils.getString(R.string.error_parsing_error));
        } else if (e instanceof ConnectException) {
            ex = new ApiException(e, HTTP_ERROR);
            ex.setDisplayMessage(StringUtils.getString(R.string.error_network_connection_failure));
        } else if (e instanceof ExecutionException
                || e instanceof InterruptedException) {
            ex = new ApiException(e, UNKNOWN);
            ex.setDisplayMessage(StringUtils.getString(R.string.error_network_get_image_failure));
        } else if (e instanceof RxCacheException) {
            ex = new ApiException(e, CACHE_ERROR);
            ex.setDisplayMessage(StringUtils.getString(R.string.error_network_cache_failure));
        } else if (e instanceof CompositeException) {
            CompositeException compositeE = (CompositeException) e;
            ex = new ApiException(e);
            for (Throwable throwable : compositeE.getExceptions()) {
                if (throwable instanceof SocketTimeoutException) {
                    ex.setCode(NETWORK_ERROR);
                    ex.setDisplayMessage(StringUtils.getString(R.string.error_network_network_error));
                } else if (throwable instanceof ConnectException) {
                    ex.setCode(NETWORK_ERROR);
                    ex.setDisplayMessage(StringUtils.getString(R.string.error_network_network_error));
                } else if (throwable instanceof UnknownHostException) {
                    ex.setCode(NETWORK_ERROR);
                    ex.setDisplayMessage(StringUtils.getString(R.string.error_network_network_error));
                } else if (throwable instanceof RxCacheException) {
                    ex.setCode(CACHE_ERROR);
                    ex.setDisplayMessage(StringUtils.getString(R.string.error_network_network_error));
                } else if (throwable instanceof MalformedJsonException) {
                    ex.setCode(PARSE_ERROR);
                    ex.setDisplayMessage(StringUtils.getString(R.string.error_parsing_error));
                }
            }
        } else {
            ex = new ApiException(e, UNKNOWN);
            ex.setDisplayMessage(e.getMessage());
        }
        return ex;
    }
public class ApiException extends RuntimeException {
    private int code;
    private String displayMessage;

    public ApiException(Throwable throwable) {
        super(throwable);
    }

    public ApiException(Throwable throwable, int code) {
        super(throwable);
        this.code = code;
    }

    public void setDisplayMessage(String displayMessage) {
        this.displayMessage = displayMessage;
    }

    public String getDisplayMessage() {
        return displayMessage;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }
}

错误你可以自己加,缺少的字符串我就不贴了


  1. rx调度线程的工具类
public class RxSchedulersHelper {

    public static  ObservableTransformer io_main() {
        return upstream -> upstream.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .unsubscribeOn(Schedulers.io());
    }

    public static  ObservableTransformer io_io() {
        return upstream -> upstream.subscribeOn(Schedulers.io())
                .observeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io());
    }

    public static  ObservableTransformer main_main() {
        return upstream -> upstream.subscribeOn(AndroidSchedulers.mainThread())
                .observeOn(AndroidSchedulers.mainThread())
                .unsubscribeOn(Schedulers.io());
    }

    public static  ObservableTransformer addDisposable(final C context) {
        if (context instanceof BaseFragment) {
            BaseFragment baseFragment = (BaseFragment) context;
            return baseFragment.bindToLifecycle();
        } else if (context instanceof BasePreferenceFragment) {
            BasePreferenceFragment basePreferenceFragment = (BasePreferenceFragment) context;
            return basePreferenceFragment.bindToLifecycle();
        } else if (context instanceof BaseActivity) {
            BaseActivity baseActivity = (BaseActivity) context;
            return baseActivity.bindToLifecycle();
        } else {
            return upstream -> upstream;
        }
    }

    public static  ObservableTransformer errorResult() {
        return upstream -> upstream.onErrorResumeNext((Function>)
                throwable -> {
                    return Observable.error(ApiError.handleThrowable(throwable));
                });
    }

    public static  Observable createData(final T t) {
        return Observable.create(observableEmitter -> {
            if (!observableEmitter.isDisposed()) {
                try {
                    observableEmitter.onNext(t);
                    observableEmitter.onComplete();
                } catch (Exception e) {
                    observableEmitter.onError(e);
                }
            }
        });
    }
}
说明

a. io_main,io_io,main_main线程的调度方法
b. addDisposable这个方法很重要,为了防止请求完成 界面销毁了,造成的内存泄漏需要说明的是BaseFragment要继承RxFragment,BasePreferenceFragment,BaseActivity也都要继承RxPreferenceFragment,RxAppCompatActivity
c. errorResult发生错误将会发送一个消息到error方法
d. createData创建一个Observable对象


  1. 数据结果处理类
public class ApiResultHandler {

    public static  ObservableTransformer, T> handleGanHuoResult() {
        return upstream -> upstream.flatMap((Function, ObservableSource>) ganHuoResponse -> {
            if (!ganHuoResponse.isError()) {
                return RxSchedulersHelper.createData(ganHuoResponse.getResults());
            } else {
                return Observable.error(new Throwable("服务器返回error"));
            }
        });
    }
}

  1. 接口API
public interface GankApi {
    /**
     * 福利列表
     */
    @GET("data/福利/{count}/{pageIndex}")
    Observable>> getGanHuoWelfareApiCall(@Path("count") @IntRange(from = 1) int count,
                                                                                @Path("pageIndex") @IntRange(from = 1) int pageIndex);

    /**
     * 休息视频
     */
    @GET("data/休息视频/{count}/{pageIndex}")
    Observable>> getGanHuoVideoApiCall(@Path("count") @IntRange(from = 1) int count,
                                                                            @Path("pageIndex") @IntRange(from = 1) int pageIndex);
}


6.缓存API

public interface GankApiCacheProvider {
    /**
     * 福利列表
     *
     * @param observable
     * @param page 緩存key
     * @param evictProvider 清楚缓存
     * @return
     */
    @Expirable(value = false)
    @LifeCache(duration = 6, timeUnit = TimeUnit.HOURS)
    Observable>> getGanHuoWelfareApiCall(Observable>> observable, DynamicKey page, EvictProvider evictProvider);

    /**
     * 休息视频
     *
     * @param observable
     * @param page 緩存key
     * @param evictProvider 清楚缓存
     * @return
     */
    @Expirable(value = false)
    @LifeCache(duration = 6, timeUnit = TimeUnit.HOURS)
    Observable>> getGanHuoVideoApiCall(Observable>> observable, DynamicKey page, EvictProvider evictProvider);
}

7.处理类

public class GankApiHelper {
    public static  Observable> getGanHuoWelfareApiCall(final T context, @IntRange(from = 1) int count,
                                                                            @IntRange(from = 1) int pageIndex, boolean isClearCache) {
        return ApiManager.getInstance().gankApiCacheProvider
                .getGanHuoWelfareApiCall(ApiManager.getInstance().gankApi.getGanHuoWelfareApiCall(count, pageIndex),
                        new DynamicKey(pageIndex), new EvictDynamicKey(isClearCache))
                .compose(ApiResultHandler.handleGanHuoResult())
                .compose(RxSchedulersHelper.addDisposable(context))
                .compose(RxSchedulersHelper.errorResult())
                .compose(RxSchedulersHelper.io_main());
    }

    public static  Observable> getGanHuoVideoApiCall(final T context, @IntRange(from = 1) int count,
                                                                        @IntRange(from = 1) int pageIndex, boolean isClearCache) {
        return ApiManager.getInstance().gankApiCacheProvider
                .getGanHuoVideoApiCall(ApiManager.getInstance().gankApi.getGanHuoVideoApiCall(count, pageIndex),
                        new DynamicKey(pageIndex), new EvictDynamicKey(isClearCache))
                .compose(ApiResultHandler.handleGanHuoResult())
                .compose(RxSchedulersHelper.addDisposable(context))
                .compose(RxSchedulersHelper.errorResult())
                .compose(RxSchedulersHelper.io_main());
    }
}
说明

由于我使用了rxcache所以为了方便使用写了一个helper类做一下处理工作,如果你的项目里没用rxcache就可以删掉GankApiCacheProvider


8.调用

 Observable.zip(GankApiHelper.getGanHuoVideoApiCall(this, REQ_COUNT, currentPage, isClearCache),
                GankApiHelper.getGanHuoWelfareApiCall(this, REQ_COUNT, currentPage, isClearCache),
                (videoBeen, welfareDatas) -> {
                    List ganHuoBeanList = new ArrayList<>();
                    if (videoBeen.size() == REQ_COUNT && welfareDatas.size() == REQ_COUNT) {
                        for (int i = 0; i < REQ_COUNT; i++) {
                            ganHuoBeanList.add(GanHuoBean.newInstance(videoBeen.get(i),
                                    welfareDatas.get(i)));
                        }
                    }
                    return ganHuoBeanList;
                }).subscribe(new RxSubscriber>() {

            @Override
            public void onSubscribe(Disposable d) {
                super.onSubscribe(d);
                if (isRefresh) {
                    swipeRefreshLayout.setRefreshing(true);
                }
            }

            @Override
            public void rxOnNext(List ganHuoBeans) {
                gankPictureAdapter.loadMoreComplete();
                if (ganHuoBeans.size() < REQ_COUNT) {
                    gankPictureAdapter.loadMoreEnd();
                }
                if (isRefresh) {
                    gankPictureAdapter.setNewData(ganHuoBeans);
                    swipeRefreshLayout.setRefreshing(false);
                } else {
                    gankPictureAdapter.addData(ganHuoBeans);
                }
                if (gankPictureAdapter.getData().isEmpty()) {
                    loadingLayout.showEmpty();
                } else {
                    loadingLayout.showContent();
                }
                saveMeiZiInDb(ganHuoBeans);
            }

            @Override
            public void rxOnError(ApiException apiException) {
                if (gankPictureAdapter.getData().isEmpty()) {
                    loadingLayout.setErrorText(apiException.getDisplayMessage());
                    loadingLayout.showError();
                } else {
                    gankPictureAdapter.loadMoreFail();
                }
                swipeRefreshLayout.setRefreshing(false);
            }
        });
说明

我是将两个请求的结果转换成一个新的数据结构,所以是这样写的


这样写的话,方便解耦易于扩展。写完了大致就这样,如果写的有问题欢迎在评论区指正.

你可能感兴趣的:(rxjava2+retrofit2+okhttp+rxCache+rxlifecycle2构建通用网络请求)