android-----XUtils框架之HttpUtils源码分析

之前我们对Volley框架源码进行了分析,知道了他适用于频繁的网络请求,但是不太适合post较大数据以及文件的上传操作,在项目中为了弥补Volley的这个缺陷,使用了XUtils框架的HttpUtils实现了文件上传的操作,上一篇博客我们通过HttpUtils实现了照片上传的实例,见:android-----基于XUtils客户端以及服务器端实现,当然文件上传的方法类似于照片上传,有时间的话单独写一篇博客介绍,这篇博客我们从源码角度来分析HttpUtils的实现原理,希望对这几天的学习做个总结:

先来回顾下HttpUtils的使用步骤:

(1)创建HttpUtils对象:HttpUtils  httpUtils = new HttpUtils();

(2)调用HttpUtils的send方法进行上传操作;

(3)或者调用download方法来进行下载任务,虽然download方法有多个重载方法并且需要传入不同个参数,但是最后他们都会归结到同一个download方法上面;

就是这么简单,那么具体在这两步中做了些什么事呢?我们就该慢慢看看啦!

先来从HttpUtils  httpUtils = new HttpUtils()开始

HttpUtils有四个构造函数,但是最终他们都会调用public HttpUtils(int connTimeout, String userAgent)这个构造函数,第一个参数是连接超时时间,默认的超时时间是15s,第二个参数是用户代理,其实也就是浏览器访问头啦:

    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)) {
            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);

        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        schemeRegistry.register(new Scheme("https", DefaultSSLSocketFactory.getSocketFactory(), 443));

        httpClient = new DefaultHttpClient(new ThreadSafeClientConnManager(params, schemeRegistry), params);

        httpClient.setHttpRequestRetryHandler(new RetryHandler(DEFAULT_RETRY_TIMES));

        httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
            @Override
            public void process(org.apache.http.HttpRequest httpRequest, HttpContext httpContext) throws org.apache.http.HttpException, IOException {
                if (!httpRequest.containsHeader(HEADER_ACCEPT_ENCODING)) {
                    httpRequest.addHeader(HEADER_ACCEPT_ENCODING, ENCODING_GZIP);
                }
            }
        });

        httpClient.addResponseInterceptor(new HttpResponseInterceptor() {
            @Override
            public void process(HttpResponse response, HttpContext httpContext) throws org.apache.http.HttpException, IOException {
                final HttpEntity entity = response.getEntity();
                if (entity == null) {
                    return;
                }
                final Header encoding = entity.getContentEncoding();
                if (encoding != null) {
                    for (HeaderElement element : encoding.getElements()) {
                        if (element.getName().equalsIgnoreCase("gzip")) {
                            response.setEntity(new GZipDecompressingEntity(response.getEntity()));
                            return;
                        }
                    }
                }
            }
        });
    }

构造函数首先在2到17行进行请求参数的设置,其中ConnManagerParams是一个final类型的类,他是HTTP协议参数的集合,主要用于客户端连接管理,第19到21行进行的是协议模式的设置,另外同样也可以使用他来进行协议属性的设置,比如设置默认端口号等等,第23行创建了HttpClient对象,注意他是使用ThreadSafeClientConnManager来进行连接管理的,也就是他是线程安全的,第25行设置他的请求重试次数,这里是默认值5次,27行添加请求拦截器,36行添加响应拦截器;

接着我们通过send来实现上传操作了,先来看看send方法:

 public <T> HttpHandler<T> send(HttpRequest.HttpMethod method, String url,
                                   RequestCallBack<T> callBack) {
        return send(method, url, null, callBack);
    }

    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);
    }

可以看到不管是调用哪个send方法,最终都会调用sendRequest(request, params, callBack)这个方法,其中request对象封装了我们的请求方法类型以及请求url,来看看sendRequest方法啦:

private <T> HttpHandler<T> sendRequest(HttpRequest request, RequestParams params, RequestCallBack<T> callBack) {

        HttpHandler<T> handler = new HttpHandler<T>(httpClient, httpContext, responseTextCharset, callBack);

        handler.setExpiry(currentRequestExpiry);
        handler.setHttpRedirectHandler(httpRedirectHandler);
        request.setRequestParams(params, handler);

        Priority priority = null;
        if (params != null) {
            priority = params.getPriority();
        }
        if (priority == null) {
            priority = Priority.DEFAULT;
        }
        handler.executeOnExecutor(EXECUTOR, priority, request);
        return handler;
    }

