【安卓】rxjava2+retrofit封装处理网络请求全解析

使用rxjava2+retrofit处理网络请求,线程的切换变得十分简单,代码也简洁了很多。但是简介的代码就是对可扩展性有着负面的影响,所以要对rxjava2+retrofit进行一定封装,使结构更清晰,可扩展性更强。这里给出一种可行的封装。

以下均以登陆请求为例子。

  • API地址:http://xxx/user/login
  • Post请求,参数accountpassword均为String

简单封装

  1. 首先定一个retrofit接口:UserService

    public interface UserService {
    
        @FormUrlEncoded
        @POST("/user/login")
        Observable<ResponseBean<UidBean>> login(@Field("account") String account,
                                                @Field("password") String password);
    }

    这里ResponseBean是一个返回值json对应的实体类,服务器返回的json数据格式为{code: xxx, message: xxx, data:xxx}。而UidBean是一个data的具体内容,也是个json实体类,具体是什么无关紧要。

    /**
     * 服务器返回值的实体类
     * Created by stephen on 2017/9/8.
     */
    
    public class ResponseBean<T> {
        @SerializedName("code")
        private int code;
    
        @SerializedName("message")
        private String message;
    
        @SerializedName("data")
        private T data;
    
        public ResponseBean(int code, String message) {
            this.code = code;
            this.message = message;
        }
    
        public int getCode() {
            return code;
        }
    
        public String getMessage() {
            return message;
        }
    
        public T getData() {
            return data;
        }
    
        /**
         * 根据返回码确定API是否请求返回失败 
         *
         * @return 失败返回true, 成功返回false
         */
        public boolean isCodeValid() {
            return code == 0;
        }
    
        @Override
        public String toString() {
            return "code:" + code + ", message:" + message + ",data:" + data.toString();
        }
    }
  2. Oberser类的封装

    这里BaseObserver里直接对Disposable进行处理,内存泄漏什么的省心多了。同时还保存了一个子类可见的errMsg,可以获取自定义的错误类型。

    (关于Disposable可以参考我的这篇文章,如果有哪里理解错了记得帮忙指出哦)。

    public abstract class BaseObserver<T> implements Observer<T> {
    
        protected String errMsg = "";
        private Disposable disposable;
    
        @Override
        public void onSubscribe(Disposable d) {
            disposable = d;
        }
    
        @Override
        public void onNext(T t) {}
    
        @Override
        public void onError(Throwable e) {
            LogUtils.e("Observer.java", e.getMessage() + " " + toastErrHint);
    
            if (!NetworkUtils.isConnected()) {
                errMsg = "网络连接出错,";
            } else if (e instanceof HttpException) {
                errMsg = "网络请求出错,";
            } else if (e instanceof IOException) {
                errMsg = "网络出错,";
            }
    
            disposeIt();
        }
    
        @Override
        public void onComplete() {
            disposeIt();
        }
    
        /**
         * 销毁disposable
         */
        private void disposeIt() {
            if (disposable != null && !disposable.isDisposed()) {
                disposable.dispose();
                disposable = null;
            }
        }
    }
  3. 接口类整合retrofit

    • HttpFactory类里是一些网络交互的基本设置,单独放一个类是为了方便扩展。之后就会发现这样做的好处。
    public class HttpFactory {
    
        private static HttpFactory instance;
        public synchronized static HttpFactory getInstance() {
            return instance == null ? instance = new HttpFactory() : instance;
        }
    
        private Retrofit mRetrofit;
    
        private HttpFactory() {
            //创建一个OkHttpClient并设置超时时间
            OkHttpClient httpClient = new OkHttpClient.Builder()
                    .connectTimeout(AppConfig.DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                    .cookieJar(cookieJar)
                    .build();
    
            mRetrofit = new Retrofit.Builder()
                    .client(httpClient)
                    .baseUrl(AppConfig.URL_BASE)
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .build();
        }
    
        Retrofit getmRetrofit() {
            return mRetrofit;
        }
    }
    • UserApi类将retrofit接口封装成rxjava调用的方法。
    public class APIUser extends BaseAPI {
        private static final APIUser INSTANCE = new APIUser();
    
        public static APIUser getInstance() {
            return INSTANCE;
        }
    
        private UserService userService;
        private Retrofit mRetrofit;
    
        private APIUser() {
            mRetrofit = HttpFactory.getInstance().getmRetrofit();
            userService = mRetrofit.create(UserService.class);
        }
    
        /**
         * 登录 这里为了方便逻辑展示,password直接明文传输
         *
         * @param account  账号
         * @param password 密码
         * @param observer 观察者
         */
        public void login(String account, String password, BaseObserver<UidBean> observer) {
            userService.login(account, password)
                    .subscribeOn(Schedulers.io())
                    .observeOn(Schedulers.computation())
                    .map(new Function<ResponseBean<UidBean>, UidBean>() {
                        @Override
                        public UidBean apply(ResponseBean<UidBean> responseBean) throws Exception {
                            return responseBean.getData();
                        }
                    })
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(observer);
        }
    
    }
  4. 愉快的调用

    APIUser.getInstance().login(account, password, new BaseObserver<UidBean>() {
        @Override
        public void onNext(UidBean uidBean) {
            // your logic code here
            ToastUtils.showShort("登录成功");
        }
    });

添加cookie支持

以上实现了一个简单的封装,现在我们开始加入cookie支持:第一次拿到set-cookie的时候保存cookie,然后再每次request的时候加入cookie。

这里借用了一下GitHub上别人造的轮子,使用了PersistentCookieJarPersistentCookieJarClearableCookieJar的一个具体实现,利用SharedPreferences保存cookie。具体使用方法如下:

  • 添加gradle依赖
  • HttpFactory添加cookieJar
public class HttpFactory {

    private static HttpFactory instance;
    public synchronized static HttpFactory getInstance() {
        return instance == null ? instance = new HttpFactory() : instance;
    }

    private Retrofit mRetrofit;
    private ClearableCookieJar cookieJar;

    private HttpFactory() {
        //设置cookie
        cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor());
        //创建一个OkHttpClient并设置超时时间
        OkHttpClient httpClient = new OkHttpClient.Builder()
                .connectTimeout(AppConfig.DEFAULT_TIMEOUT, TimeUnit.SECONDS)
                .cookieJar(cookieJar)
                .build();

        mRetrofit = new Retrofit.Builder()
                .client(httpClient)
                .baseUrl(AppConfig.URL_BASE)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
    }

    Retrofit getmRetrofit() {
        return mRetrofit;
    }

    /**
     * 退出登录后清除cookie
     */
    public void clearCookie() {
        cookieJar.clear();
    }
}

