Android MVP开发模式与Rxjava+Retrofit结合的使用(进阶版)_android mvp+retrofit+rxjava循环

52 阅读7分钟

又或者是这样的



{ "code": 200, "message": "", "data": { "userName": "张三", "headImage": "http://192.168.3.11/file/user/hf6d4g88a.jpg", "sex": 1, "bir": "2020-01-01" } }


他们都有些共同点,如code和message,那么data就是泛型了!所以定义的响应体接收的类为



public class CallResult { public int code; public String message; public T data; }


### 2、封装Retrofit


考虑到有时候需要在请求的header中加各种数据,比如说appVersion等,并且上传文件和普通的接口超时时间一般是不同的,因此就有了这种封装



#### 2.1开始封装



import android.text.TextUtils; import android.util.Log;

import com.example.mvp.OnHttpResultListener; import com.example.mvp.retrofit2.convert.MyConverterFactory;

import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit;

import okhttp3.Interceptor; import okhttp3.OkHttpClient.Builder; import retrofit2.Retrofit; import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;

/**

  • @author Administrator / public class Retrofit2Manager { /*

    • 默认的请求时间 / private long timeOut = 20000L; /*
    • 监听请求过程 / private OnHttpResultListener onHttpResultListener; /*
    • 服务器地址 / private final String baseUrl; /*
    • 请求头 / private final Map<String, String> map = new HashMap<>(); /*
    • 自定义拦截器 */ private final List<Class<? extends Interceptor>> interceptors = new ArrayList<>();

    /**

    • 静态方法,入口
    • @param baseUrl 路径
    • @return this */ public static Retrofit2Manager with(String baseUrl) { return new Retrofit2Manager(baseUrl); }

    /**

    • 私有构造方法
    • @param baseUrl 服务器路径 */ private Retrofit2Manager(String baseUrl) { this.baseUrl = baseUrl; }

    /**

    • 超时时间
    • @param timeOut timeOut
    • @return this */ public Retrofit2Manager setTimeOut(long timeOut) { this.timeOut = timeOut; return this; }

    /**

    • 监听请求过程
    • @param onHttpResultListener onHttpResultListener
    • @return this */ public Retrofit2Manager setOnHttpResultListener(OnHttpResultListener onHttpResultListener) { this.onHttpResultListener = onHttpResultListener; return this; }

    /**

    • 添加自定义请求头
    • @param key key
    • @param value value
    • @return this */ public Retrofit2Manager addHeadres(String key, String value) { if (TextUtils.isEmpty(key)) { return this; } if (TextUtils.isEmpty(value)) { value = ""; } map.put(key, value); return this; }

    public Retrofit2Manager add(Class<? extends Interceptor> mClass) { interceptors.add(mClass); return this;

    }

    /**

    • 返回retrofit2的实例
    • @return retrofit2 */ public Retrofit retrofit() { Builder okBuilder = new Builder(); okBuilder.readTimeout(this.timeOut, TimeUnit.MILLISECONDS); okBuilder.writeTimeout(this.timeOut, TimeUnit.MILLISECONDS); okBuilder.connectTimeout(this.timeOut, TimeUnit.MILLISECONDS); okBuilder.addInterceptor(new LogInterceptor(map, onHttpResultListener)); try { for (Class<? extends Interceptor> mClass : interceptors) { okBuilder.addInterceptor(mClass.newInstance()); } } catch (Exception e) { Log.e("mvp[error]", e.getMessage()); e.printStackTrace(); } return (new Retrofit.Builder()).client(okBuilder.build()) .baseUrl(this.baseUrl) //自定义解析 .addConverterFactory(MyConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); } }

#### 2.2为什么要自定义解析?


当服务器返回的数据为



{ "code": 200, "message": "", "data": [ { "userName": "李四", "headImage": "http://192.168.3.11/file/user/hf6d4g3243288a.jpg", "sex": 1, "bir": "2020-01-01" }, { "userName": "张三", "headImage": "http://192.168.3.11/file/user/hf6d4g84348a.jpg", "sex": 2, "bir": "2020-01-17" }, { "userName": "王五", "headImage": "http://192.168.3.11/file/user/hf6d4345436g88a.jpg", "sex": 1, "bir": "2020-03-01" } ] }


时,我们定义的实体类可以正常接收数据。如果接口返回的数据是



{ "code": 401, "message": "登录状态已失效", "data": null }


的时候,你会发现直接json转换闪退,因为null无法转换成list,因此我们要自己定义解析工厂,以下是部分代码



@Override
public T convert(@NonNull ResponseBody value) {
    String str = "";
    Object var3;
    try {
        if (value.contentLength() != 0L) {
            str = value.source().readUtf8();
            var3 = this.convert(str, this.type);
            return (T) var3;
        }
        str = "{\"code\":90000,\"message\":\"服务器无响应\"}";
        var3 = this.convert(str, CallResult.class);
    } catch (Exception var8) {
        //当转换出现异常,就用Void进行转换
        Object var4 = this.convert(str, CallResult.class);
        return (T) var4;
    } finally {
        value.close();
    }
    return (T) var3;
}

### 3、创建mvp的各种Base基类



#### 3.1 model层BaseModel


OnHttpResultListener是自己定义的,用来接收接口参数,对调试非常好用,上线版本可以忽略



import android.util.Log;

import com.example.mvp.OnHttpResultListener; import com.example.mvp.retrofit2.Retrofit2Manager;

import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers;

public class BaseModel implements OnHttpResultListener {

protected <T> T createService(String ip, Class<T> mClass) {
    return Retrofit2Manager.with(ip).setOnHttpResultListener(this).retrofit().create(mClass);
}

@Override
public void onResponse(String method, String requestUrl, String requestHeaders, String requestParams, int responseCode, String responseData) {
    String sb = "\n【请求方法】:" + method +
            "\n【请求路径】:" + requestUrl +
            "\n【请求头】:" + requestHeaders +
            "\n【请求参数】:" + requestParams +
            "\n【返回参数】:" + responseData;
    Log.d("exccd(mvp-http)", sb);
}

/**
 * 发起请求,并且在ui线程执行回调
 *
 * @param observable observable
 * @param <T>        泛型
 */
protected <T> Observable<T> callBackOnUi(Observable<T> observable) {
    return observable.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread());
}

/**
 * 发起请求,并且在新的子线程执行回调
 *
 * @param observable observable
 * @param <T>        泛型
 */
protected <T> Observable<T> callBackOnThread(Observable<T> observable) {
    return observable.subscribeOn(Schedulers.io())
            .observeOn(Schedulers.newThread());
}

}


