美文网首页 Android知识进阶(遥远的重头开始)
Android-Retrofit2+Rxjava2之网络请求预处

Android-Retrofit2+Rxjava2之网络请求预处

作者: MonkeyLei | 来源:发表于2019-07-18 09:06 被阅读4次

申明一下,目前对Rx家族还没做过什么研究和学习,目前要学习的东西比较多,暂且也是用Rx多一点点。所以就针对使用上做一个总结,记录一下,后续肯定还是要加强研究的。

(这几天也是搞ViewPaper的轮播,主要是Glide动态加载和回收部分,搞了好几天....后面还打算继续研究,做一个自己需求的轮播(重点想搞下图片加载的优化部分 - 我看有的banner是直接一次性创建多个ImageView以及Glide直接初始化好...我觉得这样内存上会有所损耗,当然具体还得再研究下))

那就开始吧。。。Func1主要是请求获取的数据上做预处理,然后返出去给到Subscriber(具体Rxjava的东西后面研究).

So,我们可以自定义Func1,然后重写其R call(T t);方法。看下这个接口的定义:

    public interface Func1<T, R> extends Function {
    R call(T t);
}

这是一个泛型定义,T就是获取的数据(入参), R就是需要返给Subscriber的数据,出参。

1.我们在定义Retrofit相关请求方法的时候可以这样:

image

也就是内部将会把数据转换为我们需要的类型(字符串或者对象,然后返给我们),所以这里的这个T类型就是上面的String,或者HttpResponse<NewsBean>等...

HttpResponse<NewsBean>这个是什么呢? 直接看:

HttpResponse.java - 后台常规返回的数据类型结构基本都是这样(而这个data就是我们真正需要的json数据对象),这样看要清楚多了。。。。

/*
*@Description: 基础网络数据封装
*@Author: hl
*@Time: 2018/9/27 16:16
*/
public class HttpResponse<T> {
    private int code;
    private String message;
    private String request_time;
    private T data;

    public HttpResponse(){}
    public int getCode() {
        return code;
    }

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

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getRequest_time() {
        return request_time;
    }

    public void setRequest_time(String request_time) {
        this.request_time = request_time;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "HttpResponse{" +
                "status=" + code +
                ", msg='" + message + '\'' +
                ", data=" + data +
                ", data=" + request_time +
                '}';
    }
}

现在我们就可以自定义ResponseFunc了。

ResponseFunc.java

import rx.functions.Func1;

/*
*@Description: 请求数据封装 - 增加中间处理过程(可以选择处理或者直接返回)
*@Author: hl
*@Time: 2018/9/29 15:38
* W - 表示输入的数据,也就是请求获得的data数据(对象,字符串等格式)
* T - 表示返回的数据,最终到onNext(Ojbect o)
* W输入数据通过回调CallMe.onCall可以进行中间处理过程,然后返回T
 */
public class ResponseFunc<T, W> implements Func1<HttpResponse<W>, T> {
    private CallMe<T, W> callMe;
    public ResponseFunc(CallMe<T, W> callMe){
        this.callMe = callMe;
    }

    @Override
    public T call(HttpResponse<W> tHttpResponse) {
        if (0 == tHttpResponse.getCode()) {
            ///< 成功
            return callMe.onCall(tHttpResponse.getData(), tHttpResponse.getRequest_time());
        }else if (2 == tHttpResponse.getCode()) {
            ///< Token过期
            throw new ApiException(ApiException.TOKEN);
        }else{
            ///< 其他情况
            throw new ApiException(ApiException.ERROR);
        }
    }

    public interface CallMe<T, W>{
        public T onCall(W data, String requestTime);
    }
}

说明:相当于我拦截了原来的call回调,然后里面进行处理,然后通过自定义的接口回调onCall将真正的数据进行返回...同时还附带了请求时间啥的。可以根据自己的需求进行扩展...

其中像ApiException怎么说了,这里如果我们throw new一个ApiException的话,其结果会返给Subscriber的onError方法。进而我们就可以在自定义的Subscriber中进行异常处理。

到这里相当于就是: Fun1进行了数据预处理, Subscriber进行了异常相关处理

直接看代码:

ApiException.java

  /*
*@Description: 自定义异常封装
*@Author: hl
*@Time: 2018/9/29 15:38
*/
public class ApiException extends RuntimeException{
    public static final int ERROR = 1;
    public static final int TOKEN = 2;
    public static final int NO_NET = 3;
    private static String message;

    public ApiException(int resultCode) {
        this(getApiExceptionMessage(resultCode));
    }

    public ApiException(String detailMessage) {
        super(detailMessage);
    }

    @Override
    public String getMessage() {
        return message;
    }