第3行利用我们之前在HttpUtils构造函数中创建的httpClient,以及httpContext,responseTextCharset, callBack来创建一个HttpHandler对象,其中httpContext用于保存请求的上下文信息,这个值得设置函数是:

public HttpUtils configCookieStore(CookieStore cookieStore) {
        httpContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
        return this;
    }

可以看出这里进行了Cookie属性的设置,当然,我们可以不去设置他,不设置的话就只是一个没有值得上下文对象啦,这一点在HttpUtils的全局变量定义中能充分显示出来:

private final HttpContext httpContext = new BasicHttpContext();

responseTextCharset指的是返回的编码类型,可以通过

public HttpUtils configResponseTextCharset(String charSet) {
        if (!TextUtils.isEmpty(charSet)) {
            this.responseTextCharset = charSet;
        }
        return this;
    }

来进行设置,默认的值是UTF-8类型的啦:

private String responseTextCharset = HTTP.UTF_8;

callBack就是我们调用send方法传入的RequestCallBack对象了;

接着sendRequest的第5行设置了请求有效期,第6行设置了重定向请求,这个值默认情况下是null,当然你可以通过以下方法来进行设置:

 public HttpUtils configHttpRedirectHandler(HttpRedirectHandler httpRedirectHandler) {
        this.httpRedirectHandler = httpRedirectHandler;
        return this;
    }

第7行将刚刚创建的HttpHandler对象作为参数设置到了request请求中,第9到15行是用来设置当前所执行线程的优先级的,因为HttpUtils是采用线程池的方式执行请求的,如果请求参数中有设置线程优先级的话,则执行11行,如果请求参数没有设置优先级的话,则设置优先级为默认值,也就是默认情况下是按顺序执行请求的,其中Priority是枚举类型的,最关键的就是第16行的代码了,executeOnExecutor这个方法我们通常会在AsyncTask源码中遇到,因为当你通过execute方法执行AsyncTask任务的时候,实际上调用的方法是executeOnExecutor,那么我们就可以猜测HttpHandler是一个和AsyncTask有关的类了,来看看源码是什么样子的啦:

public class HttpHandler<T> extends PriorityAsyncTask<Object, Object, Void> implements RequestCallBackHandler

很明显了吧,他实现了PriorityAsyncTask抽象类,证明了我们的猜测,但是这还不够,我们会发现HttpHandler中并没有第16行的executeOnExecutor这个方法,显然需要到他的父类PriorityAsyncTask中去查看该方法,这个类中存在两个executeOnExecutor方法

  public final PriorityAsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
                                                                               Params... params) {
        return executeOnExecutor(exec, Priority.DEFAULT, params);
    }
  public final PriorityAsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
                                                                               Priority priority,
                                                                               Params... params) {
	}

可以看到第一个的最终执行方法还是第二个,只不过将线程的优先级设置为默认值罢了,另外这里有一点可以看出Priority其实是和Params没什么关系啦,要是有的话就不可能出现两个executeOnExecutor方法啦,我们只需要看第二个方法的实现就可以了:

public final PriorityAsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
                                                                               Priority priority,
                                                                               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;
    }

该方法首先判断当前执行的任务是否已经正在执行,如果是的话,执行第5行抛出异常表示当前任务正在执行,如果当前任务没有正在执行的话,执行第6行设置当前任务正在执行,接着执行onPreExecute方法,该方法是PriorityAsyncTask抽象类中的抽象方法,因此需要在继承PriorityAsyncTask的类中实现,具体可以在这个方法中写一些上传下载前的初始化工作,比如提示用户等等,其实从这开始就已经属于AsyncTask的源码范畴了,如果你对AsyncTask源码不是太了解的话,建议看看我的另一篇博客:android-----AsyncTask源码分析,第13行将请求参数设置到WorkerRunnable对象中,接着执行14行的execute方法,将PriorityRunnable类型的任务添加到线程池中,而该任务的执行方法也就是PriorityRunnable对象的run方法了,查看PriorityRunnable的run方法:

 @Override
    public void run() {
        this.obj.run();
    }