#### 3.2 视图层IBaseView


为了让ui实现这个回调更加简单,我这里将服务器返回的message和其他所有可能有的提示都通过s字段来返回了,因此不管是“登录状态已失效”还是“网络连接异常”,都是在s回调的。根据项目不用,有需要的可以拆分。



/**

  • ui回调
  • @param 泛型 / public interface IBaseView { /*
    • ui回调
    • @param b 是否请求成功
    • @param i 类型
    • @param s 描述
    • @param data 泛型 */ void onCallBack(boolean b, int i, String s, T data); }

#### 3.3 Presenter层


这一层最关键的是IBasePresenter和它的实现类BasePresenter



##### 3.3.1 IBasePresenter


start方法是发起请求的入口,使用时需要将model的方法传进去,然后跟视图绑定起来。



/**

  • presenter需要具备的基础方法 / public interface IBasePresenter { /*

    • 开始发起请求
    • @param observable model层返回的obs
    • @param iBaseView 视图,回调
    • @param 泛型 */ void start(Observable<CallResult> observable, IBaseView iBaseView);

    /**

    • 成功回调
    • @param iBaseView 视图、回调
    • @param data 数据
    • @param 泛型 */ void viewCallBackSuccess(IBaseView iBaseView, CallResult data);

    /**

    • 错误回调
    • @param iBaseView 视图、回调
    • @param e 错误信息
    • @param 泛型 */ void viewCallBackError(IBaseView iBaseView, Throwable e);

    /**

    • 解绑 */ void detachView(); }

