您的位置:首页 > 理论基础 > 计算机网络

xUtils封装一个网络工具类

2017-05-03 13:15 393 查看
如果让你实现这样一个需求:封装一个网络工具类,用于项目的基础网络请求。然后实现如下调用:

/**
*这个网络工具类使用了xUtils框架包实现网络请求,我们要做到就是再封装一层。
*/
newHttp.getObject(newHttp.GET, Banner.class, null,String.format(HttpUrl.NEW_AD, "app_open"),new onReuslt<Banner>() {
@Override
public void onSuccess( final Banner result) {
// TODO Auto-generated method stub
IDouKouApp.resultOk(new onOptions() {
@Override
public void isok() {
// TODO 自动生成的方法存根
if(result.getBanners() != null && result.getBanners().size()>0){
open_banners = result.getBanners().get(0);
Glide.with(IDouKouActivity.this).load(open_banners.getSrc());
}
}
@Override
public void isNO() {
// TODO 自动生成的方法存根
LogUtils.e("进入首页时显示广告界面接口失败>>>>>>>>>>>>");
}
});
}
@Override
public void onFaild(int errCord, String errMsg) {
// TODO Auto-generated method stub
LogUtils.e("广告接口失败:" + errCord);
}
@Override
public void onLoadCatch(String json) {
// TODO Auto-generated method stub

}
});
/**
* 异常拦截机制回调,定义在application类中,用于拦截网络返回的异常信息
* @param options
*/
public static void resultOk(onOptions options){
try {
options.isok();
} catch (Exception e) {
// TODO: handle exception
options.isNO();
e.printStackTrace();
}
}


从上面我们可以看出,网络框架工具类首先在BaseActivity里面创建得到,然后子Activity实现BaseActivity来拿到这个httpUtils来进行网络请求操作,然后通过传入请求方式、请求的JavaBean对象、请求参数、请求的Url,再定义了一个response接口,实现三个返回方法来拿到数据,在当前的activity进行UI更改操作。

下面我们就来封装这样的一个网络请求工具类。先定义成员变量:

/**
* 网络请求方法的 常量
*/
public final int POST = 100;
public final int PUT = 200;
public final int GET = 300;
public final int DELETE = 400;
public final int HEAD = 500;
/**
* 解析类型
*/
private final int LIST = 1;
private final int OBJECT = 2;
private final int STRING = 0;
/**
* 是否缓存json
*/
public boolean isCache = false;
private HttpHandler<String> startHandler;


这里,我们定义了网络请求的类型和网络返回的解析类型,最后就是看是否需要缓存Json,后面这个HttpHandler是xUtils的网络处理类,为了让大家快速的掌握xUtils网络请求,大家可以移步到xUtils源码解析

然后呢?我们知道每一个公司都会有自己的服务器,你去做安卓开发,也不可能让你单独搞一台服务器,所以我们需要搞清楚,我们的网络请求方式及和服务器之间的通讯协议之间的传参是怎么样的,每个公司都是不一样的,所以这个是网络框架搭建比较繁琐的一个流程,建立Http请求需要三次握手,这个三次握手的每一次握手其实都做了很多事情,TCP协议的三次握手四次挥手

参考:http://blog.csdn.net/changcsw/article/details/52103640

有些人说三次握手和四次挥手我怎么感觉不到?那是因为三次握手和四次挥手是发生在传输层协议的,Http协议是Tcp的上层协议,我们访问网络请求,只知道拿到一个链接,在基础的Socket网络通讯中可能还需要一个端口,然后就拿到网络请求返回的状态码做操作,是接触不到三次握手和四次挥手.

然后又有人说那网络请求需要传的头部信息和Cookie是因为什么呢?

上面我们提到,每个公司都会有自己的服务器,我们的手机需要请求这样的服务器拿数据,服务器自然需要一些校验机制对一些敏感信息进行加密或者校验,来确定请求的合法性。