而这里的obj指的就是PriorityRunnable构造函数的第二个参数,从源码中也能找到答案:

 public PriorityRunnable(Priority priority, Runnable obj) {
        super(priority, obj);
    

那么上面的run方法执行的就是mFuture的run方法了,我们先来看看mFuture的定义:

mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    LogUtils.w(e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };

可见他是以WorkerRunnable对象为参数的FutureTask对象,里面的done方法我们一会介绍,执行他的run方法其实就是执行FutureTask的run方法啦,源码如下:

public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

第7行的callable对象就是前面的WorkerRunnable对象mWorker了,这点看过AsyncTask源码的应该都知道吧,主要的一句代码是第12行的c.call()方法,这个方法将会执行mWorker的call方法,我们来看看mWorker的定义:

 public PriorityAsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };

那么在第8行的call方法里面你看到了熟悉的doInBackground方法,这个方法在PriorityAsyncTask里面同样也是抽象方法,那么他的实现就该在他的子类HttpHandler中了:

 @Override
    protected Void doInBackground(Object... params) {
        if (this.state == State.CANCELLED || params == null || params.length == 0) return null;

        if (params.length > 3) {
            fileSavePath = String.valueOf(params[1]);
            isDownloadingFile = fileSavePath != null;
            autoResume = (Boolean) params[2];
            autoRename = (Boolean) params[3];
        }

        try {
            if (this.state == State.CANCELLED) return null;
            // init request & requestUrl
            request = (HttpRequestBase) params[0];
            requestUrl = request.getURI().toString();
            if (callback != null) {
                callback.setRequestUrl(requestUrl);
            }

            this.publishProgress(UPDATE_START);

            lastUpdateTime = SystemClock.uptimeMillis();

            ResponseInfo<T> responseInfo = sendRequest(request);
            if (responseInfo != null) {
                this.publishProgress(UPDATE_SUCCESS, responseInfo);
                return null;
            }
        } catch (HttpException e) {
            this.publishProgress(UPDATE_FAILURE, e, e.getMessage());
        }

        return null;
    }

首先如果当前线程被暂停或者请求参数为null或者没有设置请求参数的话,直接return,第5行判断如果请求参数的个数大于三的话(这种情况是我们在调用download方法进行文件下载的时候会进入到if语句块中的,源码在分析完send方法之后分析),第15行获取到request请求,并且在16行通过request请求获取到请求url,如果我们在创建HttpHandler的时候传入了RequestCallBack对象的话,则在18行将请求url设置到RequestCallBack对象上面,第21行调用publishProgress方法传入UPDATE_START参数,这个方法会调用onProgressUpdate方法,那么他到底为什么调用publishProgress的时候会调用onProgressUpdate方法呢?查看第21行看到他调用的是this的publishProgress方法,可以发现HttpHandler中并没有publishProgress方法方法,那么就需要到他的父类PriorityAsyncTask查看是否存在了,很庆幸我们找到了定义为protected的方法了,也就是说这个方法是允许子类访问的,来看看他的定义:

protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

发现他实际上是通过InternalHandler类型的handler发送了一条标志为MESSAGE_POST_PROGRESS的消息,InternalHandler是PriorityAsyncTask的内部类,那么这条消息的具体实现就是在InternalHandler的handleMessage方法里面了:

    @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }

找到标志为MESSAGE_POST_PROGRESS的case子句,发现他会执行PriorityAsyncTask的onProgressUpdate方法:

 @SuppressWarnings({"UnusedDeclaration"})
    protected void onProgressUpdate(Progress... values) {
    }

从onProgressUpdate的定义可以看出,这个方法是需要子类实现的,也就是说实际上调用的是HttpHandler的onProgressUpdate方法啦;

在看onProgressUpdate方法之前呢,我们需要看看通过HttpHandler传入的RequestCallBack到底是什么样子的,查看源码可以发现他是一个抽象类,其中抽象方法如下:

    public abstract void onSuccess(ResponseInfo<T> responseInfo);

    public abstract void onFailure(HttpException error, String msg);

