二、实现步骤
1、基于哪些基础?
首先一定有个大前提,就是所有接口返回的数据格式,都是一样的,比如说现在我的接口返回数据格式是这样的
{
"code": 200,
"message": "提交成功"
}
又或者是这样的
{
"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<T> {
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 <T> 泛型
*/
public interface IBaseView<T> {
/**
* 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 <T> 泛型
*/
<T> void start(Observable<CallResult<T>> observable, IBaseView<T> iBaseView);
/**
* 成功回调
*
* @param iBaseView 视图、回调
* @param data 数据
* @param <T> 泛型
*/
<T> void viewCallBackSuccess(IBaseView<T> iBaseView, CallResult<T> data);
/**
* 错误回调
*
* @param iBaseView 视图、回调
* @param e 错误信息
* @param <T> 泛型
*/
<T> void viewCallBackError(IBaseView<T> iBaseView, Throwable e);
/**
* 解绑
*/
void detachView();
}
3.3.2 BasePresenter
BasePresenter处理一些回调,将一些非正常接口请求的结果转换成中文(指定描述)在回调给view,这里的所有数据都是可以自己定义的,另外如果在某种情况下需要弹窗退出登录,建议您新建一个MyBasePresenter extend BasePresenter,然后重写onTokenErrorCallBack()即可,但判断的逻辑需要更改一下。
public abstract class BasePresenter<M> 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<Integer, WeakReference<?>> 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
#### 最后是今天给大家分享的一些独家干货:
**【Android开发核心知识点笔记】**

**【Android思维脑图(技能树)】**

**【Android核心高级技术PDF文档,BAT大厂面试真题解析】**

**【Android高级架构视频学习资源】**
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.youkuaiyun.com/topics/618156601)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
ceof SocketTimeoutException) {
callBackError(view, "网络连接超时,请检查您的网络状态,稍后重试");
} else if (e instanceof UnknownHostException) {
callBackError(view, "网络异常,请检查您的网络状态");
} else if (e instanceof JSONException
#### 最后是今天给大家分享的一些独家干货:
**【Android开发核心知识点笔记】**
[外链图片转存中...(img-XrJ7sVRH-1714285139630)]
**【Android思维脑图(技能树)】**
[外链图片转存中...(img-tjFpFBL9-1714285139631)]
**【Android核心高级技术PDF文档,BAT大厂面试真题解析】**
[外链图片转存中...(img-2azFn1KM-1714285139631)]
**【Android高级架构视频学习资源】**
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.youkuaiyun.com/topics/618156601)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**