美文网首页网络
Retrofit2.0+RxJava2.0封装使用

Retrofit2.0+RxJava2.0封装使用

作者: 艾曼大山 | 来源:发表于2019-05-08 09:17 被阅读377次

    Retrofit2.0+RxJava2.0

    大家都知道Okhttp3用着已经很顺手了,Retrofit则是在OkHttp3的基础上,对网络请求做了更加系统、简洁的封装,使用面向接口的方式进行网络请求,它使用动态生成的代理类封装了接口,而且使用很多注解提供功能。RxJava就是一种用Java语言实现的响应式编程,是一个基于事件订阅的异步执行的一个类库,核心思想是观察者模式。

    其它文章

    OkHttp3简单使用和封装使用
    Android开发 多语言、指纹登录、手势登录
    Android使用IconFont阿里矢量图标
    Android Studio 使用SVN 主干和分支合并代码

    Retrofit、RxJava基础

    如果你还不了解Retrofit、RxJava的话,请先查看一些基础文章,我也是这么过来的。

    效果图
    效果图.gif

    项目地址:https://github.com/pengjunshan/Retrofit2RxJava2

    配置
    • gradle中引入相关库 (相关库请到github上查看最新版本)
        implementation 'com.squareup.retrofit2:retrofit:2.5.0'
        implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
        implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
        implementation 'io.reactivex.rxjava2:rxjava:2.2.6'
        implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
    
    • 自动下载Okhttp3和okio资源


      Libraries资源.png
    • 权限 (这里只是demo所用到的权限,实际根据项目所用配置)
      <!--联网权限-->
      <uses-permission android:name="android.permission.INTERNET"></uses-permission>
      <!--检测网络状态权限-->
      <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
      <!-- 在SDCard中创建与删除文件权限 -->
      <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
        tools:ignore="ProtectedPermissions"/>
      <uses-permission android:name="android.permission.WRITE_SETTINGS"
        tools:ignore="ProtectedPermissions"/>
      <!-- 往SDCard写入数据权限 -->
      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
      <!-- 从SDCard读取数据权限 -->
      <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    
    先看下代码调用和日志

    案例中分别写了封装之前和封装之后调的使用,封装后比封装前代码少了不止一点两点,但是该有的流程一步也不少只不过封装起来没有看到而已。

    封装之前简单使用分为4步完成

    • 创建Retrofit
    • 构建接口
    • 构建被观察者对象
    • 订阅
    /**
       * 封装之前
       * 简单使用
       */
      public void RetrofitRxJavaRequet(View view) {
        //第一步:创建Retrofit
        Retrofit mRetrofit = new Builder()
            .baseUrl(Constants.BASEURL)//添加BaseUrl
            .client(MyApplication.mOkHttpClient)//添加OkhttpClient
            .addConverterFactory(GsonConverterFactory.create())//添加Gson解析
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//  添加 rxjava 支持
            .build();
    
        //第二步:构建接口
        IApiService iApiService = mRetrofit.create(IApiService.class);
    
        //第三步:构建被观察者对象
        Observable<BaseBean<List<BannerBean>>> observable = iApiService.getBanner();
    
        //第四步:订阅
        observable.subscribeOn(Schedulers.io())//指定Observable自身在io线程中执行
            .observeOn(AndroidSchedulers.mainThread())//指定一个观察者在主线程中国观察这个Observable
            .subscribe(new Observer<BaseBean<List<BannerBean>>>() {
              @Override
              public void onSubscribe(Disposable d) {
                Log.e("TAG", "开始之前");
              }
    
              @Override
              public void onNext(BaseBean<List<BannerBean>> listBaseBean) {
                Toast.makeText(MainActivity.this,  listBaseBean.getData().toString(), Toast.LENGTH_SHORT).show();
                Log.e("TAG", "成功");
              }
    
              @Override
              public void onError(Throwable e) {
                Toast.makeText(MainActivity.this, "失败", Toast.LENGTH_SHORT).show();
                Log.e("TAG", "失败");
              }
    
              @Override
              public void onComplete() {
                Log.e("TAG", "结束");
              }
            });
    
      }
    
      /**
       * 封装后使用
       * GET请求
       */
      public void GetRequet(View view) {
    
        RetrofitRequest.getBannerApi(context, new IResponseCallBack<BaseBean<List<BannerBean>>>() {
          @Override
          public void onSuccess(BaseBean<List<BannerBean>> data) {
            Toast.makeText(MainActivity.this, "banner成功="+data.getData().toString(), Toast.LENGTH_SHORT).show();
            Log.e("TAG", "banner内容="+data.getData().toString());
          }
    
          @Override
          public void onFail(OkHttpException failuer) {
            Toast.makeText(MainActivity.this, "失败", Toast.LENGTH_SHORT).show();
            Log.e("TAG", "失败="+failuer.getEmsg());
          }
        });
    
      }
    
      /**
       * 封装后使用
       * POST请求
       */
      public void PostKeyValueRequet(View view) {
    
        Map<String,String> map  = new ArrayMap<>();
        map.put("username", "15294792877");
        map.put("password", "15294792877pp");
        RetrofitRequest.postLoginApi(context, map, new IResponseCallBack<BaseBean<LoginBean>>() {
          @Override
          public void onSuccess(BaseBean<LoginBean> data) {
            Toast.makeText(MainActivity.this, "登录成功="+data.getData().toString(), Toast.LENGTH_SHORT).show();
            Log.e("TAG", "登录成功="+data.getData().toString());
          }
    
          @Override
          public void onFail(OkHttpException failuer) {
            Toast.makeText(MainActivity.this, "失败", Toast.LENGTH_SHORT).show();
            Log.e("TAG", "失败="+failuer.getEmsg());
          }
        });
    
      }
    
      /**
       * 封装后使用
       * 下载图片
       */
      public void GetImgRequet(View view) {
    
        RetrofitRequest.downImgApi(String.valueOf(System.currentTimeMillis()) + ".png",
            new IResponseByteCallBack() {
              @Override
              public void onSuccess(File file) {
                Toast.makeText(MainActivity.this, "图片下载成功="+file.getAbsolutePath(), Toast.LENGTH_SHORT).show();
                Log.e("TAG", "图片下载成功="+file.getAbsolutePath());
              }
    
              @Override
              public void onFailure(String failureMsg) {
                Toast.makeText(MainActivity.this, "图片下载失败", Toast.LENGTH_SHORT).show();
                Log.e("TAG", "图片下载失败="+failureMsg);
              }
            });
      }
    
    日志打印.png
    封装
    创建RetrofitClient

    为我们的Retrofit配置参数,使用静态语句块来配置,只执行一次,运行一开始就开辟了内存,内存放在全局。设置baseUrl、添加OkHttpClient、添加Gson解析、添加 rxjava 支持
    request方法是一个公共请求方法接收一个被观察者(Observable)
    subscribeOn(Schedulers.io())指定Observable自身在io线程中执行,
    observeOn(AndroidSchedulers.mainThread())指定一个观察者在主线程中国观察这个Observable,
    subscribe(new CallBackObserver<T>(listener, context));订阅

    /**
     * @author:PengJunShan.
     * 时间:On 2019-05-06.
     * 描述:Retrofit对象
     */
    
    public class RetrofitClient {
    
      public static Retrofit mRetrofit;
    
      /**
       * 为我们的Client配置参数,使用静态语句块来配置
       * 只执行一次,运行一开始就开辟了内存,内存放在全局
       */
      static {
        mRetrofit = new Retrofit.Builder()
            .baseUrl(Constants.BASEURL)//添加BaseUrl
            .client(MyApplication.mOkHttpClient)//添加OkhttpClient
            .addConverterFactory(GsonConverterFactory.create())//添加Gson解析
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//  添加 rxjava 支持
            .build();
      }
    
      /**
       * 所有请求都放在一个接口中
       */
      public static IApiService createApi() {
        return mRetrofit.create(IApiService.class);
      }
    
      /**
       * 不同请求放不同接口中
       * @param service 指定接口
       */
      public static <T> T createApi(Class<T> service) {
        return mRetrofit.create(service);
      }
    
    
      /**
       * @param context 上下文
       * @param observable 被观察者
       * @param listener 回调接口
       * @param requstName 接口名称
       * @param <T> 实体类
       */
      public static <T> void request(Context context, Observable<T> observable,
          final IResponseCallBack<T> listener, String requstName) {
        Constants.requestName = requstName;
        if (!Utils.isConnected(MyApplication.context)) {
          if (listener != null) {
            listener.onFail(new OkHttpException(-1, "网络不可用,请检查网络"));
          }
          return;
        }
        observable.subscribeOn(Schedulers.io())//指定Observable自身在io线程中执行
            .observeOn(AndroidSchedulers.mainThread())//指定一个观察者在主线程中国观察这个Observable
            .subscribe(new CallBackObserver<T>(listener, context));//订阅
    
      }
    
      /**
       * 统一下载图片共用
       * @param observable 被观察者
       * @param callback 回调接口
       * @param imgPath 存储地址
       * @param requstName 功能名称
       */
      public static void downImg(Observable<ResponseBody> observable,
          final IResponseByteCallBack callback, final String imgPath, String requstName) {
        Constants.requestName = requstName;
        observable.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Observer<ResponseBody>() {
              @Override
              public void onSubscribe(Disposable d) {
              }
    
              @Override
              public void onNext(ResponseBody responseBody) {
                File file = null;
                try {
                  InputStream is = responseBody.byteStream();
                  int len = 0;
                  // 文件夹路径
                  String pathUrl =
                      Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator
                          + "/PJS/";
                  File filepath = new File(pathUrl);
                  if (!filepath.exists()) {
                    filepath.mkdirs();// 创建文件夹
                  }
                  file = new File(pathUrl, imgPath);
    
                  FileOutputStream fos = new FileOutputStream(file);
    
                  byte[] buf = new byte[2048];
                  while ((len = is.read(buf)) != -1) {
                    fos.write(buf, 0, len);
                  }
                  fos.flush();
                  fos.close();
                  is.close();
                  callback.onSuccess(file);
                } catch (final Exception e) {
                  callback.onFailure(e.getMessage());
                }
              }
    
              @Override
              public void onError(Throwable e) {
                callback.onFailure(e.getMessage());
              }
    
              @Override
              public void onComplete() {
              }
            });
    
      }
    
    }
    
    初始化OkHttpClient

    前面介绍已经说过了Retrofit是对OkHttp3进行的封装,所以网络请求核心还是OkHttp3,需要对OkHttp3进行初始化配置。

    /**
     * 作者:PengJunShan.
     * 时间:On 2019-05-06.
     * 描述:初始化OkHttpClient  当然也可以在RetrofitCLient类中初始化
     */
    public class MyApplication extends Application {
    
      public static Context context;
      public static OkHttpClient mOkHttpClient;
      /**
       * 超时时间
       */
      private static final int TIME_OUT = 30;
    
      @Override
      public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
        initOkHttp();
      }
    
      private void initOkHttp() {
        //获取缓存路径
        File cacheDir = MyApplication.context.getExternalCacheDir();
    
        //设置缓存的大小
        int cacheSize = 10 * 1024 * 1024;
        //创建我们Client对象的构建者
        OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();
        okHttpBuilder
            //为构建者设置超时时间
            .connectTimeout(TIME_OUT, TimeUnit.SECONDS)
            .readTimeout(TIME_OUT, TimeUnit.SECONDS)
            .writeTimeout(TIME_OUT, TimeUnit.SECONDS)
            ////websocket轮训间隔(单位:秒)
            .pingInterval(20, TimeUnit.SECONDS)
            //设置缓存
            .cache(new Cache(cacheDir.getAbsoluteFile(), cacheSize))
            //允许重定向
            .followRedirects(true)
            //设置拦截器
            .addInterceptor(new RequetInterceptor())
            //添加https支持
            .hostnameVerifier(new HostnameVerifier() {
              @Override
              public boolean verify(String s, SSLSession sslSession) {
                return true;
              }
            })
            .sslSocketFactory(HttpsUtils.initSSLSocketFactory(), HttpsUtils.initTrustManager());
    
        mOkHttpClient = okHttpBuilder.build();
    
      }
    
    }
    
    公共Observer 观察者

    创建一个公用的Observer来处理请求结果后进行回调,在回调数据之前先判断当前Activity是否还存在。在onSubscribe()方法里可以进行网络加载圈,onComplete()方法里可以取消网络加载圈,把错误交给ExceptionHandle类处理。

    /**
     * @author:PengJunShan.
     *
     * 时间:On 2019-05-06.
     *
     * 描述:公共Observer 观察者
     */
    public class CallBackObserver<T> implements Observer<T> {
    
      private IResponseCallBack<T> mListener;
      private Activity activity;
    
      public CallBackObserver(IResponseCallBack<T> listener, Context context) {
        this.mListener = listener;
        this.activity = (Activity) context;
      }
    
      @Override
      public void onSubscribe(Disposable d) {
        /**
         * 这里可以 显示加载圈等
         */
      }
    
      /**
       * 成功
       * @param data
       */
      @Override
      public void onNext(T data) {
        if (mListener != null && !activity.isFinishing()) {
          BaseBean baseBean = (BaseBean) data;
          /**
           * 是否成功
           */
          if (baseBean.isSuccess()) {
            mListener.onSuccess(data);
          }else {
            mListener.onFail(new OkHttpException(baseBean.getErrorCode(), baseBean.getErrorMsg()));
          }
        }
    
      }
    
      /**
       * 失败
       * @param e
       */
      @Override
      public void onError(Throwable e) {
        onComplete();
        if (mListener != null && !activity.isFinishing()) {
          /**
           * 处理失败原因
           */
          OkHttpException okHttpException = ExceptionHandle.handleException(e,activity);
          if(okHttpException !=null) {
            mListener.onFail(okHttpException);
          }
        }
    
      }
    
      @Override
      public void onComplete() {
        /**
         * 这里可以 关闭加载圈等
         */
      }
    
    }
    
    ExceptionHandle(自定义错误解析类)

    除了网络错误之外,我们还要解析前端和后端定义的错误码,比如常见的token失效错误,我们抓取错误码来进行重新登录获取新的token值。

    /**
     * @author:PengJunShan.
     * 时间:On 2019-05-06.
     * 描述:自定义错误解析类
     */
    public class ExceptionHandle {
    
      private static final int TIMEOUT_ERROR = -1;
      private static final int JSON_ERROR = -2;
      private static final int NETWORK_ERROR = -3;
      private static final int OTHER_ERROR = -4;
    
      private static final String TIMEOUTMSG = "请求超时";
      private static final String JSONMSG = "解析错误";
      private static final String NETWORKMSG = "连接失败";
      private static final String OTHERMSG = "未知错误";
    
      /**
       * 根据接口定义 错误码等于999 为token失效 需要重新登录获取新的token
       */
      private static final int TOKENLOGIN = 999;
    
      public static OkHttpException handleException(Throwable e, Activity activity) {
        OkHttpException ex = null;
        if (e instanceof HttpException) {
          ResponseBody body = ((HttpException) e).response().errorBody();
          try {
            Gson gson = new GsonBuilder().serializeNulls().create();
            String jsonStr = body.string();
            BaseBean baseBean = gson.fromJson(jsonStr, BaseBean.class);
    
            /**
             * token失效 重新登录
             */
            if (baseBean.getErrorCode() == TOKENLOGIN ) {
    //          activity.startActivity(LoginActivity.class);
    //          activity.finish();
            } else {
              ex = new OkHttpException(baseBean.getErrorCode(), baseBean.getErrorMsg());
            }
          } catch (IOException e1) {
            e1.printStackTrace();
          }
          return ex;
        } else if (e instanceof java.net.SocketTimeoutException) {
          ex = new OkHttpException(TIMEOUT_ERROR, TIMEOUTMSG);
          return ex;
        } else if (e instanceof JsonParseException
            || e instanceof JSONException
            || e instanceof ParseException) {
          ex = new OkHttpException(JSON_ERROR, JSONMSG);
          return ex;
        } else if (e instanceof ConnectException) {
          ex = new OkHttpException(NETWORK_ERROR, NETWORKMSG);
          return ex;
        } else {
          ex = new OkHttpException(OTHER_ERROR, OTHERMSG);
          return ex;
        }
      }
    
    }
    
    /**
     * @author:PengJunShan.
     * 时间:On 2019-05-06.
     * 描述:自定义异常类,返回ecode,emsg到业务层
     */
    
    public class OkHttpException extends Exception {
    
      private static final long serialVersionUID = 1L;
    
      private int ecode;
      private String emsg;
    
      public OkHttpException(int ecode, String emsg) {
        this.ecode = ecode;
        this.emsg = emsg;
      }
    
      public int getEcode() {
        return ecode;
      }
    
      public String getEmsg() {
        return emsg;
      }
    
    }
    
    IResponseCallBack<T>(回调接口)

    Retrofit支持Gson解析实体类,请求成功后把实体类我们自定义的接口回调。失败的话就回调护理过的错误实体类。

    /**
     * @author:PengJunShan.
     * 时间:On 2019-05-06.
     * 描述:自定义回调接口
     */
    
    public interface IResponseCallBack<T> {
    
      void onSuccess(T data);
    
      void onFail(OkHttpException failuer);
    
    }
    
    RequetInterceptor(拦截器)

    拦截器的作用还是很大的,一般我们工作中请求头部都会传入token、用户id标识认证参数。连接器中可以拦截到Request对象,然后添加头部公共参数。通过获取FormBody可以打印出入参参数,通过获取Response可以打印出出参参数。不能直接使用response.body().string()的方式输出日志,因为response.body().string()之后,response中的流会被关闭,我们需要创建出一个新的response给应用层处理。

    /**
     * @author:PengJunShan.
     * 时间:On 2019-05-05.
     * 描述:日志拦截器
     */
    
    public class RequetInterceptor implements Interceptor {
    
      /**
       * 这个chain里面包含了request和response,所以你要什么都可以从这里拿
       */
      @Override
      public Response intercept(Chain chain) throws IOException {
    
        /**
         * 可以添加公共头部参数如token
         */
        Request request = chain.request()
            .newBuilder()
    //        .header("TOKEN", token)
    //        .header("ID", id)
            .build();
    
        /**
         * 开始时间
         */
        long startTime = System.currentTimeMillis();
        Log.e("TAG","\n"+"requestUrl=" + request.url());
        String method = request.method();
    
        if ("POST".equals(method)) {
          try {
            JSONObject jsonObject = new JSONObject();
            if (request.body() instanceof FormBody) {
              FormBody body = (FormBody) request.body();
              for (int i = 0; i < body.size(); i++) {
                jsonObject.put(body.encodedName(i), body.encodedValue(i));
              }
              Log.e("TAG","入参JSON= " + jsonObject.toString());
            }
          } catch (JSONException e) {
            e.printStackTrace();
          }
        }
    
        Response response = chain.proceed(request);
        /**
         * 这里不能直接使用response.body().string()的方式输出日志
         * 因为response.body().string()之后,response中的流会被关闭,程序会报错,我们需要创建出一个新的response给应用层处理
         */
        ResponseBody responseBody = response.peekBody(1024 * 1024);
        Log.e("TAG","出参JSON=" + responseBody.string());
        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;
        Log.e("TAG","----------" + Constants.requestName + "耗时:" + duration + "毫秒----------");
        return response;
      }
    
    }
    
    结束

    网上有很多别人封装好的库,但是符不符合自己的项目使用就是另外一回事了。还不如自己花点时间封装一个既简单又符合自己项目使用的代码,文章中贴出来的代码是核心代码并不是所有的代码,下载代码根据自己项目需求修改一下就可以用的。下篇我要写一个以MVP+retrofit2+rxjava2进行网络请求封装,主要是讲MVP模式。

    项目地址:https://github.com/pengjunshan/Retrofit2RxJava2

    拿到代码后移到自己项目中根据自己项目需求修改即可使用。

    相关文章

      网友评论

        本文标题:Retrofit2.0+RxJava2.0封装使用

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