除此之外还有三个方法:

    public void onStart() {

    public void onCancelled() {
    }

    public void onLoading(long total, long current, boolean isUploading) {
    }

我们可以在继承RequestCallBack抽象类的时候进行重写这三个方法,回到doInBackground方法里面,在第21行调用publishProgress方法传入UPDATE_START参数后转而执行的方法是onProgressUpdate,我们看看他的源码:

protected void onProgressUpdate(Object... values) {
        if (this.state == State.CANCELLED || values == null || values.length == 0 || callback == null) return;
        switch ((Integer) values[0]) {
            case UPDATE_START:
                this.state = State.STARTED;
                callback.onStart();
                break;
            case UPDATE_LOADING:
                if (values.length != 3) return;
                this.state = State.LOADING;
                callback.onLoading(
                        Long.valueOf(String.valueOf(values[1])),
                        Long.valueOf(String.valueOf(values[2])),
                        isUploading);
                break;
            case UPDATE_FAILURE:
                if (values.length != 3) return;
                this.state = State.FAILURE;
                callback.onFailure((HttpException) values[1], (String) values[2]);
                break;
            case UPDATE_SUCCESS:
                if (values.length != 2) return;
                this.state = State.SUCCESS;
                callback.onSuccess((ResponseInfo<T>) values[1]);
                break;
            default:
                break;
        }
    }

可以发现他是一个受保护的方法,也就是子类对这个方法是不可以进行重写操作的,找到UPDATE_START的case子句,可以发现他会首先将标志位修改为start,随后执行RequestCallBack对象的onStart方法,这个方法里面我们可以实现在开始上传之前的一些初始化操作,比如提示用户将要上传文件之类的信息;

接着第25行调用sendRequest方法来执行request请求返回ResponseInfo对象,来看看sendRequest方法:

 private ResponseInfo<T> sendRequest(HttpRequestBase request) throws HttpException {

        HttpRequestRetryHandler retryHandler = client.getHttpRequestRetryHandler();
        while (true) {

            if (autoResume && isDownloadingFile) {
                File downloadFile = new File(fileSavePath);
                long fileLen = 0;
                if (downloadFile.isFile() && downloadFile.exists()) {
                    fileLen = downloadFile.length();
                }
                if (fileLen > 0) {
                    request.setHeader("RANGE", "bytes=" + fileLen + "-");
                }
            }

            boolean retry = true;
            IOException exception = null;
            try {
                requestMethod = request.getMethod();
                if (HttpUtils.sHttpCache.isEnabled(requestMethod)) {
                    String result = HttpUtils.sHttpCache.get(requestUrl);
                    if (result != null) {
                        return new ResponseInfo<T>(null, (T) result, true);
                    }
                }

                ResponseInfo<T> responseInfo = null;
                if (!isCancelled()) {
                    HttpResponse response = client.execute(request, context);
                    responseInfo = handleResponse(response);
                }
                return responseInfo;
            } catch (UnknownHostException e) {
                exception = e;
                retry = retryHandler.retryRequest(exception, ++retriedCount, context);
            } catch (IOException e) {
                exception = e;
                retry = retryHandler.retryRequest(exception, ++retriedCount, context);
            } catch (NullPointerException e) {
                exception = new IOException(e.getMessage());
                exception.initCause(e);
                retry = retryHandler.retryRequest(exception, ++retriedCount, context);
            } catch (HttpException e) {
                throw e;
            } catch (Throwable e) {
                exception = new IOException(e.getMessage());
                exception.initCause(e);
                retry = retryHandler.retryRequest(exception, ++retriedCount, context);
            }
            if (!retry) {
                throw new HttpException(exception);
            }
        }
    }

第3行首先获取retry的设置,里面可能包括一些如果连接发生错误需要重新开启连接的连接次数限制等等属性,可以看到这个方法里面是一个while死循环,也就是说只有里面的return语句以及连接次数超出retry中的设置值才会结束当前方法,第6到15行是download方法用来判断是否需要断点下载的部分,稍后我们再分析,第21行判断是否使用了缓存,如果使用了的话则从httpCache获取对应于我们请求的内容,并且当前内容获取成功的话通过24行的ResponseInfo构造方法创建ResponseInfo的实例对象返回;如果没有启用缓存或者从缓存中获取到的内容为空的话,则执行第29行判断当前请求是否被暂停的判断语句,如果没有的话,第30行执行http请求,从网络中获取到对应请求的内容,31行调用handleResponse方法封装一个ResponseInfo类型的对象出来,想必handleResponse里面肯定有存缓存的操作啦,我们可以验证一下:

 @SuppressWarnings("unchecked")
    private ResponseInfo<T> handleResponse(HttpResponse response) throws HttpException, IOException {
        if (response == null) {
            throw new HttpException("response is null");
        }
        if (isCancelled()) return null;

        StatusLine status = response.getStatusLine();
        int statusCode = status.getStatusCode();
        if (statusCode < 300) {
            Object result = null;
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                isUploading = false;
                if (isDownloadingFile) {
                    autoResume = autoResume && OtherUtils.isSupportRange(response);
                    String responseFileName = autoRename ? OtherUtils.getFileNameFromHttpResponse(response) : null;
                    result = mFileDownloadHandler.handleEntity(entity, this, fileSavePath, autoResume, responseFileName);
                } else {
                    result = mStringDownloadHandler.handleEntity(entity, this, charset);
                    if (HttpUtils.sHttpCache.isEnabled(requestMethod)) {
                        HttpUtils.sHttpCache.put(requestUrl, (String) result, expiry);
                    }
                }
            }
            return new ResponseInfo<T>(response, (T) result, false);
        } else if (statusCode == 301 || statusCode == 302) {
            if (httpRedirectHandler == null) {
                httpRedirectHandler = new DefaultHttpRedirectHandler();
            }
            HttpRequestBase request = httpRedirectHandler.getDirectRequest(response);
            if (request != null) {
                return this.sendRequest(request);
            }
        } else if (statusCode == 416) {
            throw new HttpException(statusCode, "maybe the file has downloaded completely");
        } else {
            throw new HttpException(statusCode, status.getReasonPhrase());
        }
        return null;
    }

