xUtils框架中HttpUtils模块源码剖析

2017/3/20 posted in  开源框架  

  xUtils是github上的一个Android开源工具项目,xUtils包含了很多实用的android工具,其中HttpUtils模块是处理网络连接部分。在我上一个项目《数据铁笼》中经常用到,作为与服务器连接的工具,所以想要深入学习一下。
  项目最近更新到了3.0,我项目中用到的还是2.6有些方面可能和最新的有出入,请以作者最新的代码为准。
  https://github.com/wyouflf/xUtils3
  惯例是例举出我在学习中借鉴的一些大神的博客,以此来表示对他们的尊重

  1. HTTP 协议详解
  2. Android开源项目xUtils HttpUtils模块分析
  3. Future和FutureTask
  4. xUtils异步HTTP源码分析

  上面这些文章讲的都特别好,大家如果有时间可以都看看。

  

一 . 以前的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里面还有很多细节我没有说到,大家可以自己去研究下。