xUtils是github上的一个Android开源工具项目,xUtils包含了很多实用的android工具,其中HttpUtils模块是处理网络连接部分。在我上一个项目《数据铁笼》中经常用到,作为与服务器连接的工具,所以想要深入学习一下。
项目最近更新到了3.0,我项目中用到的还是2.6有些方面可能和最新的有出入,请以作者最新的代码为准。
https://github.com/wyouflf/xUtils3
惯例是例举出我在学习中借鉴的一些大神的博客,以此来表示对他们的尊重
上面这些文章讲的都特别好,大家如果有时间可以都看看。
一 . 以前的HttpClient 方法
首先我先说下以前如果使用Http协议的话,我会使用Apache的HttpClient,大体上分为六步:
以Post方法为例
//第一步创建DefaultHttpClient对象
HttpClient httpClient=new DefaultHttpClient();
//第二步创建HttpPost
HttpPost post=new HttpPost("http:192.168.3.1:8080/login.jsp");
//第三步对传递参数进行封装
List<NameValuePairs> params=new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("name",name));
params.add(new BasicNameValuePair("pass",pass));
//第四步为post设置请求参数
post.setEntity(new UriEncodedFormEntity(params,HTTP.UTF_8));
//第五步发送post请求
HttpResponse respones=httpClient.execute(post);
//第六步解析
String msg=EntityUtils.toString(respones.getEntity());
为什么这么详细的介绍Apache 的HttpClient,因为2.6版本中,使用的就是HttpClient,但是在3.0版本中已经替换HttpClient为UrlConnection,这个回来还要再研究下。现在就当复习一遍Apache的HttpClient了吧。
二 . 上Demo,看用法
废话不多说,先看看怎么用
//第一步设置请求参数的编码
RequestParams params = new RequestParams(); // 默认编码UTF-8
//第二步根据你服务器要求的参数,进行传参
params.setHeader("Content-Type", "application/json; charset=utf-8");
JSONObject object = new JSONObject();
object.put("policenum", uLogin.getPolicenum());
object.put("taskid", taskid);
String json = JSON.toJSONString(object);
StringEntity entity=null;
try {
entity = new StringEntity(json, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
params.setBodyEntity(entity);
//第三步调用HttpUtiles的send方法,可重写这两个方法,作为回调
http.send(HttpRequest.HttpMethod.POST, url,params,
new RequestCallBack<String>() {
@Override
public void onFailure(HttpException exception, String msg) {}
@Override
public void onSuccess(ResponseInfo<String> Response) {});
}
)
三 . 解析源码
1 . 构造函数
首先我们使用HttpUtils模块,一般都要使用new一个HttpUtils出来,默认的构造函数为
public HttpUtils(int connTimeout, String userAgent) {
HttpParams params = new BasicHttpParams();
ConnManagerParams.setTimeout(params, connTimeout);
HttpConnectionParams.setSoTimeout(params, connTimeout);
HttpConnectionParams.setConnectionTimeout(params, connTimeout);
if (TextUtils.isEmpty(userAgent)) {
userAgent = OtherUtils.getUserAgent(null);
}
HttpProtocolParams.setUserAgent(params, userAgent);
ConnManagerParams.setMaxConnectionsPerRoute(params, new ConnPerRouteBean(10));
ConnManagerParams.setMaxTotalConnections(params, 10);
HttpConnectionParams.setTcpNoDelay(params, true);
HttpConnectionParams.setSocketBufferSize(params, 1024 * 8);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
}
这里就不全部截出来了,这里会设置Http协议的一些参数,这些参数往往对于不同的服务器都是固定的,比如采用Http1.1,设置超时时间等等,当然你也可以自己设置,不调用它默认的也可以,根据自己的公司业务要求自己设置。
2 . send()方法
public <T> HttpHandler<T> send(HttpRequest.HttpMethod method, String url, RequestParams params, RequestCallBack<T> callBack) {
if (url == null)
throw new IllegalArgumentException("url may not be null");
HttpRequest request = new HttpRequest(method, url);
return sendRequest(request, params, callBack);
}
解释下上述参数:
第一个参数 method:HttpRequest.HttpMethod 里面设定好的10种;
第二个参数 url:服务器地址或者接口地址;
第三个参数 params:传给服务器的参数;
第四个参数 callBack:看名字就知道,服务器返回消息后回调的接口,可以重写其中的方法;
HttpHandler实际上是一个异步AsyncTask,后面我们会详细解释
3 . 重点解析 HttpRequest
全部代码就不截图了HttpRequest(HttpMethod method, String uri),作为参数
public HttpRequest(HttpMethod method, String uri) {
super();
this.method = method;
setURI(uri);
}
(1). setRequestParams方法
主要干了三件事:
①保存Header头部
List<RequestParams.HeaderItem> headerItems = param.getHeaders();
②保存entity整体
HttpEntity entity = param.getEntity();
this.setEntity(entity);
③保存回调函数
entity.setCallBackHandler(callBackHandler);
至此Http协议中的大部分信息都保存到了HttpRequest中,函数最后运行:
handler.executeOnExecutor(EXECUTOR, request);
4 . HttpHandler类:
HttpHandler继承自PriorityAsyncTask,前面我们已经说了HttpHandler实际上是一个异步AsyncTask,让我们直接去HttpHandler继承自PriorityAsyncTask里面看executeOnExecutor方法:
//EXECUTOR:线程池
//params:参数也就是HttpRequest
public final PriorityAsyncTask<Params,Progress,Result>executeOnExecutor(Executor exec,Params... params) {
if (mExecuteInvoked) {
throw new IllegalStateException("Cannot execute task:"
+ " the task is already executed.");
}
mExecuteInvoked = true;
onPreExecute();
mWorker.mParams = params;
exec.execute(new PriorityRunnable(priority, mFuture));
return this;
}
解释下:
mWorker.mParams = params;
...
private static abstract class WorkerRunnable<Params,Result>implements Callable<Result> {
Params[] mParams;
}
mWorker是一个Callable,用于后面的调用这点很重要,因为最后其实就是调用的mWork;
exec.execute(new PriorityRunnable(priority, mFuture));
调用PriorityObject的run方法,会去调用mFuture的run方法,mFuture是一个FutureTask,为什么前面说最后其实会去调用mWorker呢,注意mFuture定义的地方:
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
...
}
};
我们去找FutureTask的run函数,翻开FutureTask的源码,找到run的源码
public void run() {
....
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//////////////////////////////这里调用的mWorker的call方法
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
....
}
}
也就是说最终会去调用mWork的call()方法,那就让我们看看call()方法
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
....
return postResult(doInBackground(mParams));
}
};
最终会去最后的BOSS级代码,HttpHandler的doInBackground,其实上面的一系列方法很好理解,就是为了创建异步AsyncTask,以前都只是直接拿AsyncTask来用,根本没想过自己实现一个,分析源码也算是对我自己的一个学习,好了不感概了看一下doInBackground():
首先肯定是要取出来之前保存在HttpRequest里面的各种参数.
request = (HttpRequestBase) params[0];
requestUrl = request.getURI().toString();
接下来就是重头戏发送请求
ResponseInfo<T> responseInfo = sendRequest(request);
看一下sendRequest()方法:
...
ResponseInfo<T> responseInfo = null;
if (!isCancelled()) {
HttpResponse response = client.execute(request, context);
responseInfo = handleResponse(response);
}
return responseInfo;
...
最后调用
this.publishProgress(UPDATE_SUCCESS, responseInfo);
....
case UPDATE_SUCCESS:
if (values.length != 2) return;
this.state = State.SUCCESS;
//回调callback
callback.onSuccess((ResponseInfo<T>) values[1]);
break;
xUtils里面还有很多细节我没有说到,大家可以自己去研究下。