这里HttpFactory还提供了一个对外接口,可以在退出登录后调用清除clearCookie()cookie。

现在每次处理请求的时候,ClearableCookieJar就会帮你打点好所有的cookie验证相关信息。

添加错误码统一处理

很多时候服务器返回的json数据格式会有一个统一的格式,比如以上说的形式:{code: xxx, message: xxx, data:xxx}。带有返回状态码和错误信息,如果对于每个code我们都在Observer中的onNext处理,显然很麻烦很冗余一点也不优雅,可扩展性也很差。比如服务器新添加一个状态码,所有的代码都要改,不切实际嘛。所以我们下面要做的就是对错误码添加统一处理。

实现思路:对于json数据response的时候,我们都会用GsonConverterFactory来将json转换为java对象,那么我们就可以利用GsonConverterFactory来统一处理。我们来重新定义一个类实现GsonConverterFactory的关键方法。因为GsonConverterFactory是final的,不可继承,所以这里会重新定义三个类:CustomGsonConverterFactoryCustomGsonResponseBodyConverterCustomGsonRequestBodyConverter分别对应默认的GsonConverterFactoryGsonResponseBodyConverterGsonRequestBodyConverter

  • CustomGsonConverterFactory

    /**
     * 自定义的GsonConverterFactory
     * 主要是重写了responseBodyConverter类
     * Created by stephen on 2017/10/2.
     */
    public final class CustomGsonConverterFactory extends Converter.Factory {
        /**
         * Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
         * decoding from JSON (when no charset is specified by a header) will use UTF-8.
         */
        public static CustomGsonConverterFactory create() {
            return create(new Gson());
        }
    
        /**
         * Create an instance using {@code gson} for conversion. Encoding to JSON and
         * decoding from JSON (when no charset is specified by a header) will use UTF-8.
         */
        public static CustomGsonConverterFactory create(Gson gson) {
            return new CustomGsonConverterFactory(gson);
        }
    
        private final Gson gson;
    
        private CustomGsonConverterFactory(Gson gson) {
            if (gson == null) throw new NullPointerException("gson == null");
            this.gson = gson;
        }
    
        @Override
        public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
            TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
            return new CustomGsonResponseBodyConverter<>(gson, adapter);
        }
    
        @Override
        public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations,
                                                              Annotation[] methodAnnotations, Retrofit retrofit) {
            TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
            return new CustomGsonRequestBodyConverter<>(gson, adapter);
        }
    }
  • CustomGsonRequestBodyConverter。这个类没有改动,直接copy了GsonRequestBodyConverter的源码。

    final class CustomGsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
        private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
        private static final Charset UTF_8 = Charset.forName("UTF-8");
    
        private final Gson gson;
        private final TypeAdapter<T> adapter;
    
        CustomGsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
            this.gson = gson;
            this.adapter = adapter;
        }
    
        @Override public RequestBody convert(T value) throws IOException {
            Buffer buffer = new Buffer();
            Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
            JsonWriter jsonWriter = gson.newJsonWriter(writer);
            adapter.write(jsonWriter, value);
            jsonWriter.close();
            return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
        }
    }
  • CustomGsonResponseBodyConverter。重信写convert方法。这里直接将返回值转换为responseBean,并根据返回值判断是否返回成功(这里根据responseBean.isCodeValid()判断)。如果返回不成功,可以自定义一些exception,并抛出异常。比如我这里的throw new APIException

    final class CustomGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
        private final Gson gson;
        private final TypeAdapter<T> adapter;
        private static final Charset UTF_8 = Charset.forName("UTF-8");
    
        CustomGsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
            this.gson = gson;
            this.adapter = adapter;
        }
    
        @Override
        public T convert(ResponseBody value) throws IOException {
            String response = value.string();
            //关键代码
            ResponseBean responseBean = gson.fromJson(response, ResponseBean.class);
            if (!responseBean.isCodeValid()) {
                value.close();
                throw new APIException(responseBean.getCode(), responseBean.getMessage());
            }
    
            MediaType contentType = value.contentType();
            Charset charset = contentType != null ? contentType.charset(UTF_8) : UTF_8;
            InputStream inputStream = new ByteArrayInputStream(response.getBytes());
            Reader reader = new InputStreamReader(inputStream, charset);
            JsonReader jsonReader = gson.newJsonReader(reader);
    
            try {
                return adapter.read(jsonReader);
            } finally {
                value.close();
            }   
        }
    }   
  • 同样的自定义的exception可以在BaseObserveronError中捕获然后获取不同的errMsg

    @Override
    public void onError(Throwable e) {
        LogUtils.e("Observer.java", e.getMessage() + " " + toastErrHint);
    
        if (!NetworkUtils.isConnected()) {
            errMsg = "网络连接出错,";
        } else if (e instanceof APIException) {
            APIException exception = (APIException) e;
            errMsg = exception.getMessage();
        } else if (e instanceof HttpException) {
            errMsg = "网络请求出错,";
        } else if (e instanceof IOException) {
            errMsg = "网络出错,";
        }
    }

