一个普通APP的开发中使用最频繁的操作莫过于网络请求,既然这样我们便需要将全部的网络请求操作都集中起来使用统一的方法进行管理。否则一个临时的任务变更会增加大量的工作量
对一个完整的网络请求进行分析,大致分为以下几个流程:
- 发起网络连接
- 得到回执消息,判断连接情况
- 根据实际需求对数据进行解析
- 发起网络连接通常会使用三方网络请求库,这里以
Retrofit
为例,首先需要对Retrofit进行初始化操作
OkHttpClient okHttpClient = new OkHttpClient.Builder()
//连接主机超时时间
.connectTimeout(10, TimeUnit.SECONDS)
//从主机读取超时时间
.readTimeout(120, TimeUnit.SECONDS)
//上传到主机的超时时间
.writeTimeout(120, TimeUnit.SECONDS)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
网络请求库初始化完成后开始正式发起网络请求。首先这里大致的介绍一下Retrofit的网络请求方式,在接口中通过注解的方式对URL以及请求参数进行配置,然后通过之前初始化得到的Retrofit对象来生成请求接口的实现类对象。这样就能得到每个网络请求具体的网络执行对象Call<>
,最后通过Call对象开始执行网络请求并且实现请求回调。
//配置
public interface Api {
@GET("xxxxx")
Call<ResponseBody> getData();
}
//得到Call对象
public class Net implements Api {
private static final Net ourInstance = new Net();
private final Api mApi;
public static Net getInstance() {
return ourInstance;
}
private ElNet() {
mApi = NetUtil.getInstance().getRetrofit().create(Api.class);
}
@Override
public Call<ResponseBody> getData() {
return mApi.getData();
}
}
//开始请求
Call<ResponseBody> call = getData();
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
- 这样一个请求部分就算做完了,接下来是需要对状态进行判断,因为虽然请求已经发出但是是否请求成功是不确定的,Retrofit的回调接口中实现了两个方法,分别是成功和失败,但是需要注意,这里的失败回调仅仅是当由于自身网络原因产生的失败才会回调进
onFailure()
方法中,那些因为服务器原因造成的请求失败需要在onResponse()
中进行进一步判断,这里可以通过response.isSuccessful()
判断是否真的网络请求成功,其实这个方法的原理也就是通过判断响应码是否为200(200为成功,其他都是错误)。
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
//请求成功
}else {
//请求失败
LogUtil.setLog("failed: " + "\n 错误码:" + response.code() + " 错误信息:" + response.body());
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
//请求失败
}
});
得到最终数据后,根据实际的项目需求进行解析,常见的解析方式是拿到Json字符串,使用Gson等三方解析库将Json转换成对应实体类进行下一步操作。
其实如果是使用Retrofit进行数据解析的话,它是可以通过添加Gson转换器GsonConverterFactory
进行直接转换的,只需要在编写网络请求参数接口时直接给Call对象指定想要生成的实体类对象便可以回调到想要的实体类对象。
但是这样做有很大的局限性,首先你得有个好后台,你得有个好后台,你得有个好后台!重要的话说三遍!!!否则不知道数据就直接交给库进行转换,到时候各种bug都不知道错在哪里,其次这样会导致在项目中是完全看不到Json结构的,需要借助别的工具先请求该接口,看到实际的结构后生成实体类才能进行。当然,如果你有个很给力并且负责的后台,可以保证数据不会有一些奇葩的错误,并且每个接口文档都有完善的Json结构示例的话完全可以直接使用这种方式。以上说的是常规情况下的处理方式,在正常开发中,很多接口除开请求失败意外本身就是被设计成两种结果的,比较常见的例如一个登陆接口,会有登陆成功和失败两种结果,当成果是返回用户的信息,token等等,失败后返回失败原因如账号密码错误等等。这里就会涉及到处理两种完全不同的Json结构。在这种情况下Json会有一个
status
属性,当status为FALSE时则为失败,需要返回对应错误消息,为TRUE时成功按正常流程进行下去。所以需要先判断Json是否有Status状态,并且根据状态值进行下一步操作,由于Status状态的不确定性,还需要使用try/catch进行异常捕获,大致实现如下:
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
//请求成功
String json = null;
try {
json = response.body().string();
try {
JSONObject jsonObject = new JSONObject(json);
String status = jsonObject.getString("status");
if (status.equals("error")) {
String msg = jsonObject.getString("msg");
if (msg == null)
msg = "未知原因";
//失败情况
return;
}
} catch (JSONException e) {
e.printStackTrace();
}
MyApplication.gson.fromJson(json, reBody);
} catch (IOException e) {
e.printStackTrace();
}
} else {
//请求失败
LogUtil.setLog("failed: " + "\n" + response.code() + " " + response.body());
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
- 看起来是不是非常繁琐,而且里面还有很多细节其实是没有判断的,比如Json为空的情况等等,加上各种回调方法,这样的方法应该没有人愿意频繁的重复书写吧,但是由于每个网络请求的数据结构都是不同的,因此需要生成的实体类对象也不尽相同,常规的写法无法针对不同的数据得到不同的结果。这里便需要用的泛型的概念,不为方法指定某个具体的类型,而是我给你什么类型你就按照什么类型来解析。代码如下:
/**
* presenter工具类
* 需要返回数据
*
* @param reBody 需要转出的类型
* @param call 网络请求call模型
* @param callBack 网络回调
*/
public <T> void getData(final Class<T> reBody, Call<ResponseBody> call, final PublicCallback.NetCallback<T> callBack) {
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
String json = response.body().string();
LogUtil.setLog(reBody.getSimpleName() + " json :" + "\n" + json);
dealJson(json, callBack);
T t = MyApplication.gson.fromJson(json, reBody);
if (t != null)
callBack.onSuccess(t);
else
callBack.onFailed("没有数据");
} catch (IOException e) {
LogUtil.setLog(reBody.getSimpleName() + "error: " + "\n" + e.toString());
callBack.onFailed(e.getMessage());
}
} else {
callBack.onFailed(response.code() + "");
LogUtil.setLog(reBody.getSimpleName() + "error: " + "\n" + response.code() + " " + response.body());
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
callBack.onFailed(t.getMessage());
LogUtil.setLog(reBody.getName() + "error: " + t.toString());
}
});
}
/**
* 对json进行初步处理,避免NULL及错误
*/
private void dealJson(String json, final PublicCallback.BaseCallback callBack) {
if (json.trim().equals("")) {
callBack.onFailed("");
return;
}
try {
JSONObject jsonObject = new JSONObject(json);
String status = jsonObject.getString("status");
if (status.equals("error")) {
String msg = jsonObject.getString("msg");
if (msg == null)
msg = "未知原因";
callBack.onFailed(msg);
return;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
- 上面方法需要传入三个参数,分别是需要解析和生成的实体类类型,Retrofit的网络请求对象Call,对应的回调接口,这里接口为了统一我也根据情况定义了几个标准的接口类型:
public class PublicCallback {
public interface BaseCallback {
void onFailed(String msg);
}
public interface NetCallBack extends BaseCallback {
void onSuccess();
void onFailed(String msg);
}
public interface NetCallback<T> extends BaseCallback {
void onStart();
void onSuccess(T t);
void onFailed(String msg);
}
public interface NetCallbackVerifica<T> {
void onVerification();
void onStart();
void onSuccess(T t);
void onFailed(String msg);
}
public static class SimpleCallback<T> implements NetCallback<T> {
@Override
public void onStart() {
}
@Override
public void onSuccess(T t) {
}
@Override
public void onFailed(String msg) {
}
}
public static class SimpleCallbackVerifica<T> implements NetCallbackVerifica<T> {
@Override
public void onVerification() {
}
@Override
public void onStart() {
}
@Override
public void onSuccess(T t) {
}
@Override
public void onFailed(String msg) {
}
}
}
通过这样的方式就可以实现全局网络请求的统一解析,当发现网络解析有遗漏或者需要修改参数解析逻辑的时候,便能实现全局修改,减少了很大的工作量以及个别出错的可能性。