概述:此篇为Android网络请求框架的第二弹,基于原始的请求Api HttpUrlConection的封装
一 我们为什么需要封装网络请求。
1让我们来看看,不使用封装的情况下,是如何使用HttpUrlConnection请求的。
a 让我们来回顾一些基本的概念。
①主线程不能执行耗时操作。
所以我们使用的HttpUrlConection需要在子线程中进行。
②子线程中不能修改UI。
所以我们需要将在子线程中请求的数据,传回到到主线程。
③基于以上两点,我们使用AsyncTask异步任务来做到以上两点。(这里对AsyncTask不熟悉的同学请自行百度用法)
b 一个请求的基本步骤。
① 开始一个异步任务。
HttpAsyncTask httpAsyncTask=new HttpAsyncTask(context,method,params,listener);
httpAsyncTask.execute(url);
在构造方法传入基本参数了和一个接口 我们将会通过这个接口将数据传出去。
② 在主线程中得到结果
doGet(MainActivity.this, "get", url, null, new HttpResponseListener() {
@Override
public void onRequestResult(String response) {
//这里可以对UI进行操作使用 response
}
});
2 从这里可以看到,我们每发出一个请求,都需要new一个异步任务。
二 封装之后一个网络请求的基本步骤 。
1 传入一个Url 和参数集合。
不管是get请求还是post请求 都会有Url 和参数集合不同个是get请求需要将url和参数集合拼接在一起,而post请求需要将参数拼接之后传入到请求体body中。
2 开始一个请求,显示一个正在请求的Loading
3 开始下载任务,我们需要传入 url和参数集合
4 结束一个请求,关闭Loading,在主线程中使用请求后的数据。
三 封装网络请求的步骤。
1定义一个请求结果的实体类 Response 包含四个字段。
private boolean error;
private int errorType;
private String errorMessage;
private String result;
这里isError是调用api成功与否,errorType是错误类型(如果成功则为0)errorMessage是错误信息(如果成功则为空)
result 是成功请求返回的数据结果(如果失败则返回为空) 我们这里的思想就是 只要发送一个请求 一定会返回一个Response对象 像没有网络和url不正确的情况 都会返回一个response对象 这里的error 就为true errorType 可以定义为-1 errorMessage则为“当前网络不可用,请稍后再试”result自然就为空了“”。其他异常情况同理
2定义接口 HttpRequestListener 提供 一系列请求过程需要用到的方法
public interface HttpRequestListener<T> {
/**
* 开始请求时
*/
void onStartRequest();
/**
* 将一个字符串解析成一个对象传出来
*
* @param t
*/
void onRequestResult(T t);
/**
* 请求结果
*
* @param response
*/
void onRequestResult(Response response);
}
3 一个抽象类 HttpResponseListener实现HttpResponseListener接口,提供一些方法,在请求结果返回之前和返回之后调用。
public abstract class HttpResponseListener<T> implements HttpRequestListener<T> {
private Class<T> cls;
private Context context;
private boolean isShowLoading;
private CustomLoadingController controller;
/**
* 默认显示Loading
*
* @param context
* @param cls
*/
public void init(Context context, Class<T> cls) {
this.context = context;
this.cls = cls;
this.isShowLoading = true;
}
/**
* 由外界选择是否显示Loading
*
* @param context
* @param cls
* @param isShowLoading
*/
public void init(Context context, Class<T> cls, boolean isShowLoading) {
this.context = context;
this.cls = cls;
this.isShowLoading = isShowLoading;
}
/**
* 开始请求之前的动作 比如显示进度条
*/
@Override
public void onStartRequest() {
if (context != null && isShowLoading) {
if (!((Activity) context).isFinishing()) { //判断当前页面是否关闭
if (controller == null) {
controller = new CustomLoadingController(context);
controller.create();
}
controller.show();
}
}
}
@Override
public void onRequestResult(Response response) {
if (isShowLoading && controller != null) {
controller.dismiss();
}
if (response.getErrorType() == -200) {
showErrorMessage("当前网络不可用,请稍后再试");
} else if (response.isError()) {
Log.i("errorMessage", response.getErrorMessage());
} else {
String strResponse = response.getResult();
T t = new Gson().fromJson(strResponse, cls);
onRequestResult(t);
}
}
public void showErrorMessage(String errorMessage) {
Toast.makeText(context, errorMessage, Toast.LENGTH_SHORT).show();
}
}
在这个抽象类中 我们实现接口中的部分方法,比如 void onStartRequest(); 在一个请求开始之前的方法 在这个方法中 我们实现了 显示一个Loading 。其中 void onRequestResult(T t);方法并没有具体实现 在 void onRequestResult(Response response) 方法中是这样实现的:
if (isShowLoading && controller != null) {
controller.dismiss();
}
if (response.getErrorType() == -200) {
showErrorMessage("当前网络不可用,请稍后再试");
} else if (response.isError()) {
Log.i("errorMessage", response.getErrorMessage());
} else {
String strResponse = response.getResult();
T t = new Gson().fromJson(strResponse, cls);
onRequestResult(t);
}
首先是关闭了Loading 然后对Response进行判断 当result中有值时 将字符串转换成对象 然后 通过 onRequestResult(t)方法调出去。
4 真正执行请求的异步任务 HttpAsyncTask 。
a 构造方法:
public HttpAsyncTask(Context context, String method, HashMap<String, Object> paramsMap, HttpResponseListener responseListener)
b请求之前的 onPreExecute
protected void onPreExecute() {
super.onPreExecute();
mResponseListener.onStartRequest();
}
c 在子线程中执行耗时操作的 doInBackground
protected Response doInBackground(String... strings) {
Response response = new Response();
if (mContext != null) {
if (strings[0] == null) {
response.setError(true);
response.setErrorType(-2);
response.setErrorMessage("请求地址为空");
return response;
} else if (!NetUtils.isNetworkConnected(mContext)) {
response.setError(true);
response.setErrorType(-200);
response.setErrorMessage("网络没有连接");
} else {
try {
return new DownUtils().getResponse(strings[0], method, mParamsMap);
} catch (UnsupportedEncodingException e) {
response.setError(true);
response.setErrorType(-1);
response.setErrorMessage("下载异常");
}
}
}
return null;
}
首先对网络和url进行判断 返回一个Response 。当网络和url没有问题时,执行下载操作 new DownUtils().getResponse(strings[0], method, mParamsMap);
d 在执行耗时操作后返回的onPostExecute
protected void onPostExecute(Response response) {
if (response == null) {
Response response1 = new Response();
response1.setError(true);
response1.setErrorType(-1);
response1.setErrorMessage("请求异常");
response1.setResult("");
mResponseListener.onRequestResult(response);
} else {
mResponseListener.onRequestResult(response);
}
}
首先还是对Response进行判断 然后通过接口 mResponseListener.onRequestResult(response); 将请求结果传出去。我们已经在抽象类中进行了处理。
5 正在下载的工具类 DownUtils
public Response getResponse(String url, String method, HashMap<String, Object> paramsMap) throws UnsupportedEncodingException {
if (method.equals("get")) {
String newUrl = getUrl(url, paramsMap);
return getResponseByGet(newUrl);
} else if (method.equals("post")) {
return getResponseByPost(url,getUrlData(paramsMap));
} else {
return null;
}
}
我们提供一个方法 传入了url 参数集合 标识字符串method 返回一个 Response .还提供了 一系列的下载方法 和拼接字符串的方法。根据项目需要我们还可以提供 一些加密的方法。
四 项目使用时需要注意的地方。
1 一个常量类Constant 保存用到的url
public class Constant {
public static String LOGINURL = "http://192.168.31.141:8080/ServerDemo/loginServlet";
}
2 一个请求类
public class ApiClass {
/**
* 登录方法
* @param context
* @param params
* @param listener
*/
public void loginRequestByGet(Context context, HashMap<String, Object> params, HttpResponseListener listener){
String url = Constant.LOGINURL;
sendRequest(context,url,"get",LoginBean.class,params,listener);
}
/**
* 发送请求
* @param context
* @param url
* @param method
* @param params
* @param listener
*/
public void sendRequest(Context context, String url,String method,Class cls, HashMap<String, Object> params, HttpResponseListener listener) {
if (listener != null) {
listener.init(context, cls);
HttpAsyncTask httpAsyncTask = new HttpAsyncTask(context, method, params, listener);
httpAsyncTask.execute(url);
}
}
}
3 具体使用的地方。
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new ApiClass().loginRequestByGet(MainActivity.this, map, new HttpResponseListener<LoginBean>() {
@Override
public void onRequestResult(LoginBean loginBean) {
Log.i("xixi", loginBean.toString());
}
});
}
});
五 总结
1其实写下来感觉有些细节的地方处理的并不好,但是总体的框架是可以的。
2 具体的细节根据具体的项目可以自由更改。