第6行如果当前请求被暂停的话,直接返回null,第9行获取请求的返回状态码,如果状态码小于300的话表示请求成功类型,获取到返回结果的内容实体HttpEntity,第15行到18行同样也是属于download下载文件部分,稍后我们分析,我们需要重点看看第20行了,因为正是这行才能够允许我们在上传的过程中实时查看上传进度进而显示上传进度条来提示用户,mFileDownloadHandler是StringDownloadHandler类型的对象,我们看看StringDownloadHandler里面的handleEntity方法:

 public String handleEntity(HttpEntity entity, RequestCallBackHandler callBackHandler, String charset) throws IOException {
        if (entity == null) return null;

        long current = 0;
        long total = entity.getContentLength();

        if (callBackHandler != null && !callBackHandler.updateProgress(total, current, true)) {
            return null;
        }

        InputStream inputStream = null;
        StringBuilder sb = new StringBuilder();
        try {
            inputStream = entity.getContent();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, charset));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line).append('\n');
                current += OtherUtils.sizeOfString(line, charset);
                if (callBackHandler != null) {
                    if (!callBackHandler.updateProgress(total, current, false)) {
                        break;
                    }
                }
            }
            if (callBackHandler != null) {
                callBackHandler.updateProgress(total, current, true);
            }
        } finally {
            IOUtils.closeQuietly(inputStream);
        }
        return sb.toString().trim();
    }

其实思路也是很简单啦,首先14行获取到返回的输入流,接着17行通过循环来操作输入流,只不过在每次循环过程中记录下已经上传了的字节数,接着调用RequestCallBackHandler的updateProgress方法,注意这里的RequestCallBackHandler是一个接口,这个方法的第三个参数表示的是是否马上更新UI,因为下载结束的时候就是我们需要更新UI的时候,所以第27行的第3个参数是true;那么updateProgress方法具体会执行什么呢?这个需要我们分析下类结构了,因为HttpHandler是实现RequestCallBackHandler接口的,所以实际上是执行HttpHandler里面的updateProgress方法的,来看看这个方法里面做了些什么吧:

 @Override
    public boolean updateProgress(long total, long current, boolean forceUpdateUI) {
        if (callback != null && this.state != State.CANCELLED) {
            if (forceUpdateUI) {
                this.publishProgress(UPDATE_LOADING, total, current);
            } else {
                long currTime = SystemClock.uptimeMillis();
                if (currTime - lastUpdateTime >= callback.getRate()) {
                    lastUpdateTime = currTime;
                    this.publishProgress(UPDATE_LOADING, total, current);
                }
            }
        }
        return this.state != State.CANCELLED;
    }

