网络请求框架(基于okhttp+rxjava2+retrofit2的mvp模式网络请求框架+RxBus+RxView控制按钮重复点击)

前言

开发的项目也不少了,网络请求这一块是必不可少的,使用过的网络请求方式也很多了,也不想每次搭建一个项目,都要重复造轮子,决定封装一个网络请求框架供自己以后使用,同时如果同行的小伙伴觉得不错的话也可以使用使用!(如果有不好的地方多多提提意见,将继续完善)

刚好最近公司有一个新的项目要启动,正好可以使用我封装的框架,还可以在测试中完善。

首先先介绍下封装的框架的类的作用:

HttpMethods:http网络请求助手,在此类中设置请求的基地址,拦截器,请求时间,请求参数等
BasePresenter:接口,用于绑定VIew和分离View
ApiException:自定义异常类
ApiCallback:自定义返回结果回调
RxBaseView:空接口,主要是在BasePresenter中绑定View中需要使用到,而且MVP模式中的总回调方法也可以继承此类,进行方法的拓展。具体使用方法下面再详细介绍。
RxBus:事件处理,与eventBus差不多。
RxPresenter:此类实现BasePresenter类,View的绑定,分离,Rxjava的绑定和解绑操作都在此类。
SubscriberCallBack:自定义解析类,实现Observer类,与ApiCallback的回调方法一起使用,错误信息通过转换,转换成用户能理解的通俗语言表达。

这里只是网络请求的封装,使用方法也很简单,下面继续详细介绍主要的类和方法。

HttpMethods

1、考虑到不同的人,不同的公司对于网络请求时间的控制是不一样的,所以开放了请求连接超时时间、读取超时时间、写入超时时间的设置,如果不设置则使用默认时间10s,具体方法:

/**
 * 设置连接超时时间(秒)
 *
 * @param connectTimeOut 连接时间
 */
public static void setConnectTimeOut(int connectTimeOut) {
    HttpMethods.connectTimeOut = connectTimeOut;
}

/**
 * 设置读取超时时间(秒)
 *
 * @param readTimeOut 读取时间
 */
public static void setReadTimeOut(int readTimeOut) {
    HttpMethods.readTimeOut = readTimeOut;
}

/**
 * 设置写入超时时间(秒)
 *
 * @param writeTimeOut 写入时间
 */
public static void setWriteTimeOut(int writeTimeOut) {
    HttpMethods.writeTimeOut = writeTimeOut;
}

2、解析库,默认使用的是Gson的解析库,但是可能因为公司返回数据的特殊,所以提供了一个可以自定义的解析库,然后解析逻辑可在自定义的解析方法中解析。

/**
 * 设置解析库
 * 可自定义解析逻辑
 *
 * @param factory 解析库
 */
public static void setFactory(Converter.Factory factory) {
    mFactory = factory;
}

3、设置拦截器

/**
 * 设置拦截器
 *
 * @param interceptor
 */
public static void setInterceptor(Interceptor interceptor) {
    mInterceptor = interceptor;
}

/**
 * 设置日志打印logger拦截器
 *
 * @param logger HttpLoggingInterceptor.Logger
 */
public static void setLogger(HttpLoggingInterceptor.Logger logger) {
    mLogger = logger;
}

4、设置日志打印级别,可根据自己下个查看的日志信息设置不同的打印级别

/**
 * 设置日志打印级别
 *
 * @param level
 */
public static void setLevel(HttpLoggingInterceptor.Level level) {
    Level = level;
}

RxPresenter

1、rxjava订阅,订阅目前只有Observer的回调,没Consumer的方式(待完善)

/**
 * RxJava绑定
 * 添加订阅
 *
 * @param observable
 * @param observer
 * @param <T>
 */
public <T> void addSubscription(Observable<T> observable, Observer<T> observer) {
    observable.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(observer);
}

使用方法:

一、HttpMethods初始化:

在application中进行HttpMethods的初始化

HttpMethods.setBaseUrl(Constants.BASE_URL);//必不可少,请求的基地址
HttpMethods.setInterceptor(new CommonParametersInterceptor());//设置自定义的公共参数拦截器
HttpMethods.setLevel(HttpMethods.BODY);//设置日志打印基本
HttpMethods.setLogger(new HttpLogger());//设置日志打印拦截器

1.3版本以前使用上面的初始化方式,1.3版本以后使用以下初始化方式

 HttpMethods
                .getInstanceBuilder()
                .setBaseUrl(Constants.BASE_URL)//设置域名
                .setLogLevel(LogLevel.ERROR)//设置日志打印级别,使用默认的日志打印才需要设置这个
                .setLogName("123456")//设置默认日志打印名字
                .setIsOpenLog(true)//设置是否开启框架默认的日志打印
                .setCookieJar(new CookieJarImpl())//设置自定义的cookiejar
//                .setLogger(new HttpLogger())//设置自定义logger,此设置是打印网络请求的数据(如果设置了自定义的,则框架默认的则不需要设置)
//                .setLevel(LoggerLevel.BODY)//设置日志打印级别(自定义logger可设置,框架默认的是BODY级别,如果上架需要关闭日志打印,则设置setIsOpenLog(false)即可)
                .setReadTimeOut(60)
                .setConnectTimeOut(60)
                .setWriteTimeOut(60)
//                .setInterceptor(new CommonParametersInterceptor())//设置拦截器
//                .setNetworkInterceptor(new CommonParametersInterceptor())//设置拦截器
//                .setFactory(CustomConverterFactory.create())//设置自定义解析器
                .setInterceptors(new CommonParametersInterceptor(), new CommonParametersInterceptorHead());//设置多个拦截器

 以下是自定义的logger类:

CommonParametersInterceptor类

/**
 * 添加公共参数
 *
 * @author Administrator
 * @date 2019/3/11
 */

public class CommonParametersInterceptor implements Interceptor {
    private String TAG = "CommonParametersInterceptor";

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request oldRequest = chain.request();
        Response response = null;
        // 新的请求,添加参数
        Request newRequest = addParam(oldRequest);
        response = chain.proceed(newRequest);
        return response;
    }

    /**
     * 添加公共参数
     *
     * @param oldRequest
     * @return
     */
    private Request addParam(Request oldRequest) {

        HttpUrl.Builder builder = oldRequest.url()
                .newBuilder()
                .setEncodedQueryParameter("lversion", Constants.BASE_URL)
                .setEncodedQueryParameter("token", Constants.BASE_URL);
        Request newRequest = oldRequest.newBuilder()
                .method(oldRequest.method(), oldRequest.body())
                .url(builder.build())
                .build();
        return newRequest;
    }


}

HttpLogger类

public class HttpLogger implements HttpLoggingInterceptor.Logger {
    private StringBuilder mMessage = new StringBuilder();

    @Override
    public void log(String message) {
        // 请求或者响应开始
        if (message.startsWith("--> POST")) {
            mMessage.setLength(0);
        }
        // 以{}或者[]形式的说明是响应结果的json数据,需要进行格式化
        if ((message.startsWith("{") && message.endsWith("}"))
                || (message.startsWith("[") && message.endsWith("]"))) {
            message = JsonUtil.formatJson(message);
        }
        mMessage.append(message.concat("\n"));
        // 请求或者响应结束,打印整条日志
        if (message.startsWith("<-- END HTTP")) {
            LogUtil.d(mMessage.toString());
            LogUtil.e(mMessage.toString());
        }
    }

}

此日志打印使用的logger打印框架,具体使用方法可到github查看文档:

https://github.com/orhanobut/logger

日志打印拦截器使用到的日志打印类LogUtil和JsonUtil分别如下:

LogUtil