    /**
     * 由于服务器传递过来的错误信息直接给用户看的话,用户未必能够理解
     * 需要根据错误码对错误信息进行一个转换,在显示给用户
     * @param code
     * @return
     */
    private static String getApiExceptionMessage(int code){
        switch (code) {
            case TOKEN:
                message = "Token过期";
                break;
            case ERROR:
                message = "请求错误";
                break;
            case NO_NET:
                message = "网络未连接";
                break;
            default:
                message = "未知错误";
        }
        return message;
    }
}

然后就可以直接看自定义Subscriber - BaseView(MVP模式相关) 可以删除掉先(我本来打算用来吐司啥的...后面可以扩展....)

BaseSubscribers.java

import rx.Subscriber;

/**
 * Created by hl on 2018/7/4.
 * 1\. 增加具体错误处理回调 - 给需要使用的地方(比如没有重试处理界面的地方)
 */

public abstract class BaseSubscribers<T> extends Subscriber<T> {

    private BaseView baseView;

    private BaseSubscribers() {
    }

    public BaseSubscribers(BaseView baseView) {
        this.baseView = baseView;
    }

    @Override
    public void onStart() {
        super.onStart();
        if (!NetworkUtil.isNetworkConnected(MyApplication.getInstance())) {
            ///< 取消订阅(后续订阅通知则不再重复发送)
            unsubscribe();
            ///< 发送错误事件(一定要加,因为网络请求可能需要错误处理,比如进度条消失等)
            //onError(new Throwable("当前网络不可用!"));
            onError(new ApiException(ApiException.NO_NET));
            //baseView.showToast("当前网络不可用,请检查网络情况!");
            //onCompleted();
        }
    }

    @Override
    public void onError(Throwable e) {
        if (e instanceof Exception) {
            ///< 访问获得对应的Exception
            onErrors(ExceptionHandle.handleException(e));
        } else if (e.getMessage().contains("当前网络不可用")) {
            ///< 将Throwable 和 网络错误的status code返回
            ExceptionHandle.ResponeThrowable responeThrowable = new ExceptionHandle.ResponeThrowable(e, ExceptionHandle.ERROR.NO_NETWORK);
            responeThrowable.message = e.getMessage();
            onErrors(responeThrowable);
        } else {
            ///< 将Throwable 和 未知错误的status code返回
            ExceptionHandle.ResponeThrowable responeThrowable = new ExceptionHandle.ResponeThrowable(e, ExceptionHandle.ERROR.UNKNOWN);
            responeThrowable.message = e.getMessage();
            onErrors(responeThrowable);
        }
    }

    public abstract void onErrors(ExceptionHandle.ResponeThrowable responeThrowable);
}

说明:其实也是相当于拦截了onError,然后自定义onErrors进行错误回调处理。和Fun1一样的玩法...

其中ExceptionHandle是错误类型的相关处理,可以根据需要扩展:

ExceptionHandle.java

import android.util.Log;

import com.google.gson.JsonParseException;

import org.json.JSONException;

import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

import retrofit2.HttpException;

/**
 * 错误异常处理类
 * 1.Retrifit网络错误回调,有时候需要做提示
 */
public class ExceptionHandle {
    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;

    public static ResponeThrowable handleException(Throwable e) {
        ResponeThrowable ex;
        Log.e("tag", "e.toString = " + e.toString());
        if (e instanceof HttpException) {
            HttpException httpException = (HttpException) e;
            ex = new ResponeThrowable(e, ERROR.HTTP_ERROR);
            switch (httpException.code()) {
                case UNAUTHORIZED:
                case FORBIDDEN:
                case NOT_FOUND:
                case REQUEST_TIMEOUT:
                case GATEWAY_TIMEOUT:
                case INTERNAL_SERVER_ERROR:
                case BAD_GATEWAY:
                case SERVICE_UNAVAILABLE:
                default:
                    //ex.code = httpException.code();
                    ex.message = "连接错误";
                    break;
            }
            return ex;
        } else if (e instanceof ServerException) {
            ServerException resultException = (ServerException) e;
            ex = new ResponeThrowable(resultException, resultException.code);
            ex.message = resultException.message;
            return ex;
        } else if (e instanceof JsonParseException
                || e instanceof JSONException
            /*|| e instanceof ParseException*/) {
            ex = new ResponeThrowable(e, ERROR.PARSE_ERROR);
            ex.message = "解析错误";
            return ex;
        } else if (e instanceof ConnectException) {
            ex = new ResponeThrowable(e, ERROR.NETWORK_ERROR);
            ex.message = "连接失败";
            return ex;
        } else if (e instanceof javax.net.ssl.SSLHandshakeException) {
            ex = new ResponeThrowable(e, ERROR.SSL_ERROR);
            ex.message = "证书验证失败";
            return ex;
        }else if (e instanceof ApiException){
            if (e.getMessage().contains("Token")){
                ex = new ResponeThrowable(e, ERROR.TOKEN);
            }else if (e.getMessage().contains("网络未连接")){
                ex = new ResponeThrowable(e, ERROR.NO_NETWORK);
            }else{
                ex = new ResponeThrowable(e, ERROR.UNKNOWN);
            }
            ex.message = e.getMessage();
            return ex;
        }else if (e instanceof SocketTimeoutException){
            ex = new ResponeThrowable(e, ERROR.SOCKET_TIMEOUT_ERROR);
            ex.message = "请求超时";
            return ex;
        }
        else if (e instanceof UnknownHostException){
            ex = new ResponeThrowable(e, ERROR.SOCKET_TIMEOUT_ERROR);
            ex.message = "无法连接服务器";
            return ex;
        }
        else {
            ex = new ResponeThrowable(e, ERROR.UNKNOWN);
            ex.message = e.getMessage();
            return ex;
        }
    }