从关键字override就可以看出他是实现接口中的方法的,这个方法会根据forceUpdateUI的值来决定执行if语句块还是else语句块,但是他们最后都会执行publishProgress,并且传入的标志参数都是UPDATE_LOADING,很显然接下来执行的就是onProgressUpdate方法了,查看case语句标志是UPDATE_LOADING的部分:

 case UPDATE_LOADING:
                if (values.length != 3) return;
                this.state = State.LOADING;
                callback.onLoading(
                        Long.valueOf(String.valueOf(values[1])),
                        Long.valueOf(String.valueOf(values[2])),
                        isUploading);
                break;

可以看出它会执行我们RequestCallBack的onLoading方法,这个方法呢也就是我们平时实现RequestCallBack接口时需要实现的方法了;

继续回到handleResponse方法中,在20行我们获取到result值之后,21行判断是否开启了缓存功能,如果开启的话,则将当前值添加到缓存中,并且在第26行返回一个封装好的ResponseInfo对象;

如果返回状态码是301或者302的话,表示需要进行重定向操作了;

返回状态码是416的话表示文件早已下载结束了;

这样handleResponse方法分析结束,接着我们的sendRequest方法分析,如果我们在请求的过程中出现异常的话,就会执行34---50行部分,可以看到这部分都有一个retryRequest方法,目的就是用来进行请求失败之后重新请求的,至于最多请求多少次呢,sendRequest方法的第3行的HttpRequestRetryHandler的对象中就有啦,这样我们的整个请求就结束啦,请求结束之后我们有时候就需要来通知用户上传或者下载成功了,那么这里的调用时机是在什么时候呢?不用说,当然是回到doInBackground方法中了,第25行通过sendRequest方法获取到请求结果之后,26行判断该请求结果是否为空,不为空的话调用publishProgress方法,传入的标志信息是UPDATE_SUCCESS,此时就会执行onProgressUpdate的case值是UPDATE_SUCCESS的语句块,进而执行RequestCallBack的onSuccess方法,在这个方法中我们就可以进行提示用户的操作了;如果说在doInBackground方法中13--29行部分出现异常的话,那么他会执行标志信息是UPDATE_FAILURE的onProgressUpdate
case语句块,进而调用RequestCallBack的onFailure方法,这个方法是用户自己实现的,在此方法中可以提示用户下载失败的信息;

好了,在上面我们并没有分析mFuture的done方法,现在是时候分析一下了,为了方便查看,我们再次贴出mFuture的定义:

 mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    LogUtils.w(e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };

done方法的执行时机是当前线程正常执行结束或者执行过程中发生异常现象,done方法的第一句是postResultIfNotInvoked方法,该方法的定义是:

   private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

也就是说如果当前线程没有被触发的话,他会调用postResult方法:

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

很明显,在postResult方法里面,我们通过InternalHandler类型的handler发送了一条标志为MESSAGE_POST_RESULT的消息,InternalHandler是PriorityAsyncTask的内部私有类,具体的处理该消息的操作应该是出现在InternalHandler的handleMessage里面:

 @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }

查看MESSAGE_POST_RESULT的case子句,它会执行PriorityAsyncTask的finish方法,这个方法的定义如下:

  private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
    }

可以看出如果当前线程被暂停的话执行onCancelled方法,这个方法在PriorityAsyncTask中并没有实现,也就是说实际上调用的是继承自PriorityAsyncTask抽象类的HttpHandler里的onCancelled方法,如果当前线程没有被暂停的话则执行onPostExecute方法,同样这个方法PriorityAsyncTask也没实现,实际上执行的是HttpHandler里的;