public class LogUtil {
    /**
     * 初始化log工具,在app入口处调用
     *
     * @param logName 打印日志名字
     * @param isLog 是否打印log
     */
    public static void init(String  logName, final boolean isLog) {
        FormatStrategy mFormatStrategy = PrettyFormatStrategy.newBuilder()
                .showThreadInfo(false)  // (可选)是否显示线程信息。默认值true
//                .methodCount(5)         // (可选)要显示的方法行数。默认值2
//                .methodOffset(7)        // (可选)隐藏内部方法调用到偏移量。默认值5
//                .logStrategy() // (可选)更改要打印的日志策略。默认LogCat
                .tag(logName)   // (可选)每个日志的全局标记。默认PRETTY_LOGGER .build
                .build();
        //log日志打印框架Logger
        com.orhanobut.logger.Logger.addLogAdapter(new AndroidLogAdapter(mFormatStrategy){
            @Override
            public boolean isLoggable(int priority, @Nullable String tag) {
                return isLog;
            }
        });
    }

    public static void d(String message) {
        Logger.d(message);
    }
    public static void e(String message) {
        Logger.e(message);
    }

    public static void i(String message) {
        Logger.i(message);
    }

    public static void w(String message, Throwable e) {
        String info = e != null ? e.toString() : "null";
        Logger.w(message + ":" + info);
    }

    public static void e(String message, Throwable e) {
        Logger.e(e, message);
    }

    public static void json(String json) {
        Logger.json(json);
    }
}

JsonUtil类

public class JsonUtil {
    /**
     * 格式化json字符串
     *
     * @param jsonStr 需要格式化的json串
     * @return 格式化后的json串
     */
    public static String formatJson(String jsonStr) {
        if (null == jsonStr || "".equals(jsonStr)) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        char last = '\0';
        char current = '\0';
        int indent = 0;
        for (int i = 0; i < jsonStr.length(); i++) {
            last = current;
            current = jsonStr.charAt(i);
            //遇到{ [换行,且下一行缩进
            switch (current) {
                case '{':
                case '[':
                    sb.append(current);
                    sb.append('\n');
                    indent++;
                    addIndentBlank(sb, indent);
                    break;
                //遇到} ]换行,当前行缩进
                case '}':
                case ']':
                    sb.append('\n');
                    indent--;
                    addIndentBlank(sb, indent);
                    sb.append(current);
                    break;
                //遇到,换行
                case ',':
                    sb.append(current);
                    if (last != '\\') {
                        sb.append('\n');
                        addIndentBlank(sb, indent);
                    }
                    break;
                default:
                    sb.append(current);
            }
        }
        return sb.toString();
    }

    /**
     * 添加space
     *
     * @param sb
     * @param indent
     */
    private static void addIndentBlank(StringBuilder sb, int indent) {
        for (int i = 0; i < indent; i++) {
            sb.append('\t');
        }
    }

}

二、MVP模式构建

  • 1、baseView:新建baseView接口,继承RxBaseView,可自定义方法,也可不自定义,示例如下:
public interface BaseView extends RxBaseView {
    void showResult(String result);
}
  • 2、contract:此类起着一个桥梁的作用,把view和presenter使用接口的方式进行通信
public interface MainContract {
    interface View extends BaseView {
        void onSuccess(LoginBean loginBean);

        void onSuccess(HttpResult loginBean);

        void onError(String msg);
    }

    interface Presenter extends BasePresenter<View> {
        void doLogin1(String userName, String pwd);

        void doLogin2(String userName, String pwd);

        void loadLoginStatusEntity();

        void doLogin(String phone, String password);
    }
}
  • 3、presenter:进行网络请求等操作,示例如下:
public class MainPresenter extends RxPresenter<MainContract.View> implements MainContract.Presenter {
    ApiServer apiServer = HttpMethods.getInstance().create(ApiServer.class);