总结

好了至此,封装已经可以达到了一定的可扩展性,使用也很方便。事实上你还可以在BaseObserver中添加一些其他的东西,比如加载动画等等。

大家有意见提出来一起探讨,共同进步。:)

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
使用RxJava2 + Retrofit2 + OKHttp进行POST请求,可以按照以下步骤进行: 1. 添加依赖 在项目的build.gradle文件中添加以下依赖: ``` dependencies { implementation 'io.reactivex.rxjava2:rxjava:2.2.19' implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.9.1' } ``` 2. 创建Service接口 创建一个接口,用于定义POST请求的方法。例如: ``` public interface ApiService { @POST("login") Observable<LoginResponse> login(@Body LoginRequest request); } ``` 3. 创建Retrofit对象 在Application类或其他初始化类中,创建Retrofit对象: ``` public class MyApp extends Application { private static ApiService apiService; @Override public void onCreate() { super.onCreate(); // 创建OkHttpClient对象 OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .build(); // 创建Retrofit对象 Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://example.com/api/") .client(client) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); apiService = retrofit.create(ApiService.class); } public static ApiService getApiService() { return apiService; } } ``` 4. 发起POST请求 在需要发起POST请求的地方,可以使用以下代码: ``` LoginRequest request = new LoginRequest(); request.setUsername("admin"); request.setPassword("123456"); MyApp.getApiService().login(request) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<LoginResponse>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(LoginResponse response) { // 处理响应数据 } @Override public void onError(Throwable e) { // 处理异常 } @Override public void onComplete() { } }); ``` 上述代码中,我们首先创建了一个LoginRequest对象,用于存储要发送的数据。然后调用MyApp.getApiService().login(request)方法,发起POST请求。在这里,我们使用了RxJava2的Observable对象,将请求结果封装为一个可观察对象。使用subscribeOn(Schedulers.io())指定在IO线程中进行网络请求,使用observeOn(AndroidSchedulers.mainThread())指定在主线程中处理响应。最后通过subscribe方法订阅请求结果,处理响应数据或异常。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值