/**
* 获取包含规则的头信息请求对象
* @return
*/
private RequestParams getHeaderRequestParams() {
RequestParams params = new RequestParams();
// 平台信息
params.addHeader("PlatForm", android.os.Build.MODEL);
// 系统-版本号
params.setHeader("System", "Android" + android.os.Build.VERSION.RELEASE);
// 唯一标示符
if(getShared().getString("uuid",true) == null){
regist(new onNetWork() {
@Override
public void accessNetwork() {
// TODO Auto-generated method stub
//单纯的注册(不做任何操作);
}
});
}
params.addHeader("UUID", getShared().getString("uuid",true));
// 应用版本号
params.addHeader("Version", IDouKouApp.getVerName());
params.addHeader("Accept", "application/json");
return params;
}
/**
* 将Map集合转换成 Xtuils请求参数对象
* RequestParams是xUtils的一个参数封装类,我们需要拿它来传递参数
* @param params
* @return
*/
private RequestParams getParamas(Map<String, String> params,int method) {
RequestParams rquestParames = getHeaderRequestParams();
if (params != null) {
for (Map.Entry<String, String> flag : params.entrySet()) {
if (method == GET) {
rquestParames.addQueryStringParameter(flag.getKey(),flag.getValue());
} else {
rquestParames.addBodyParameter(flag.getKey(),flag.getValue());
}
}
}
return rquestParames;
}
/**
* 获取加密请求的参数
* @param params
* @param method
* @return
*/
private RequestParams getSecurityParamas(Map<String, String> params,int method) {
RequestParams rquestParames = getHeaderRequestParams();
if (params != null) {
JSONObject json = new JSONObject();
for (Map.Entry<String, String> flag : params.entrySet()) {
try {
json.put(flag.getKey(), flag.getValue());
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
json.put("stamp", System.currentTimeMillis()/1000);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String aesKey = getShared().getString("aes_key",true);
if(aesKey != null){
String body = AES.encrypt(json.toString(), aesKey);
if (method == GET) {
rquestParames.addQueryStringParameter("body",body);
} else {
rquestParames.addBodyParameter("body",body);
}
}else{
LogUtils.e("加密参数失败");
}
}
return rquestParames;
}


有了请求方法、请求参数,我们在调用时再传入Url、数据Bean、再传入一个数据返回的接口接受数据就可以了:

/**
* 对象结果处理接口
* @param <T>
* @method onSuccess 访问网络成功方法
* @method onFailure 访问网络失败方法
* @method onStart 开始访问网络方法
*/
public interface onReuslt<T> {
void onLoadCatch(String json);
void onSuccess(T result);
void onFaild(int errCord, String errMsg);
}


接下来我们就来看看基础的网络请求方式:

/**
* 非加密的网络请求方法
* @param method  请求类型
* @param clazz  解析类型
* @param params  参数(可以为null)
* @param url     请求的URL
* @param result  返回传入的clazz对象(如果clazz为 String类型,则不解析 返回服务器结果)
*/
private <T> void request(final int method, final Class<?> clazz,final int type, final Map<String, String> params, final String url,
final onReuslt<T> result) {
loadLcaoJson(url,clazz, type,result);
HttpUtils http = new HttpUtils();
com.lidroid.xutils.HttpUtils.sHttpCache.clear();
RequestParams requestParams = getParamas(params, method);
startHandler = http.send(getMethod(method), url, requestParams,
new RequestCallBack<String>() {
@Override
public void onFailure(HttpException arg0, String arg1) {
// TODO Auto-generated method stub
result.onFaild(arg0.getExceptionCode(), arg1);
}
@SuppressWarnings("unchecked")
@Override
public void onSuccess(ResponseInfo<String> arg0) {
LogUtils.i("服务器json:" + arg0.result);
result.onSuccess((T) parserJson(url,arg0.result, clazz,type, false));
}
});
}
/**
* 加密方式访问网络请求 底层 为Xutils;
* @author zhangye
* @param <T>
* @param method  请求方式
* @param url请求地址URL
* @params context 上下文对象
* @param params  请求参数(此方法会加密参数)
* @param result  结果处理接口
*/
private <T> void SecurityReqeust(final int method,final Class<?> clazz, final int type,final Map<String, String> params, final String url,
final onReuslt<T> result) {
loadLcaoJson(url,clazz, type,result);
// 异步加载参数
loadAesKey(new onNetWork() {
@Override
public void accessNetwork() {
RequestParams rquestParames = getSecurityParamas(params, method);
/**
* 发起网络请求
*/
com.lidroid.xutils.HttpUtils.sHttpCache.clear();
com.lidroid.xutils.HttpUtils http = new com.lidroid.xutils.HttpUtils();
/**
* 连接超时时间
*/
http.configSoTimeout(5 * 1000);
http.configTimeout(5 * 1000);
startHandler = http.send(getMethod(method), url, rquestParames,
new RequestCallBack<String>() {
/**
* 请求失败操作处理
*/
@Override
public void onFailure(HttpException arg0,String arg1) {
result.onFaild(arg0.getExceptionCode(), arg1);
}
/**
* 请求成功操作处理
*/
@SuppressWarnings("unchecked")
@Override
public void onSuccess(ResponseInfo<String> arg0) {
LogUtils.i("服务器JSON:"+arg0.result);
try {
JSONObject json = new JSONObject(arg0.result);
if(!json.isNull("state")){
if(json.getString("state").equals("outdated") ||
getShared().getString("aes_key", true) == null){
regist(new onNetWork(){
@Override
public void accessNetwork() {
// TODO Auto-generated method stub
SecurityReqeust(method, clazz, type, params, url, result);
}
});
}else{
if((T) parserJson(url,arg0.result,clazz, type, true) == null){
result.onFaild(200, json.toString());
}else{
result.onSuccess((T) parserJson(url,arg0.result,clazz, type, true));
}
}
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 请求之前操作处理
*/
@Override
public void onStart() {
super.onStart();
}
});
}
});
}
/**
* 发起请求(并解析成对象)
* @param method
* @param clazz
* @param params
* @param url
* @param result
*/
public <T> void getObject(final int method, final Class<?> clazz,final Map<String, String> params, final String url,
final onReuslt<T> result) {
request(method, clazz, OBJECT, params, url, result);
}

/**
* 发起请求(并解析成集合)
* @param method
* @param clazz
* @param params
* @param url
* @param result
*/
public <T> void getList(final int method, final Class<?> clazz,final Map<String, String> params, final String url,
final onReuslt<T> result) {
request(method, clazz, LIST, params, url, result);
}
/**
* 发起请求(直接返回Json字符串)
* @param method
* @param params
* @param url
* @param result
*/
public <T> void getString(final int method,final Map<String, String> params, final String url,final onReuslt<T> result) {
request(method, String.class, STRING, params, url, result);
}
/**
* 加密请求并解析成对象
* @param method  请求方式
* @param clazz    类型
* @param params 请求参数
* @param url  请求URL
* @param result 回调接口
*/
public <T> void getSecurityObject(final int method,final Class<?> clazz, final Map<String, String> params,
final String url, final onReuslt<T> result) {
SecurityReqeust(method, clazz, OBJECT, params, url, result);
}

/**
* 加密请求并解析成集合
* @param method  请求方式
* @param clazz    类型
* @param params    请求参数
* @param url      请求URL
* @param result   回调接口
*/
public <T> void getSecurityList(final int method,final Class<?> clazz, final Map<String, String> params,
final String url, final onReuslt<T> result) {
SecurityReqeust(method, clazz, LIST, params, url, result);
}

/**
* 加密请求并返回json字符串
* @param method 请求方式
* @param clazz  类型
* @param params  请求参数
* @param url    请求URL
* @param result  回调接口
*/
public <T> void getSecurityString(final int method,final Map<String, String> params,final String url, final onReuslt<T> result) {
SecurityReqeust(method, String.class, STRING, params, url, result);
}


好了,基础的网络请求方法实现了!我们可以想到的就是数据的解析和下面的数据加密的网络请求方式的封装。

/**
* 解析方法
* @param json  JSON串
* @param clazz  类型
* @return clazz类型的对象
*/
@SuppressWarnings("unchecked")
public <T> T parserJson(String url,String json, Class<?> clazz, int type,boolean isEncryption) {
LogUtils.d("服务器返回的JSON:"+json);
if (json != null && !json.equals("")) {
try {
JSONObject jsonObj = new JSONObject(json);
/**
* 判断是否是加密结果
*/
String body = jsonObj.toString();
String aeskey = getShared().getString("aes_key",true);
if (isEncryption) {
body = jsonObj.getString("body");
body = AES.decrypt(body, aeskey);
/**
* 存储JSON(不存储非解析类型的json)
*/
if(type != STRING && isCache){
getShared().setString(url,body, false);
}
LogUtils.e("解密后的JSON:"+body);
if (type == OBJECT) {
return (T) JSON.parseObject(body, clazz);
}
if (type == LIST) {
return (T) JSON.parseArray(body, clazz);
}
if (type == STRING) {
return (T) body;
}
} else {
/**
* 存储JSON(不存储非解析类型的json)
*/
if(type != STRING && isCache){
getShared().setString(url, body, false);
}
if (type == OBJECT) {
return (T) JSON.parseObject(jsonObj.toString(), clazz);
}
if (type == LIST) {
return (T) JSON.parseArray(jsonObj.toString(), clazz);
}
if (type == STRING) {
return (T) jsonObj.toString();
}
}
} catch (JSONException e) {
LogUtils.e("错误的JSON:" + json);
} catch (Exception e){
LogUtils.e("解析错误:" + json);
}
}
return null;
}

/**
* 读取本地JSON
* @param <T>
* @param clazz
* @param type
*/
private <T> void loadLcaoJson(final String url,final Class<?> clazz,final int type,final onReuslt<T> result){
IDouKouApp.resultOk(new onOptions() {
@Override
public void isok() {
// TODO Auto-generated method stub
String json =   getShared().getString(url,false);
LogUtils.i("类型"+clazz.getSimpleName()+"加载本地JSON缓存:"+json);
if(json != null){
result.onLoadCatch(json);
}
}
@Override
public void isNO() {
// TODO Auto-generated method stub
}
});
}
/**
* 判断请求方式
* @author zhangye
* @param method
* @return
*/
private HttpMethod getMethod(int method) {
HttpMethod xmethod = HttpMethod.POST;
if (method == PUT) {
xmethod = HttpMethod.PUT;
} else if (method == POST) {
xmethod = HttpMethod.POST;
} else if (method == GET) {
xmethod = HttpMethod.GET;
} else if (method == DELETE) {
xmethod = HttpMethod.DELETE;
}
return xmethod;
}


加密请求,比如RSA非对称加密,如何获取服务器返回的私钥,并保存好,然后在拿出来解析数据,并加密数据返回给服务器的呢?还有就是这个RSA私钥的过期时间限制等。

/**
* 网络获取时间回调接口,得到百度服务器的时间毫秒值
* @author zhangye
*/
private interface onTime {
void onGetTime(Long time);
}


获取服务器的RSA密钥

/**
* 得到服务器的AES_KEY,结果处理接口
* @author zhangye
* @date 2015年4月1日 17:57:02
*/
private void loadAesKey(final onNetWork network) {
/**
* 获取网络时间后进行判断操作
*/
getNetTime(new onTime() {
@Override
public void onGetTime(Long time) {
/**
* 得到上次获取密钥的时间
*/
long oldTime = getShared().getLong("key_time");
LogUtils.d("得到上次获取密钥的时间:" + oldTime);
/**
* 判断密钥是否为空或过期 2592000 代表间隔为一个月
*/
if (getShared().getString("aes_key",true) == null || ((time - oldTime) / 1000) > 2592000
|| getShared().getString("uuid",true) == null) {
/**
* 与服务器交换密钥并执行后续网络请求
*/
regist(network);
} else {
/**
* 直接发起网络请求
*/
network.accessNetwork();
}
}
});
}

/**
* 跟服务器端交换密钥
* @author zhangye
* @param network 结果处理接口
* @desc 保证获取密钥之前,不会进行其他网络操作;
*/
private void regist(final onNetWork network) {
getNetTime(new onTime() {
@Override
public void onGetTime(final Long time) {
/**
* 生成public,private密钥
*/
Map<String, String> keys = RSA.genNewKeys(SharedUtils.NEW_RSA);
/**
* 生成随机UUID
*/
getShared().setString("uuid", IDouKouApp.getRadomUUID(),true);
/**
* 初始化Xutils网络请求参数
*/
RequestParams params = getHeaderRequestParams();
params.addBodyParameter("uuid", getShared().getString("uuid",true));
params.addBodyParameter("public_key",keys.get(RSA.PUBLIC_KEY_NAME));
/**
* 发起网络请求 交换public密钥并附带UUID
*/
com.lidroid.xutils.HttpUtils http = new com.lidroid.xutils.HttpUtils();
/**
* 需要修改URL
*/
startHandler = http.send(HttpMethod.POST, HttpUrl.NEW_BIN_REGIST, params,
new RequestCallBack<String>() {
@Override
public void onFailure(HttpException arg0,String arg1) {
LogUtils.e("网络错误:" + arg1);
}

@Override
public void onSuccess(ResponseInfo<String> arg0) {
String resetCode = "";
LogUtils.d("与服务器交换密钥结果:" + arg0.result);
JSONObject jsonObject;
try {
jsonObject = new JSONObject(arg0.result);
if (!jsonObject.isNull("state")) {
resetCode = jsonObject.getString("state");
}
if (resetCode.equals("OK")) {
getShared().setLong("key_time", time);
getShared().setString("aes_key",RSA.decrypt(jsonObject.getString("aes_key"), SharedUtils.NEW_RSA),true);
network.accessNetwork();
} else {
/**
* 验证失败操作 {}
*/
}
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
});
}


到这里,我们的网络请求工具类就封装完成了。我们只需要按照需求传参,当然里面也用到了可变参数、泛型、反射拿到数据Bean对象等操作。

所以我接下来为大家整理了这些Java高级特性用法:

Java反射机制

Java泛型

Java泛型方法

Java泛型通配符解释
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息