至此通过Httputils实现上传操作的源码分析完毕了,当然这个只是android客户端在上传操作过程中的一些操作,我们还需要相应的服务器端代码来配合实现真正的上传操作,上面分析的过程中我们都避开了download文件下载部分的代码,其实下载部分的代码分析流程和上传过程是非常类似的,只不过在下载的过程中我们需要处理一些断点下载、重命名下载的问题,下面简单分析一下:

众所周知,使用HttpUtils实现下载操作只需要调用HttpUtils对象的download方法就可以了,这个方法有多个重载类型实现,但是最终他们都会执行到下面这个方法:

  public HttpHandler<File> download(HttpRequest.HttpMethod method, String url, String target,
                                      RequestParams params, boolean autoResume, boolean autoRename, RequestCallBack<File> callback) {

        if (url == null) throw new IllegalArgumentException("url may not be null");
        if (target == null) throw new IllegalArgumentException("target may not be null");

        HttpRequest request = new HttpRequest(method, url);

        HttpHandler<File> handler = new HttpHandler<File>(httpClient, httpContext, responseTextCharset, callback);

        handler.setExpiry(currentRequestExpiry);
        handler.setHttpRedirectHandler(httpRedirectHandler);
        request.setRequestParams(params, handler);

        handler.executeOnExecutor(EXECUTOR, request, target, autoResume, autoRename);
        return handler;
    }

可以发现和send方法的最大差别在第15行部分,这里在调用executeOnExecutor方法时传入的参数要多于send方法,这里面的target指的是下载文件的存储路径,autoResume指的是是否允许在下载暂停恢复之后从断点处下载,autoRename指的是当下载完成之后是否需要根据返回的头部信息来重命名下载的文件,这两个值默认情况下都是false,接着真正的执行者就是PriorityAsyncTask里面的

executeOnExecutor方法了,通过mFuture的run方法调用mWorker的call方法,而call方法中存在doInBackground方法,在PriorityAsyncTask中他又是没有实现的方法,因而它会执行HttpHandler的doInBackground方法,重点就来了,正是这个方法里面有一些处理断点下载以及重命名下载方面的内容,doInBackground中会执行sendRequest方法,sendRequest方法的前几行有如下代码:

if (autoResume && isDownloadingFile) {
                File downloadFile = new File(fileSavePath);
                long fileLen = 0;
                if (downloadFile.isFile() && downloadFile.exists()) {
                    fileLen = downloadFile.length();
                }
                if (fileLen > 0) {
                    request.setHeader("RANGE", "bytes=" + fileLen + "-");
                }
            }

可以看到首先会判断是否允许断点下载以及是否正在下载文件isDownloadingFile的默认值是false,if条件成立的话,则执行if语句块,如果当前要下载的文件已经存在的话,则获取已经下载的文件大小,并且设置request请求头的RANGE字段为当前文件的大小,也就是说当前request请求并不需要从头开始下载文件了,这点达到了断点下载的目的,如果你对断点下载不是很了解的话,可以查看我的另一篇博客:多线程断点文件下载实现,至于重命名下载文件的实现呢,具体实现是在HttpHandler的handleResponse方法里面的,这个方法里面有下面一句代码:

 result = mFileDownloadHandler.handleEntity(entity, this, fileSavePath, autoResume, responseFileName);

那么真正的实现就该在FileDownloadHandler里的handleEntity方法里面啦,这个方法主要就是一些http进行请求下载文件的操作,想了解的可以自行看看啦;

至此,这几天的学习总结完毕,希望能够帮助到大家,有什么错误的地方望指正,赠人玫瑰,手留余香!!!!!

XUtils源码下载链接!!!!!

时间: 2024-10-12 16:40:31

android-----XUtils框架之HttpUtils源码分析的相关文章

Android之rild进程启动源码分析

Android 电话系统框架介绍 在android系统中rild运行在AP上,AP上的应用通过rild发送AT指令给BP,BP接收到信息后又通过rild传送给AP.AP与BP之间有两种通信方式: 1.Solicited Response:Ap向Bp发送请求,Bp给Ap发送回复,该类型的AT指令及其回调函数以数组的形式存放在Ril_commands.h文件中: {数组中的索引号,请求回调函数,响应回调函数} [plain] view plaincopy {0, NULL, NULL},      