    @Override
    public void doLogin1(String userName, String pwd) {
        Observable observable = apiServer.login1(userName, pwd);
        addSubscription(observable, new SubscriberCallBack<>(new ApiCallback<HttpResult>() {
            @Override
            public void onSuccess(HttpResult model) {
                mView.onSuccess(model);
                Logger.d(model);
            }

            @Override
            public void onFailure(String msg) {
                mView.onError(msg);
                Logger.d(msg);
            }
        }));
    }

    @Override
    public void doLogin2(String userName, String pwd) {
        Observable<LoginBean> observable = apiServer.login2(userName, pwd).map(new HttpResultFunc<LoginBean>());
        addSubscription(observable, new SubscriberCallBack<>(new ApiCallback<LoginBean>() {
            @Override
            public void onSuccess(LoginBean model) {
                Logger.d(model);
                mView.onSuccess(model);
            }

            @Override
            public void onFailure(String msg) {
                mView.onError(msg);
                Logger.d(msg);
            }
        }));
    }




    @Override
    public void loadLoginStatusEntity() {
        Observable<LoginStatusEntity> observable = apiServer.loadLoginStatus();
        addSubscription(observable, new SubscriberCallBack<>(new ApiCallback<LoginStatusEntity>() {
            @Override
            public void onSuccess(LoginStatusEntity model) {
                LogUtil.e(model.toString());
            }

            @Override
            public void onFailure(String msg) {
                LogUtil.e(msg);
            }
        }));
    }

    @Override
    public void doLogin(String phone, String password) {
        Observable<LoginEntity> observable = apiServer.login(phone, password);
        addSubscription(observable, new SubscriberCallBack<>(new ApiCallback<LoginEntity>() {
            @Override
            public void onSuccess(LoginEntity model) {
                LogUtil.e(model.toString());
            }

            @Override
            public void onFailure(String msg) {
                ToastUtil.shortShow(msg);
                LogUtil.e(msg);
            }
        }));
    }
}

注意:presenter类是contract类中Presenter接口的实现类,view层不直接与presenter层交互, 而是通过contract层作为桥梁与presenter层交互,此presenter实现类是要进行网络请求,所有 还需要继承RxPresenter,RxPresenter使用泛型,传入了view对象。

  • 4、view:activity或者fragment。activity和fragment是必须集成activity和fragment的, 但是我们还需要使用到presenter,于是,我们使用了泛型类,所有的activity(或者fragment) 都继承此泛型activity(fragment)基类。
    基类示例:
public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity implements RxBaseView {
    protected T mPresenter;
    protected Activity mActivity;
    private View netErrorView;

    protected abstract int getLayout();

    protected abstract void initEventAndData();

    protected abstract T createPresenter();

    @SuppressLint("RestrictedApi")
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        /**
         * 创建presenter对象
         */
        mPresenter = createPresenter();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        }
        super.onCreate(savedInstanceState);
        setContentView(getLayout());

        mActivity = this;
        //活动控制器
        App.getInstance().addActivity(this);


        if (mPresenter != null) {
            mPresenter.attachView(this);
        }

        initEventAndData();
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        /**
         * presenter 解除view订阅
         */
        if (mPresenter != null) {
            mPresenter.detachView();
        }

        App.getInstance().removeActivity(this);

    }

    /**
     * 是否需要注册网络变化的Observer,如果不需要监听网络变化,则返回false;否则返回true
     */
    protected boolean needRegisterNetworkChangeObserver() {
        return true;
    }



    //设置title
    public void setTitleTx(String title_tx) {
        try {
            TextView title = findViewById(R.id.title);
            title.setText(title_tx);
        } catch (Exception e) {

        }
    }

    /**
     * 打开一个Activity 默认 不关闭当前activity
     */
    public void gotoActivity(Class<?> clz) {
        gotoActivity(clz, false, null);
    }

    public void gotoActivity(Class<?> clz, boolean isCloseCurrentActivity) {
        gotoActivity(clz, isCloseCurrentActivity, null);
    }

    public void gotoActivity(Class<?> clz, boolean isCloseCurrentActivity, Bundle ex) {
        Intent intent = new Intent(this, clz);
        if (ex != null) intent.putExtras(ex);
        startActivity(intent);
        if (isCloseCurrentActivity) {
            finish();
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                super.onBackPressed();//返回
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

}

activity示例:

public class MainActivity extends BaseActivity<MainPresenter> implements MainContract.View, RxView.OnRxViewClickListener {
    private final static String TAG = "MainActivity";
    private EditText username, pwd;
    private TextView tvResult;
    private Disposable mSubscribe;
    private Button rx_view;

    @Override
    protected int getLayout() {
        return R.layout.activity_main;
    }

    @Override
    protected void initEventAndData() {
        username = findViewById(R.id.username);
        rx_view = findViewById(R.id.rx_view);
        pwd = findViewById(R.id.pwd);
        tvResult = findViewById(R.id.result);
        mSubscribe = RxBus.getDefault().tObservable(RxEvent.class).subscribe(new Consumer<RxEvent>() {
            @Override
            public void accept(RxEvent rxEvent) throws Exception {
                if (rxEvent.getCode() == 1000) {
                    username.setText(rxEvent.getUserName());
                    pwd.setText(rxEvent.getPassWord());
                }
            }
        });
        RxView.setIntervalTime(2000);
        RxView.setOnClickListeners(this, rx_view);
    }

    @Override
    protected MainPresenter createPresenter() {
        return new MainPresenter();
    }


    @Override
    public void onSuccess(LoginBean loginBean) {
        Logger.e("onSuccess");
        Logger.d(loginBean);
        tvResult.setText(loginBean.toString());
    }

    @Override
    public void onSuccess(HttpResult loginBean) {
        tvResult.setText(loginBean.toString());
    }


    @Override
    public void onError(String msg) {
        Log.e(TAG, msg);
        tvResult.setText(msg);
    }


    public void update(View view) {

    }


    public void login(View view) {
        mPresenter.doLogin1(username.getText().toString().trim(), pwd.getText().toString().trim());
    }

    @Override
    public void showResult(String result) {
        tvResult.setText(result);
    }

    public void rxBusOnclick(View view) {
        RxBusActivity.startAction(this);
//        jsonTest();
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mSubscribe != null) {
            if (mSubscribe.isDisposed()) {
                mSubscribe.dispose();
            }

        }
    }

    public void login2(View view) {
        mPresenter.doLogin2(username.getText().toString().trim(), pwd.getText().toString().trim());
    }

    public void cookieLogin(View view) {
        mPresenter.doLogin("13790994100", "caishouhui0524");
    }

    public void cookieLoginStatus(View view) {
        mPresenter.loadLoginStatusEntity();
    }

    public void downOnclick(View view) {
        DownActivity.startAction(this);
    }

    public void systemDownOnclick(View view) {
        SystemDownloadActivity.startAction(this);
    }

    @Override
    public void onRxViewClick(View view) {
        switch (view.getId()) {
            case R.id.rx_view:
                LogUtil.e("点击了");
                break;
            default:
                break;
        }
    }

    public void downTask(View view) {
        DownTaskListActivity.startAction(this);
    }
}

三、接口请求

在activity中进行接口的请求,一句代码:

mPresenter.doLogin1(username.getText().toString().trim(), pwd.getText().toString().trim());

  • 1、不抽取结果封装方式请求