##### 3.3.2 BasePresenter


BasePresenter处理一些回调,将一些非正常接口请求的结果转换成中文(指定描述)在回调给view,这里的所有数据都是可以自己定义的,另外如果在某种情况下需要弹窗退出登录,建议您新建一个MyBasePresenter extend BasePresenter,然后重写onTokenErrorCallBack()即可,但判断的逻辑需要更改一下。



public abstract class BasePresenter implements IBasePresenter { /** * 未授权登录,登录状态已失效 / public static final int UNAUTHORIZED = 401; /* * 请求成功 / public static final int SUCCESS = 200; /* * 请求被禁止 / public static final int FORBIDDEN = 403; /* * 接口失效 / public static final int NOT_FOUND = 404; /* * 请求超时 / public static final int REQUEST_TIMEOUT = 408; /* * 服务器错误 / public static final int INTERNAL_SERVER_ERROR = 500; /* * 错误的网关 / public static final int BAD_GATEWAY = 502; /* * 服务器不可用 / public static final int SERVICE_UNAVAILABLE = 503; /* * 网络超时 / public static final int GATEWAY_TIMEOUT = 504; /* * 在默认线程回调 / private boolean callBackInLoop = false; /* * 是否已经解绑了,避免重复解绑 / private boolean isDttached = false; /* * model层 / protected M module; /* * 视图 / private final Map<Integer, IBaseView> mapView = new HashMap<>(); /** * 视图引用 */ private final Map> mapReference = new HashMap<>(); /* * 请求对象 / private final Map<Integer, Disposable> mapDisposables = new HashMap<>(); /* * 主线程 */ protected Handler handler;

/**
 * 构造方法
 * 您需要手动{@link #detachView()}解绑
 */
public BasePresenter() {
    onCreate(null);
}

/**
 * 构造方法
 *
 * @param activity activity的实例
 */
public BasePresenter(Activity activity) {
    onCreate(activity);
}

/**
 * 构造方法
 *
 * @param context 如果这是个activity的实例,那么不需要手动{@link #detachView()}即可解绑,否则您需要调用他
 */
public BasePresenter(Context context) {
    if (context instanceof Activity) {
        onCreate((Activity) context);
    } else {
        onCreate(null);
    }
}

/**
 * 初始化方法
 */
private void onCreate(Activity activity) {
    this.handler = new Handler(Looper.getMainLooper());
    if (this.module == null) {
        this.module = this.createModel();
    }
    if (activity != null) {
        String acName = activity.getLocalClassName();
        Application app = activity.getApplication();
        Application.ActivityLifecycleCallbacks callbacks = new Application.ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle bundle) {

            }

            @Override
            public void onActivityStarted(Activity activity) {

            }

            @Override
            public void onActivityResumed(Activity activity) {

            }

            @Override
            public void onActivityPaused(Activity activity) {

            }

            @Override
            public void onActivityStopped(Activity activity) {

            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {
                if (acName.equals(activity.getLocalClassName())) {
                    detachView();
                    app.unregisterActivityLifecycleCallbacks(this);
                }
            }
        };
        app.registerActivityLifecycleCallbacks(callbacks);
    }
}

/**
 * 绑定
 *
 * @param view 视图
 */
@SuppressWarnings("all")
private <T, V extends IBaseView<T>> void attachView(V view) {
    if (view != null) {
        WeakReference<V> weakReference = new WeakReference<V>(view);
        mapReference.put(view.hashCode(), weakReference);
        ClassLoader classLoader = view.getClass().getClassLoader();
        Class<?>[] interfaces = view.getClass().getInterfaces();
        InvocationHandler invocationHandler = new MvpViewHandler((IBaseView) weakReference.get());
        IBaseView<T> v = (V) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        mapView.put(view.hashCode(), v);
    }
}