android缓存系列:ASimpleCache源码分析

接触Acache是因为阅读oschina的开源android端代码,发现oschina采用了该框架缓存新闻分页数据.后来知道这是个杨福海的开源项目,他还开源过afinal框架,项目的地址如下: https://github.com/yangfuhai/ASimpleCache 一.官方介绍 ASimpleCache 是一个为android制定的 轻量级的 开源缓存框架.轻量到只有一个java文件(由十几个类精简而来). 1.它可以缓存什么东西? 普通的字符串.JsonObject.JsonArr

缓存框架OSCache部分源码分析

在并发量比较大的场景,如果采用直接访问数据库的方式,将会对数据库带来巨大的压力,严重的情况下可能会导致数据库不可用状态,并且时间的消耗也是不能容忍的.在这种情况下,一般采用缓存的方式.将经常访问的热点数据提前加载到内存中,这样能够大大降低数据库的压力. OSCache是一个开源的缓存框架,虽然现在已经停止维护了,但是对于OSCache的实现还是值得学习和借鉴的.下面通过OSCache的部分源码分析OSCache的设计思想. 缓存数据结构 通常缓存都是通过<K,V>这种数据结构存储,但缓存都是应

Android万能适配器base-adapter-helper的源码分析

项目地址:https://github.com/JoanZapata/base-adapter-helper 1. 功能介绍 1.1. base-adapter-helper base-adapter-helper 是对传统的 BaseAdapter ViewHolder 模式的一个封装.主要功能就是简化我们书写 AbsListView 的 Adapter 的代码,如 ListView,GridView. 1.2 基本使用 mListView.setAdapter(mAdapter = new

【Android】Handler、Looper源码分析

一.前言 源码分析使用的版本是 4.4.2_r1. Handler和Looper的入门知识以及讲解可以参考我的另外一篇博客:Android Handler机制 简单而言:Handler和Looper是对某一个线程实现消息机制的重要组成部分,另外两个重要元素是Message和MessageQueue,通过这四个类,可以让某个线程具备接收.处理消息的能力. 二.源码剖析 虽然只有四个类,而且这里只是剖析其中两个,但是也不能独立分析,必须组合进行解析.切入点是类Looper的注释中的一段示例代码: 1

Android JobService的使用及源码分析

Google在Android 5.0中引入JobScheduler来执行一些需要满足特定条件但不紧急的后台任务,APP利用JobScheduler来执行这些特殊的后台任务时来减少电量的消耗.本文首先介绍JobSerice的使用方法,然后分析JobService的源码实现. JobService的使用 使用JobScheduler的时候需要把待执行的后台任务封装到JobService中提交.下面就来介绍JobService的使用,首先看一下JobService是什么东东. 从上面的截图,可以看出J

Android 资源加载Resources源码分析(8.0)

我们熟悉的资源加载代码: 1.Activity.getResources(); 2.Context.getResources(); 这2种方式获取的都是Resources对象 先看第一种获取Resources对象源码分析: 说明:(AppcompatActivity中getResource()方法与Activity.getResources()是有区别的.AppcompatActivity是new Resources(...)对象) 一:Activity.getResources()源码分析:

android-----XUtils框架之BitmapUtils源码分析

上一篇使用XUtils的BitmapUtils实现了一个照片墙的功能,参见:android-----XUtils框架之BitmapUtils加载照片实现,这一篇我们从源码的角度分析下BitmapUtils到底是怎么一个执行流程的: 先来回顾下之前我们使用BitmapUtils的步骤: 很简单,就只有两步: (1)通过BitmapUtils的构造函数创建对象: (2)调用BitmapUtils对象的display方法: 好了,我们先从创建BitmapUtils对象开始分析,很自然想到了Bitmap

首选项框架PreferenceFragment部分源码分析

因为要改一些settings里面的bug以及之前在里面有做过勿扰模式,准备对勿扰模式做一个总结,那先分析一下settings的源码,里面的核心应该就是android3.0 上面的首选项框架PreferenceFragment.因为在3.0之前都是把这些东西放在PreferenceActivity的,但是3.0之后google建议把setting放在PreferenceFragment,但是PreferenceActivity也同时在用的,下面就此总结一下: PreferenceActivity的