ApiServer apiServer = HttpMethods.getInstance().create(ApiServer.class);
 @Override
      public void doLogin1(String userName, String pwd) {
          Observable observable = apiServer.login1(userName, pwd);
          addSubscription(observable, new SubscriberCallBack<>(new ApiCallback<HttpResult>() {
              @Override
              public void onSuccess(HttpResult model) {
                  mView.onSuccess(model);
                  Logger.d(model);
              }
  
              @Override
              public void onFailure(String msg) {
                  mView.onError(msg);
                  Logger.d(msg);
              }
          }));
      }
  • 2、抽取结果封装
 @Override
    public void doLogin2(String userName, String pwd) {
        Observable<LoginBean> observable = apiServer.login2(userName, pwd).map(new HttpResultFunc<LoginBean>());
        addSubscription(observable, new SubscriberCallBack<>(new ApiCallback<LoginBean>() {
            @Override
            public void onSuccess(LoginBean model) {
                Logger.d(model);
                mView.onSuccess(model);
            }

            @Override
            public void onFailure(String msg) {
                mView.onError(msg);
                Logger.d(msg);
            }
        }));
    }

四、文件下载

因为每一个人使用的数据库都不一样,下载就不做保存,下载信息类为HttpDownInfo,如需保存,则自己处理即可

 HttpDownMethods mHttpDownMethods;
    String wechatUrl = "http://dldir1.qq.com/weixin/android/weixin703android1400.apk";
    String qqUrl = "https://qd.myapp.com/myapp/qqteam/AndroidQQ/mobileqq_android.apk";
    private HttpDownInfo mDownInfo;
    private HttpDownInfo mDownInfo2;
    private void initDownloads() {
        mHttpDownMethods = HttpDownMethods.getInstance();
        mHttpDownMethods.setDeleteFile(true);
        mDownInfo = new HttpDownInfo();
        mDownInfo.setUrl(qqUrl);
        mDownInfo.setSavePath(Environment.getExternalStorageDirectory().getAbsolutePath() + "/text/qq.apk");
        mDownInfo2 = new HttpDownInfo();
        mDownInfo2.setUrl(wechatUrl);
    }
    
     mHttpDownMethods.downStart(mDownInfo, new HttpDownListener() {
                        @Override
                        public void downStart() {
                            btn_download1.setText("暂停");
                        }
    
                        @Override
                        public void downPause(HttpDownInfo httpDownInfo,long progress) {
                            LogUtil.e("暂停了");
                            mDownInfo.setReadLength(progress);
                            btn_download1.setText("下载");
                        }
    
                        @Override
                        public void downStop(HttpDownInfo httpDownInfo) {
                            LogUtil.e("停止了");
                            pb_progress1.setProgress(0);
                            tv_progress1.setText(0 + "%");
                            mDownInfo.setReadLength(0);
                            mDownInfo.setCountLength(0);
                            btn_download1.setText("下载");
                        }
    
                        @Override
                        public void downFinish(HttpDownInfo httpDownInfo) {
                            LogUtil.e("下载完成");
                            mDownInfo = httpDownInfo;
                            btn_download1.setText("下载完成");
                        }
    
                        @Override
                        public void downError(HttpDownInfo httpDownInfo, String msg) {
                            LogUtil.e("出错了");
                            mDownInfo = httpDownInfo;
                            btn_download1.setText("下载出错了");
                        }
    
                        @Override
                        public void downProgress(long readLength, long countLength) {
                            int pro = 0;
                            try {
                                pro = (int) (readLength * 100 / countLength);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            pb_progress1.setProgress(pro);
                            tv_progress1.setText(pro + "%");
                        }
                    });

五、RxView使用

  • 1、在需要使用RxView的activity或者fragment中实现接口:RxView.OnRxViewClickListener
  • 2、添加点击事件
  RxView.setIntervalTime(2000);//设置间隔时间
        RxView.setOnClickListeners(this, rx_view,tvResult);//设置需要控制重复点击的按钮,可设置多个

 如果RxView使用觉得不方便,可以查看我的博文:

Android处理按钮重复点击事件

网络请求框架代码传送门

如果遇上Android9.0系统无法请求接口的问题,可以查看博文:

Android9.0 http网络请求失败问题分析与解决方案

 

猜你喜欢

转载自blog.csdn.net/freak_csh/article/details/86712826