/**
 * 是否在默认线程回调
 *
 * @param callBackInLoop 如果是false,回调会在ui线程处理,否则就是在发起默认的线程回调
 */
public void setCallBackInLoop(boolean callBackInLoop) {
    this.callBackInLoop = callBackInLoop;
}

/**
 * 页面是否已经不存在了
 *
 * @param iBaseView 视图
 * @param <T>       泛型
 * @return true存在,则回调,否则忽略
 */
protected <T> boolean isViewAttached(IBaseView<T> iBaseView) {
    if (iBaseView == null) {
        return false;
    }
    int key = iBaseView.hashCode();
    IBaseView<?> view = mapView.get(key);
    WeakReference<?> weakReference = mapReference.get(key);
    return view != null && weakReference != null && weakReference.get() != null;
}

/**
 * 创建module
 *
 * @return M
 */
protected abstract M createModel();

/**
 * 请求是否成功
 *
 * @param data 响应体
 * @return 成功true,失败false
 */
protected <T> boolean isSuccess(CallResult<T> data) {
    return data != null && data.code == SUCCESS;
}

/**
 * 开始发起请求
 *
 * @param observable model层返回的obs
 * @param baseView   视图、回调
 * @param <T>        泛型
 */
@Override
public <T> void start(Observable<CallResult<T>> observable, IBaseView<T> baseView) {
    attachView(baseView);
    mapDisposables.put(baseView.hashCode(), observable
            .subscribe(data -> viewCallBackSuccess(baseView, data), e -> viewCallBackError(baseView, e)));
}

/**
 * 成功回调
 *
 * @param view 视图、回调
 * @param data 数据
 * @param <T>  泛型
 */
@Override
public <T> void viewCallBackSuccess(IBaseView<T> view, CallResult<T> data) {
    if (callBackInLoop) {
        _viewCallBackSuccess(view, data);
    } else {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            _viewCallBackSuccess(view, data);
        } else {
            handler.post(() -> _viewCallBackSuccess(view, data));
        }
    }
}

/**
 * 错误回调
 *
 * @param view 视图、回调
 * @param e    错误信息
 * @param <T>  泛型
 */
@Override
public <T> void viewCallBackError(IBaseView<T> view, Throwable e) {
    if (callBackInLoop) {
        _viewCallBackError(view, e);
    } else {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            _viewCallBackError(view, e);
        } else {
            handler.post(() -> _viewCallBackError(view, e));
        }
    }
}

/**
 * 解绑
 */
