美文网首页
RxAndroid+okHttp进行网络请求

RxAndroid+okHttp进行网络请求

作者: 飞奔吧牛牛 | 来源:发表于2018-12-20 19:12 被阅读0次

    前言

    之前也做过OkHttp和RxJava方面的学习,只是简单总结了以下,但是并没有将两者真正结合起来使用过。
    众所周知,OkHttp请求后返回的结果回调并不是在主线程中的,而RxJava却可以做线程的调度。所以,我们可以使用RxJava在io线程中请求网络,获取结果后,在主线程中处理。

    服务端(SpringMVC)

    我们首先约定数据格式,data字段中放Object类型的数据,客户端根据约定获取数据。
    code=1时处理成功,msg=success,并返回data
    code=0时处理失败,msg可以附上失败原因。

    {"code":1,"msg":"success","data":{}}
    

    再创建两个Model类

    public class BaseResponse {
        private int code;
        private String msg;
        private Object data;
    
        public BaseResponse() {
            super();
        }
    
        public BaseResponse(int code, String msg, Object data) {
            super();
            this.code = code;
            this.msg = msg;
            this.data = data;
        }
    
            // getter and setter...
    }
    
    
    public class OkBean {
        private int id;
        private String name;
        private List<String> values;
        private Date date;
    
        public OkBean() {
            super();
        }
        
        public OkBean(int id, String name, List<String> values, Date date) {
            super();
            this.id = id;
            this.name = name;
            this.values = values;
            this.date = date;
        }
    
         // getter and setter...
    }
    

    在控制器中写两个方法,返回json格式数据:

    @RequestMapping("ok")
    @Controller()
    public class HttpTest {
        
        @ResponseBody
        @RequestMapping(value="get")
        public String getJson() {
            List<String> values = new ArrayList<String>();
            values.add("aaa");
            values.add("bbb");
            values.add("ccc");
            OkBean okBean = new OkBean(1, "okhttp", values, new Date());
            BaseResponse result = new BaseResponse(1, "success", okBean);
            return JSON.toJSONString(result);
        }
        
        @ResponseBody
        @RequestMapping(value="get2")
        public String getJson2() {
            List<String> values = new ArrayList<String>();
            values.add("aaa");
            values.add("bbb");
            values.add("ccc");
            OkBean okBean = new OkBean(1, "okhttp", values, new Date());
            return JSON.toJSONString(okBean);
        }
        
    }
    

    第二个方法未按约定数据格式返回数据,这个是为了一会儿的测试使用。

    客户端

    说明:

    在使用高版本的okhttp和okio时,报了
    java.lang.NoClassDefFoundError: didn't find class kotlin.text.charsets;异常,但使用了一些网上的方法后并没有解决。所以现在使用较低版本的okhttp和okio,可以去maven上去下载:https://mvnrepository.com/ ,打开网址后分别输入okhttp和okio选择版本号下载即可。
    这是我这次测试使用的jar包:

    QQ截图20181220182108.png

    经过这个小插曲就要开始写代码了。

    先定义异常类,来封装异常类型
    
    public class OkhttpException extends Exception {
    
        private int code;
    
        public OkhttpException(int code) {
            this.code = code;
        }
    
        public int getCode() {
            return code;
        }
    
    }
    
    定义异常类型,大致可以分为以下类型,已在注释中标注

    1.有网络但,请求异常
    2.网络链接失败
    3.返回格式出错

    public class ExceptionCode {
        //有网络,但请求失败,如接口地址写错404,服务器异常500等。
        public static final int CODE_REQUEST_FAIL = 1;
        //没网,无法链接服务器等
        public static final int CODE_NET_ERROR = 2;
        //成功返回数据,但数据格式不对,json解析失败
        public static final int CODE_DATA_FORMAT = 3;
    }
    
    
    定义回调类,我们要在这个回调中集中处理各种情况
    
    public interface RxHttpHandler {
        void onStart();
        void onSuccess(String s);
        void onError(int code);
        void onFinish();
    }
    
    
    实现回调类,这个类继承上面的RxHttpHandler接口 ,只做json格式的校验,并将所需的数据类型自动回调给调用方。
    
    public class RxJsonHttpHandler<T extends BaseResponseBean> implements RxHttpHandlerInte {
    
        private Class<T> clazz;
    
        public RxJsonHttpHandler(Class<T> clazz) {
            this.clazz = clazz;
        }
    
        @Override
        public void onStart() {
    
        }
    
        @Override
        public void onSuccess(String s) {
            T t = JSON.parseObject(s, clazz);
            if (t != null) {
                onSuccess(t);
            } else {
                onError(ExceptionCode.CODE_DATA_FORMAT);
            }
        }
    
        public void onSuccess(T t) {
    
        }
    
        @Override
        public void onError(int code) {
    
        }
    
        @Override
        public void onFinish() {
    
        }
    
    }
    
    okhttp,get和post方法
    import java.util.Map;
    
    import okhttp3.Callback;
    import okhttp3.FormBody;
    import okhttp3.OkHttpClient;
    import okhttp3.Request;
    
    public class HttpUtil {
        private OkHttpClient client;
    
        public HttpUtil () {
            client = new OkHttpClient();
        }
    
        public void get(String url, Callback callback) {
            Request request = new Request.Builder().url(url).build();
            client.newCall(request).enqueue(callback);
        }
        
        public void post(String url, Map<String, String> requestParams, Callback callback) {
            FormBody.Builder bodyBuilder = new FormBody.Builder();
            for (Map.Entry<String, String> entry : requestParams.entrySet()) {
                bodyBuilder.add(entry.getKey(), entry.getValue());
            }
            Request request = new Request.Builder()
                    .url(url)
                    .post(bodyBuilder.build())
                    .build();
            client.newCall(request).enqueue(callback);
        }
    
    }
    
    Rx代码实现异步功能。终于到这一步了~~

    这里第一个get方法中做网络请求,将各种事件 “发射” 出去,返回Observable<String>对象,由第二个get方法来调用。

    import java.io.IOException;
    
    import io.reactivex.Observable;
    import io.reactivex.ObservableEmitter;
    import io.reactivex.ObservableOnSubscribe;
    import io.reactivex.Observer;
    import io.reactivex.android.schedulers.AndroidSchedulers;
    import io.reactivex.annotations.NonNull;
    import io.reactivex.disposables.Disposable;
    import io.reactivex.schedulers.Schedulers;
    import okhttp3.Call;
    import okhttp3.Callback;
    import okhttp3.Response;
    
    public class RxHttp {
        private HttpUtil httpUtil = new HttpUtil();
    
        public Observable<String> get(final String url) {
            return Observable.create(new ObservableOnSubscribe<String>() {
                @Override
                public void subscribe(@NonNull final ObservableEmitter<String> emitter) throws Exception {
                    httpUtil.get(url, new Callback() {
    
                        @Override
                        public void onFailure(Call call, IOException e) {
                            emitter.onError(new OkhttpException(ExceptionCode.CODE_NET_ERROR));
                        }
    
                        @Override
                        public void onResponse(Call call, Response response) throws IOException {
                            if (response.isSuccessful()) {
                                String body = response.body().string();
                                Log.e("responseJson", body);
                                emitter.onNext(body);
                                emitter.onComplete();
                            } else {
                                emitter.onError(new OkhttpException(ExceptionCode.CODE_REQUEST_FAIL));
                            }
                            
                        }
                    });
                }
            });
        }
    
        public void get(String url, final RxHttpHandlerInte handler) {
            get(url)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Observer<String>() {
                        @Override
                        public void onSubscribe(@NonNull Disposable disposable) {
                            handler.onStart();
                        }
    
                        @Override
                        public void onNext(@NonNull String s) {
                            handler.onSuccess(s);
                        }
    
                        @Override
                        public void onError(@NonNull Throwable throwable) {
                            //强转为我们自定义的 OkhttpException,onError中传入异常类型
                            if (throwable.getClass().equals(OkhttpException.class)) {
                                int code = ((OkhttpException) throwable).getCode();
                                handler.onError(code);
                                handler.onFinish();
                            }
                        }
    
                        @Override
                        public void onComplete() {
                            handler.onFinish();
                        }
                    });
        }
    }
    
    
    下面来测试三种情况,
    1.正常返回数据。2.请求网址出错(其实就是404,这个和500测试结果是一样的)3.数据格式出错(这里用的就是 http://192.168.1.111:8080/ssh/ok/get2 这个地址)。

    最后终于开始调用写页面逻辑了

    xml页面

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="get"
            android:text="get" />
    
        <EditText
            android:id="@+id/et_url"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="http://192.168.1.111:8080/ssh/ok/get" />
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="get2"
            android:text="get2" />
    </LinearLayout>
    
    

    activity

    
    public class OkHttpActivity extends AppCompatActivity {
    
        private static final String TAG = "Rx_Ok";
        private RxHttp mRxHttp;
        private EditText mEtUrl;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_ok_http);
            mEtUrl = ((EditText) findViewById(R.id.et_url));
            mRxHttp = new RxHttp();
        }
    
        public void get(View v) {
            String url = "http://192.168.1.111:8080/ssh/ok/get";
            mRxHttp.get(url)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Observer<String>() {
                        @Override
                        public void onSubscribe(@NonNull Disposable disposable) {
    
                        }
    
                        @Override
                        public void onNext(@NonNull String s) {
    
                        }
    
                        @Override
                        public void onError(@NonNull Throwable throwable) {
                            if (throwable.getClass().equals(OkhttpException.class)) {
                                int code = ((OkhttpException) throwable).getCode();
                                switch (code) {
                                    case ExceptionCode.CODE_REQUEST_FAIL:
                                        // TODO: 2018/12/20 请求失败
                                        break;
                                    case ExceptionCode.CODE_NET_ERROR:
                                        // TODO: 2018/12/20 网络异常
                                        break;
                                    default:
                                        break;
                                }
                            }
                        }
    
                        @Override
                        public void onComplete() {
                        }
                    });
    
        }
    
        public void get2(View v) {
            String url = "";
            url = mEtUrl.getText().toString().trim();
    
            mRxHttp.get(url, new RxJsonHttpHandler<ResponseOkBean>(ResponseOkBean.class) {
    
                @Override
                public void onStart() {
                    // 请求之前,弹出dialog 等
                    Log.e(TAG, "onStart");
                }
    
                @Override
                public void onSuccess(ResponseOkBean responseOkBean) {
                    int code = responseOkBean.getCode();
                    if (code == 1) {
                        OkBean data = responseOkBean.getData();
                        Log.e(TAG, data.toString());
                        // TODO: setView...设置界面数据
                    } else {
                        String msg = responseOkBean.getMsg();
                        if (TextUtils.isEmpty(msg)) {
                            Log.e(TAG, "xxx失败");
                        } else {
                            Log.e(TAG, msg);
                        }
                    }
                }
    
                @Override
                public void onError(int code) {
                    switch (code) {
                        case ExceptionCode.CODE_REQUEST_FAIL:
                            Log.e(TAG, "请求失败");
                            Toast.makeText(OkHttpActivity.this, "请求失败", Toast.LENGTH_SHORT).show();
                            break;
                        case ExceptionCode.CODE_NET_ERROR:
                            Log.e(TAG, "网络异常");
                            Toast.makeText(OkHttpActivity.this, "网络异常", Toast.LENGTH_SHORT).show();
                            break;
                        case ExceptionCode.CODE_DATA_FORMAT:
                            Log.e(TAG, "请求数据格式不正确");
                            Toast.makeText(OkHttpActivity.this, "请求数据格式不正确", Toast.LENGTH_SHORT).show();
                            break;
                        default:
                            break;
                    }
                }
    
                @Override
                public void onFinish() {
                    // 请求结束:关闭dialog 等
                    Log.e(TAG, "onFinish");
                }
            });
        }
    }
    

    get(View v)点击事件是为了测试RxHttp中的第一个get(final String url)方法。
    get2(View v)点击事件,测试了上诉三种情况:

    1正常情况:

    12-20 17:53:30.859 19877-19877/com.app.rxandroid E/Rx_Ok: onStart
    12-20 17:53:30.894 19877-20019/com.app.rxandroid E/responseJson: {"code":1,"data":{"date":1545299610162,"id":1,"name":"okhttp","values":["aaa","bbb","ccc"]},"msg":"success"}
    12-20 17:53:30.933 19877-19877/com.app.rxandroid E/Rx_Ok: OkBean{id=1, name='okhttp', values=[aaa, bbb, ccc], date=Thu Dec 20 17:53:30 GMT+08:00 2018}
    12-20 17:53:30.933 19877-19877/com.app.rxandroid E/Rx_Ok: onFinish
    

    2.请求失败。(如:404)

    12-20 17:54:59.288 19877-19877/com.app.rxandroid E/Rx_Ok: onStart
    12-20 17:54:59.318 19877-19877/com.app.rxandroid E/Rx_Ok: 请求失败
    12-20 17:54:59.388 19877-19877/com.app.rxandroid E/Rx_Ok: onFinish
    

    3.数据格式不正确。

    12-20 17:55:38.497 19877-19877/com.app.rxandroid E/Rx_Ok: onStart
    12-20 17:55:38.551 19877-21414/com.app.rxandroid E/responseJson: {"date":1545299737813,"id":1,"name":"okhttp","values":["aaa","bbb","ccc"]}
    12-20 17:55:38.558 19877-19877/com.app.rxandroid E/Rx_Ok: xxx失败
    12-20 17:55:38.558 19877-19877/com.app.rxandroid E/Rx_Ok: onFinish
    

    其实还有第四种情况,就是网络问题导致的IOException。如网络未连接。

      @Override
      public void onFailure(Call call, IOException e) {
           emitter.onError(new OkhttpException(ExceptionCode.CODE_NET_ERROR));
      }                  
    

    这个我也测试过,正常回调到了这里:

    case ExceptionCode.CODE_NET_ERROR:
        Log.e(TAG, "网络异常");
        Toast.makeText(OkHttpActivity.this, "网络异常", Toast.LENGTH_SHORT).show();
        break;                
    

    相关文章

      网友评论

          本文标题:RxAndroid+okHttp进行网络请求

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