又或者是这样的
{ "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 {