    /**
     * 约定异常
     */
    public class ERROR {
        /**
         * 网络错误
         */
        public static final int NO_NETWORK = 999;
        /**
         * 未知错误
         */
        public static final int UNKNOWN = 1000;
        /**
         * 解析错误
         */
        public static final int PARSE_ERROR = 1001;
        /**
         * 网络错误
         */
        public static final int NETWORK_ERROR = 1002;
        /**
         * 协议出错
         */
        public static final int HTTP_ERROR = 1003;

        /**
         * 证书出错
         */
        public static final int SSL_ERROR = 1005;

        /**
         * Socket超时
         */
        public static final int SOCKET_TIMEOUT_ERROR = 10060;

        /**
         * TOKEN过期
         */
        public static final int TOKEN = 110110;
    }

    public static class ResponeThrowable extends Exception {
        public int code;
        public String message;

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

    /**
     * ServerException发生后,将自动转换为ResponeThrowable返回
     */
    class ServerException extends RuntimeException {
        int code;
        String message;
    }
}

到此,我们整个自定义Func1和Subscriber就完事了。目前经过一段时间运行,问题不大...性能也没啥特别影响。后续研究的比较明白了,可以再扩展和完善。

用法简单走一走:

image
Subscription subscription = informationService.getCategoryNew(hashMap). ///< getCategory
                subscribeOn(Schedulers.io())
                //.timeout() ///< 这个地方不要设置超时处理,多次调用会闪退,正确的用法是自定义okhttp3去设置超时
                .map(new ResponseFunc<NewsBean, NewsBean>(new ResponseFunc.CallMe<NewsBean, NewsBean>() {
                    @Override
                    public NewsBean onCall(NewsBean data, String requestTime) {
                        return data;
                    }
                }))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new BaseSubscribers<NewsBean>(view) {
                    @Override
                    public void onCompleted() {
                        ///< 如果是刷新才调用刷新结束接口
                        if (bIsRefresh) {
                            if (1 == page) {
                                view.finishRefresh();
                            } else {
                                view.finishLoadMore();
                            }
                        }
                    }

                    @Override
                    public void onErrors(ExceptionHandle.ResponeThrowable responeThrowable) {
                        ///< 接下来就可以根据状态码进行处理...
                        int statusCode = responeThrowable.code;
                        switch (statusCode) {
                            case ExceptionHandle.ERROR.TOKEN:
                                UserInfoControlPresenter.clearAcount();
                                view.showToast("你的账号异常,请重新登录,谢谢!");
                                break;
                            default:
                                view.showToast(responeThrowable.message);
                                break;
                        }
                        ///< 不是刷新,比如首次加载失败或者重试也失败,则显示点击重试界面
                        if (!bIsRefresh) {
                            view.retryDialog();
                        } else {
                            view.onRequestFailer();
                        }
                        onCompleted();
                    }

                    @Override
                    public void onNext(NewsBean o) {
                        if (null == o) {
                            //view.showToast("发生了点小意外!");
                            if (!bIsRefresh) {
                                view.retryDialog();
                            }
                        } else if (null != o.getPosts() && o.getPosts().size() < 1) {
                            view.showToast("我们正努力为您编辑更多资讯!");
                            if (!bIsRefresh) {
                                view.disDialog();
                            } else {
                                view.onRequestFailer();
                            }
                        } else {
                            if (1 == page) {
                                view.resetItemList();
                                view.addNewsItemList(o);
                            } else {
                                view.addNewsItemList(o);
                            }
                            if (!bIsRefresh) {
                                view.disDialog();
                            }
                        }
                    }
                });

什么不精简,封装不太好等,小白都清楚,毕竟小白的起步封装。后面还会继续学习研究,将对刷新和不刷新做通用的网络请求和处理....相信就不用那么多的presenter了。。。

简单记录下。。顺便给自己回忆的理由....爱爱哎!

相关文章

网友评论

    本文标题:Android-Retrofit2+Rxjava2之网络请求预处

    本文链接:https://www.haomeiwen.com/subject/ufhklctx.html