项目的代码中看到了这样一种的方法(就称之为“Gson泛型”吧)来封装网络请求,简单、方便、条理清晰,自己用后感觉非常nice,特此记录下来。
首先是网络请求需要注意的几个点:
第一:如何使用规范简洁的代码来实现网络架构?
第二:数据解析要用什么方法?
第三:网络异常如何统一处理?
1.定义网络封装类
/**
-
网络请求封装
-
Created by kuaigeng01 on 2018/1/20.
*/
public class ServerRequest {
public static final int Error_from_net = 1;
public static final int Error_from_server = 2;
public static final int Error_from_parser = 3;/**
- 发起网络请求,回调在ui线程
- @param tagPrefix 请求前缀:用于设置请求的Tag,这样能根据tag取消请求
- @param tClass 需要返回的数据类型
- @param url 请求url
- @param params 请求参数
- @param callback 回调
- @param <T> 数据类型
*/
public static <T> void sendRequestCallbackInMainThread(String tagPrefix, Class<T> tClass,
String url, Map<String, String> params, RequestCallback<T> callback) {
sendRequest(tagPrefix, true, tClass, url, params, callback);
}
/**
-
发起网络请求,回调在非ui线程
-
@param tagPrefix 请求前缀:用于设置请求的Tag,这样能根据tag取消请求
-
@param tClass 需要返回的数据类型
-
@param url 请求url
-
@param params 请求参数
-
@param callback 回调
-
@param <T> 数据类型
*/
public static <T> void sendRequestCallbackInBackgroundThread(String tagPrefix, Class<T> tClass,
String url, Map<String, String> params, RequestCallback<T> callback) {
sendRequest(tagPrefix, false, tClass, url, params, callback);
}
public static void cancelRequest(String tagPrefix, String url) {
if (TextUtils.isEmpty(url)) {
return;
}
String tag = createTag(tagPrefix, url);
RequestQueue queue = VolleyHelper.getInstance().getRequestQueue();
queue.cancelAll(tag);
queue = VolleyHelper.getInstance().getAsyncRequestQueue();
queue.cancelAll(tag);
}private static <T> void sendRequest(String tagPrefix, boolean callbackInMainThread,
final Class<T> tClass, final String url, Map<String, String> params,
final RequestCallback<T> callback) {
sendRequestImpl(tagPrefix, callbackInMainThread, url, params,
new Response.Listener<String>() {
@Override public void onResponse(String s) {
ServerResult<T> result = ServerDataParser.simpleParseJsonToPojo(tClass, s);
if (null != result) {
if (result.getStatus() == 200) {
if (null != callback) {
callback.onSuccess(result.getData());
}
} else {
if (null != callback) {callback.onFailure(Error_from_server, TextUtils.isEmpty(result.getMsg())?"":result.getMsg()); } } } else { if (null != callback) { callback.onFailure(Error_from_parser, "parse json failure"); } } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError volleyError) { if (null != callback) { callback.onFailure(Error_from_net, String.valueOf(volleyError)); } } });
}
private static void sendRequestImpl(String tagPrefix, boolean callbackInMainThread, String url,
Map<String, String> params, Response.Listener<String> listener,
Response.ErrorListener errorListener) {if (TextUtils.isEmpty(url)) { throw new IllegalArgumentException("url can't be empty!!!"); } if (TextUtils.isEmpty(tagPrefix)) { throw new IllegalArgumentException("tagPrefix can't be empty!!!"); } if (null == params) { params = getCommonRequestParams(); } else { params.putAll(getCommonRequestParams()); } AcosStringRequest request = new AcosStringRequest(url, params, listener, errorListener); RequestQueue queue; if (callbackInMainThread) { queue = VolleyHelper.getInstance().getRequestQueue();
// queue = PluginAnswerLiveCooperation.getInstance().getRequestQueue();
} else {
// queue = PluginAnswerLiveCooperation.getInstance().getAsyncRequestQueue();
queue = VolleyHelper.getInstance().getAsyncRequestQueue();
}
String tag = createTag(tagPrefix, url);
request.setTag(tag);
queue.cancelAll(tag);
queue.add(request);
}
public interface RequestCallback<T extends Object> {
void onSuccess(T data);
void onFailure(int from, String errorMsg);
}
private static String createTag(String tagPrefix, String url) {
return tagPrefix + "_" + StringUtils.calcMd5(url);//用于 同一个url请求,cancel掉掉旧掉
}
private static HashMap<String, String> getCommonRequestParams() {
HashMap<String, String> map = new HashMap<>();
User user = AnswerDataCenter.getInstance().getHostUser();
String token = user == null ? "" : user.token;
map.put("token", token);
map.put("_appid", "10000");
return map;
}
}
这个类做了这么几件事:
1)定义三种请求异常的类型,分别是网络异常、服务器异常、解析异常。
2)定义一个发起网络请求的方法,回调在UI线程(同时定义了一个回调在非UI线程的方法),各个参数的含义在代码中已经注释的很明白。重要的一点需要注意的是泛型的参数。
3)定义一个取消网络请求的方法,在发起请求的时候传入了一个tag设置到请求对象中,取消的时候也会用到那个tag。
4)例子中使用的是Volley网络请求库,在sendRequestImpl中设置Volley的请求参数。
5)定义RequestCallback接口,Volley回调使用,传入的参数为用户定义的实体类对象,定义为泛型的原因是为了实现Gson统一解析数据。
sendRequest方法中调用sendRequestImpl方法请求数据,在onResponse方法回调中处理返回的数据,这个时候就会调用服务端数据解析器ServerDataParser.simpleParseJsonToPojo(tClass,s),第一个参数是json字符串解析成的最终类。接下来看数据解析器里面是如何处理服务器返回的json字符串。
2.服务端数据解析器
public class ServerDataParser {
private static final TypeAdapter<Boolean> booleanAsIntAdapter = new TypeAdapter<Boolean>() {
@Override
public void write(JsonWriter out, Boolean value) throws IOException {
if (value == null) {
out.nullValue();
} else {
out.value(value);
}
}
@Override
public Boolean read(JsonReader in) throws IOException {
JsonToken peek = in.peek();
switch (peek) {
case BOOLEAN:
return in.nextBoolean();
case NULL:
in.nextNull();
return null;
case NUMBER:
return in.nextInt() != 0;
case STRING:
String val = in.nextString();
if (TextUtils.equals("1", val)) {
return true;
}
if (TextUtils.equals("0", val)) {
return false;
}
return Boolean.parseBoolean(val);
default:
throw new IllegalStateException("Expected BOOLEAN or NUMBER but was " + peek);
}
}
};
public static Gson createGson() {
Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd hh:mm:ss").excludeFieldsWithoutExposeAnnotation().
registerTypeAdapter(Boolean.class, booleanAsIntAdapter).
registerTypeAdapter(boolean.class, booleanAsIntAdapter).create();
return gson;
}
public static <T> ServerResult<T> simpleParseJsonToPojo(Class<T> tClass, String json) {
if (TextUtils.isEmpty(json)) {
return null;
}
Type type = new ParameterizedTypeImpl(ServerResult.class, new Class[]{tClass});
try {
Gson gson = createGson();
ServerResult<T> parseResult = gson.fromJson(json, type);
return parseResult;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
数据解析器设置了Gson的一些参数,然后就调用Gson的泛型,得到传入实体类的Type对象,用来供gson.fromJson()使用。
3.Gson泛型
定义gson泛型类
public class ParameterizedTypeImpl implements ParameterizedType {
private final Class raw;
private final Type[] args;
public ParameterizedTypeImpl(Class raw, Type[] args) {
this.raw = raw;
this.args = args != null ? args : new Type[0];
}
@Override
public Type[] getActualTypeArguments() {
return args;
}
@Override
public Type getRawType() {
return raw;
}
@Override
public Type getOwnerType() {
return null;
}
}
Gson泛型的作用,就是将传进来的实体类转换成gson能够识别的Type类型。
4.数据实体类
移动端请求接口得到的数据,大致都是统一格式的,例如下面这种:
public class ServerResult<T> {
@SerializedName("status")
@Expose
private int status;
@SerializedName("msg")
@Expose
private String msg;
@SerializedName("result")
@Expose
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
这里是一个实体类的基类,之后所有的实体类都会继承它。
一个状态码字段status,一个错误信息字段msg,一个result对象。
这里的result对象定义为泛型,是为了编译之后的统一解析。
总结一下封装步骤:
1.创建ServerRequest网络请求类,调用sendRequest方法请求网络,tClass泛型类,继承自ServerResult。
2.网络请求成功,返回json字符串s,然后调用服务器端解析器ServerDataParser.simpleParseJsonToPojo(tClass,s);网络请求失败,则调用callback.onFailure(),传入失败类型和失败原因。
3.在simpleParseJsonToPojo方法中,调用
Type type=ParameterizedTypeImpl(ServerResult.class,new Class[]{tClass});
返回泛型的Type类型,然后调用
ServerResult<T> result=gson.fromJson(json,type);
最后得到Gson解析的实体对象。
网友评论