@Override
public void detachView() {
    if (isDttached) {
        return;
    }
    isDttached = true;

// this.module = null; this.handler.removeCallbacksAndMessages(null); for (WeakReference<?> weakReference : mapReference.values()) { if (weakReference != null) { weakReference.clear(); } } mapReference.clear(); mapView.clear(); try { for (Disposable disposable : mapDisposables.values()) { if (disposable != null && !disposable.isDisposed()) { disposable.dispose(); } } } catch (Exception e) { Log.e("mvp[error]", e.getMessage()); } }

/**
 * 统一执行成功回调,看{@link #viewCallBackSuccess}
 */
private <T> void _viewCallBackSuccess(IBaseView<T> view, CallResult<T> data) {
    if (data.code == UNAUTHORIZED) {
        onTokenErrorCallBack(data.message);
    }
    if (isViewAttached(view)) {
        view.onCallBack(data.code == SUCCESS, data.code, data.message, data.data);
    }
}

/**
 * 统一执行错误回调,看{@link #viewCallBackError}
 */
private <T> void _viewCallBackError(IBaseView<T> view, Throwable e) {
    if (isViewAttached(view)) {
        try {
            if (e instanceof HttpException) {
                HttpException httpException = (HttpException) e;
                switch (httpException.code()) {
                    case UNAUTHORIZED:
                        callBackError(view, "登录验证已过期");
                        onTokenErrorCallBack("登录验证已过期");
                        break;
                    case INTERNAL_SERVER_ERROR:
                        callBackError(view, "服务器错误");
                        break;
                    case FORBIDDEN:
                    case NOT_FOUND:
                        callBackError(view, "无效的请求");
                        break;
                    case REQUEST_TIMEOUT:
                    case GATEWAY_TIMEOUT:
                    case BAD_GATEWAY:
                    case SERVICE_UNAVAILABLE:
                    default:
                        callBackError(view, httpException.getMessage());
                        break;
                }
            } else if (e instanceof ConnectException) {
                callBackError(view, "网络连接异常,请检查您的网络状态");
            } else if (e instanceof SocketTimeoutException) {
                callBackError(view, "网络连接超时,请检查您的网络状态,稍后重试");
            } else if (e instanceof UnknownHostException) {
                callBackError(view, "网络异常,请检查您的网络状态");
            } else if (e instanceof JSONException
                    || e instanceof ParseException) {
                callBackError(view, "数据解析错误");
            } else if (e instanceof SSLHandshakeException) {
                callBackError(view, "证书验证失败");
            } else if (e instanceof RuntimeException) {
                callBackError(view, "运行时异常");
            } else {
                callBackError(view, e.toString());
            }
        } catch (Exception e1) {
            Log.e("mvp[error]", e.getMessage());
        }
    }
}

/**
 * {@link #_viewCallBackError}
 */
private <T> void callBackError(IBaseView<T> view, String message) {
    view.onCallBack(false, 9000, message, null);
    Log.e("excce", "UI回调错误信息:" + message);
}

/**
 * 返回一个value类型为Object的哈希表
 *
 * @param initSize 大小
 * @return Map
 */
protected Map<String, Object> createMap(int initSize) {
    return new HashMap<>(initSize);
}

/**
 * 返回一个value类型为Integer的哈希表
 *
 * @param initSize 大小
 * @return Map
 */
protected Map<String, Integer> createMapInt(int initSize) {
    return new HashMap<>(initSize);
}

/**
 * 返回一个value类型为String的哈希表
 *
 * @param initSize 大小
 * @return Map
 */
protected Map<String, String> createMapStr(int initSize) {
    return new HashMap<>(initSize);
}

/**
 * 登录状态失效,需要回到登录页
 *
 * @param message message
 */
protected void onTokenErrorCallBack(String message) {

}

/**
 * 动态代理
 *
 * @param <T> 泛型
 */
private class MvpViewHandler<T> implements InvocationHandler {
    private final IBaseView<T> mvpView;

    MvpViewHandler(IBaseView<T> mvpView) {
        this.mvpView = mvpView;
    }

    @Override
    @SuppressWarnings("SuspiciousInvocationHandlerImplementation")
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (isViewAttached(mvpView)) {
            return method.invoke(this.mvpView, args);
        } else {
            Log.d("excci", "页面已关闭,不执行view层方法!");
            return null;
        }
    }
}

}


### 4、开始使用


经过一系列漫长又复杂的封装,终于可以开始使用了,这里就以登录接口和获取用户信息接口为例,展示两个不同模块的使用方法



#### 4.1 根据接口文档编写公共模块的IModelCom


这其实就是大家熟悉的Service



/**

  • 公共方法模块 / public interface IModelCom { /*

    • 登录
    • @param map phone,passWord */ @POST("api/user/login") Observable<CallResult> login(@Body Map<String, Object> map);

    /**

    • 注册
    • @param map phone,code,passWord */ @POST("api/user/register") Observable<CallResult> register(@Body Map<String, Object> map);

    /**

    • 忘记密码
    • @param map phone,code,newPassWord */ @POST("api/user/forget") Observable<CallResult> forgetPwd(@Body Map<String, Object> map); }

另一个IModelUserCenter的写法差不多的,忽略。



#### 4.2来看看实现类


是否在主线程回调,看自己咯



public class ModelCom extends BaseModel implements IModelCom {

private static final class IHolder {