XUtils 源码分析(一)--网络操作模块

来源:互联网 发布:软件复杂性 编辑:程序博客网 时间:2024/06/06 07:06

1.HttpUtils.java配置Http请求参数。

2.PriorityAsyncTask.java抽象类,执行线程池任务,将子类实现方法放在线程池中执行。

3.HttpHandler.java是PriorityAsyncTask.java抽象类的子类实现,在工作线程中处理具体具体请求,缓存,文件字符串等资源处理,

4.HttpCache.java处理资源缓存

5.LruMemoryCache.java资源缓存

6.FileDownloadHandler.java处理文件资源,生成文件,重命名,续传

7.StringDownloadHandler.java处理字符串资源,字符串组装,编码

过程:

1.新建HttpUtils对象,调用send方法传入URL、RequestParams、Callback。

2.新建HttpRequest对象,设置传入参数等。

3.新建HttpHandler对象,传入新建线程池执行网络请求。线程池执行doInBackground。

4.检查是否有缓存,没有去执行网络请求。

5.处理网络请求响应,根据字符串还有文件分别处理,返回ResponseInfo对象。

一 新建HttpUtils对象

1.添加请求参数,新建对象

   RequestParams params = new RequestParams();//添加请求参数    params.addBodyParameter("type", inforType);    params.addBodyParameter("userUUID", userUUID);    params.addBodyParameter("circle", circle);    params.addBodyParameter("page", page);    params.addBodyParameter("time", time);    HttpUtils http = new HttpUtils();

1.1构造方法

//无参构造方法public HttpUtils() {    this(HttpUtils.DEFAULT_CONN_TIMEOUT, null);}//传入参数 连接超时 public HttpUtils(int connTimeout, String userAgent) {    HttpParams params = new BasicHttpParams();    //1.设置超时参数,超时释放资源    ConnManagerParams.setTimeout(params, connTimeout);    HttpConnectionParams.setSoTimeout(params, connTimeout);//Sets the default socket timeout (SO_TIMEOUT) in milliseconds which is the timeout for waiting for data.    HttpConnectionParams.setConnectionTimeout(params, connTimeout);//Sets the timeout until a connection is etablished.    //2.User-Agent请求报头域允许客户端将它的操作系统、浏览器和其它属性告诉服务器。    if (TextUtils.isEmpty(userAgent)) {        userAgent = OtherUtils.getUserAgent(null);    }    HttpProtocolParams.setUserAgent(params, userAgent);    //3.设置连接池最大连接数,每台主机最多连接数    ConnManagerParams.setMaxConnectionsPerRoute(params, new ConnPerRouteBean(10));//Sets lookup interface for maximum number of connections allowed per route.    ConnManagerParams.setMaxTotalConnections(params, 10);//Sets the maximum number of connections allowed.    //4.关闭算法延迟    HttpConnectionParams.setTcpNoDelay(params, true);    HttpConnectionParams.setSocketBufferSize(params, 1024 * 8);    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);    //5.声明协议    SchemeRegistry schemeRegistry = new SchemeRegistry();    schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));    schemeRegistry.register(new Scheme("https", DefaultSSLSocketFactory.getSocketFactory(), 443));    //6.设置多线程管理    httpClient = new DefaultHttpClient(new ThreadSafeClientConnManager(params, schemeRegistry), params);    //7.请求失败处理,重试机制    httpClient.setHttpRequestRetryHandler(new RetryHandler(DEFAULT_RETRY_TIMES));    //8.请求发送前执行设置拦截器。 默认加上gizp压缩。 通过gizp压缩后的数据传输效率高很多。    httpClient.addRequestInterceptor( new HttpRequestInterceptor() {//Processes a request. On the client side, this step is performed before the request is sent to the server. On the server side, this step is performed on incoming messages before the message body is evaluated.        @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);            }        }    });    //9.对接受的数据处理    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;                    }                }            }        }    });}

1.1.1 重试机制类RetryHandler

public class RetryHandler implements HttpRequestRetryHandler {private static final int RETRY_SLEEP_INTERVAL = 500;private static HashSet<Class<?>> exceptionWhiteList = new HashSet<Class<?>>();private static HashSet<Class<?>> exceptionBlackList = new HashSet<Class<?>>();static {    exceptionWhiteList.add(NoHttpResponseException.class);    exceptionWhiteList.add(UnknownHostException.class);    exceptionWhiteList.add(SocketException.class);    exceptionBlackList.add(InterruptedIOException.class);    exceptionBlackList.add(SSLHandshakeException.class);}private final int maxRetries;public RetryHandler(int maxRetries) {    this.maxRetries = maxRetries;//最大重试次数}//覆写方法重试调用@Overridepublic boolean retryRequest(IOException exception, int retriedTimes, HttpContext context) {    boolean retry = true;    if (exception == null || context == null) {        return false;    }    //判断是否已发送请求    Object isReqSent = context.getAttribute(ExecutionContext.HTTP_REQ_SENT);    boolean sent = isReqSent == null ? false : (Boolean) isReqSent;    if (retriedTimes > maxRetries) {//大于最大重试次数        retry = false;    } else if (exceptionBlackList.contains(exception.getClass())) {//IO操作异常中断,SSL握手异常        retry = false;    } else if (exceptionWhiteList.contains(exception.getClass())) {//服务器丢掉了连接,未知主机,Socket异常        retry = true;    } else if (!sent) {//未发送        retry = true;    }    if (retry) {//重试        try {            Object currRequest = context.getAttribute(ExecutionContext.HTTP_REQUEST);            if (currRequest != null) {                if (currRequest instanceof HttpRequestBase) {//基础请求类                    HttpRequestBase requestBase = (HttpRequestBase) currRequest;                    retry = "GET".equals(requestBase.getMethod());//是否是GET方法                } else if (currRequest instanceof RequestWrapper) {//包装请求类                    RequestWrapper requestWrapper = (RequestWrapper) currRequest;                    retry = "GET".equals(requestWrapper.getMethod());                }            } else {                retry = false;                LogUtils.e("retry error, curr request is null");            }        } catch (Throwable e) {            retry = false;            LogUtils.e("retry error", e);        }    }    if (retry) {        SystemClock.sleep(RETRY_SLEEP_INTERVAL); // sleep a while and retry http request again.    }    return retry;}

二 发送网络请求

1 客户端请求函数

http.send(HttpRequest.HttpMethod.POST,            url,            params,            new RequestCallBack<String>() {                @Override                public void onStart() {                    LogUtils.e("onStart()   ");                }                @Override                public void onLoading(long total, long current, boolean isUploading) {                    LogUtils.e("请求文章onLoading()   ");                }                @Override                public void onSuccess(ResponseInfo<String> responseInfo) {                    LogUtils.e("请求文章onSuccess()   " + responseInfo.result);                    if (httpStateListener != null) {                        httpStateListener.refreshArticleState(responseInfo.result);                    }                }                @Override                public void onFailure(HttpException error, String msg) {                    LogUtils.e("请求文章onFailure()   " + msg);                }            });}

1.1 方法http.send;

public <T> HttpHandler<T> send(HttpRequest.HttpMethod method, String url, RequestParams params,                               RequestCallBack<T> callBack) {    //1网址为空处理    if (url == null) throw new IllegalArgumentException("url may not be null");    //2.创建请求对象    HttpRequest request = new HttpRequest(method, url);    return sendRequest(request, params, callBack);}
1.1.1 新建请求对象new HttpRequest(method, url);
public HttpRequest(HttpMethod method, String uri) {    super();    this.method = method;    setURI(uri);//存储uri}
1.1.1.1 存储URI setURI(uri);
public void setURI(String uri) {    this.uriBuilder = new URIBuilder(uri);}public URIBuilder(final String uri) {    try {        digestURI(new URI(uri));    } catch (URISyntaxException e) {        LogUtils.e(e.getMessage(), e);    }}private void digestURI(final URI uri) {    this.scheme = uri.getScheme();    this.encodedSchemeSpecificPart = uri.getRawSchemeSpecificPart();    this.encodedAuthority = uri.getRawAuthority();    this.host = uri.getHost();    this.port = uri.getPort();    this.encodedUserInfo = uri.getRawUserInfo();    this.userInfo = uri.getUserInfo();    this.encodedPath = uri.getRawPath();    this.path = uri.getPath();    this.encodedQuery = uri.getRawQuery();    this.queryParams = parseQuery(uri.getRawQuery());    this.encodedFragment = uri.getRawFragment();    this.fragment = uri.getFragment();}
1.1.2 发送请求 sendRequest(request, params, callBack);
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);//设置请求参数    if (params != null) {        handler.setPriority(params.getPriority());//设置优先级    }    handler.executeOnExecutor(EXECUTOR, request);    return handler;}
1.1.2.1 线程池EXECUTOR
private final static PriorityExecutor EXECUTOR = new PriorityExecutor(DEFAULT_POOL_SIZE);public PriorityExecutor(int poolSize) {    mThreadPoolExecutor = new ThreadPoolExecutor(            poolSize,            MAXIMUM_POOL_SIZE,            KEEP_ALIVE,            TimeUnit.SECONDS,            mPoolWorkQueue,            sThreadFactory);}private static final ThreadFactory sThreadFactory = new ThreadFactory() {    //AtomicInteger,一个提供原子操作的Integer的类。在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字。而AtomicInteger则通过一种线程安全的加减操作接口。    private final AtomicInteger mCount = new AtomicInteger(1);    @Override    public Thread newThread(Runnable r) {        return new Thread(r, "PriorityExecutor #" + mCount.getAndIncrement());    }};private final BlockingQueue<Runnable> mPoolWorkQueue = new PriorityObjectBlockingQueue<Runnable>();
1.1.2.2 构造Handler对象
public HttpHandler(AbstractHttpClient client, HttpContext context, String charset,                   RequestCallBack<T> callback) {    this.client = client;    this.context = context;    this.callback = callback;    this.charset = charset;    this.client.setRedirectHandler(notUseApacheRedirectHandler);//设置重定向结构自己处理}private static final NotUseApacheRedirectHandler notUseApacheRedirectHandler = new        NotUseApacheRedirectHandler();/** * forward是服务器内部重定向,程序收到请求后重新定向到另一个程序,客户机并不知道;redirect则是服务器收到请求后发送一个状态头给客 * 户,客户将再请求一次,这里多了两次网络通信的来往。当然forward也有缺点,就是forward的页面的路径如果是相对路径就会有些问题了。 */private static final class NotUseApacheRedirectHandler implements RedirectHandler {    @Override    public boolean isRedirectRequested(HttpResponse httpResponse, HttpContext httpContext) {        return false;    }    @Override    public URI getLocationURI(HttpResponse httpResponse, HttpContext httpContext) throws            ProtocolException {        return null;    }}/**执行父类构造方法 * Creates a new asynchronous task. This constructor must be invoked on the UI thread. */public PriorityAsyncTask() {    //实现抽象类Callable    mWorker = new WorkerRunnable<Params, Result>() {        public Result call() throws Exception {            mTaskInvoked.set(true);            android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);            //noinspection unchecked            return postResult(doInBackground(mParams));        }    };    mFuture = new FutureTask<Result>(mWorker) {        /**         * 子类可以重写此方法,以调用完成回调或执行簿记。注意,可以查询此方法的实现内的状态,从而确定是否已取消了此任务。*/        @Override        protected void done() {            try {                postResultIfNotInvoked(get());//get()如有必要,等待计算完成,然后获取其结果。            } catch (InterruptedException e) {                LogUtils.d(e.getMessage());            } catch (ExecutionException e) {                throw new RuntimeException("An error occured while executing doInBackground()" +                        "" + "", e.getCause());            } catch (CancellationException e) {                postResultIfNotInvoked(null);            }        }    };}private void postResultIfNotInvoked(Result result) {    final boolean wasTaskInvoked = mTaskInvoked.get();    if (!wasTaskInvoked) {        postResult(result);    }}private Result postResult(Result result) {    @SuppressWarnings("unchecked")    Message message = sHandler.obtainMessage            (MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result));    message.sendToTarget();    return result;}//构造Handler对象,发送消息private static final InternalHandler sHandler = new InternalHandler();private static class InternalHandler extends Handler {    private InternalHandler() {        super(Looper.getMainLooper());    }    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})    @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;        }    }} private void finish(Result result) {    if (isCancelled()) {        onCancelled(result);    } else {        onPostExecute(result);    }}
1.1.2.3 Handler处理请求handler.executeOnExecutor(EXECUTOR, request);
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;//添加HttpRequest    exec.execute(new PriorityRunnable(priority, mFuture));//生成对象代理完成FutureTask    return this;}public class PriorityRunnable extends PriorityObject<Runnable> implements Runnable {public PriorityRunnable(Priority priority, Runnable obj) {    super(priority, obj);}@Overridepublic void run() {    this.obj.run();//代理}}
1.1.2.3.1 执行在构造函数PriorityAsyncTask()生成的FutureTask中的WorkerRunnable的 return postResult(doInBackground(mParams));
/** * 后台线程池处理执行,此处是在Work线程处理任务 */@Overrideprotected 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]);//target文件存储路径位置        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;}
1.1.2.3.1.1 向UI线程发布状态this.publishProgress(UPDATE_START);
/** * PriorityAsyncTask.java * 向UI线程发布状态 */protected final void publishProgress(Progress... values) {    if (!isCancelled()) {        sHandler.obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this,                 values)).sendToTarget();    }}private static class AsyncTaskResult<Data> {    final PriorityAsyncTask mTask;    final Data[] mData;    AsyncTaskResult(PriorityAsyncTask task, Data... data) {        mTask = task;        mData = data;    }}
1.1.2.3.1.1.1 sHandler引用对象
/** * PriorityAsyncTask.java * 处理异步消息在UI线程 */private static class InternalHandler extends Handler {    private InternalHandler() {        super(Looper.getMainLooper());    }    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})    @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]);//调用PriorityAsyncTask引用结束任务                break;            case MESSAGE_POST_PROGRESS://任务进度                result.mTask.onProgressUpdate(result.mData);//调用PriorityAsyncTask引用发布进度                break;        }    }}/** * PriorityAsyncTask.java * 处理结束请求 * 调用方法需要子类实现 */private void finish(Result result) {    if (isCancelled()) {        onCancelled(result);    } else {        onPostExecute(result);    }}
1.1.2.3.1.1.1.1 在子类HttpHandler处理上述抽象函数result.mTask.onProgressUpdate(result.mData);
 /** * HttpHandler.java * 覆写父类方法 * 处理客户端回调接口方法,通知客户端状态信息 */@Override@SuppressWarnings("unchecked")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;    }}
1.1.2.3.1.1.1.2 在子类HttpHandler处理上述抽象函数onCancelled(result);onPostExecute(result)未覆写;
/** * HttpHandler.java * 覆写父类方法处理结束状态 * cancel request task. */@Overridepublic void cancel() {    this.state = State.CANCELLED;    if (request != null && !request.isAborted()) {        try {            request.abort();        } catch (Throwable e) {        }    }    if (!this.isCancelled()) {        try {            this.cancel(true);//调用父类方法去结束该异步任务        } catch (Throwable e) {        }    }    if (callback != null) {        callback.onCancelled();    }}/** * PriorityAsyncTask.java * 处理异步任务的结束 */public final boolean cancel(boolean mayInterruptIfRunning) {    mCancelled.set(true);    return mFuture.cancel(mayInterruptIfRunning);}
1.1.2.3.1.2 执行请求sendRequest(request);
/**执行网络请求执行步骤: * 1.获取重定向处理对象 * 2.死循环执行,首先判断如果是续传下载文件,根据新建的文件对象大小设置相应下载范围。 * 3.获取缓存文件,如果存在缓存直接返回响应对象 * 4.没有缓存去请求执行网络获取数据 * 5.处理获取的网络数据,并返回响应对象 * 6.遇到异常重新请求 * * */@SuppressWarnings("unchecked")private ResponseInfo<T> sendRequest(HttpRequestBase request) throws HttpException {    HttpRequestRetryHandler retryHandler = client.getHttpRequestRetryHandler();//请求重试回调:重定向    while (true) {        if (autoResume && isDownloadingFile) {//对应download(...)下载函数            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();//请求方式GET?POST?            if (HttpUtils.sHttpCache.isEnabled(requestMethod)) {//默认GET方法可用                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);        }    }}
1.1.2.3.1.2.1 执行handleResponse(response);根据响应获得信息
 /** * 处理网络响应执行步骤: *1.检查当前执行状态,如果是取消立即返回方法。 * 2.获取状态码判断网络请求状态 * 3.如果是请求成功,判断是否在下载文件或是字符串请求 * 4.对于文件下载,首先检查是否续传是否自动重命名,然后传入以上参数构造文件 * 5.对于字符串,首先获取数据,然后放入缓存,最后返回所得 * 6.对于重定向继续发送网络请求处理 * * */@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;//自动重命名                FileDownloadHandler downloadHandler = new FileDownloadHandler();                result = downloadHandler.handleEntity(entity, this, fileSavePath, autoResume,                        responseFileName);//根据响应获得文件            } else {//不是在下载文件                StringDownloadHandler downloadHandler = new StringDownloadHandler();                result = downloadHandler.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) {// Range 中指定的任何数据范围都与当前资源的可用范围不重合,        throw new HttpException(statusCode, "maybe the file has downloaded completely");    } else {        throw new HttpException(statusCode, status.getReasonPhrase());    }    return null;}/** * OthreUtils.java工具类中的两个方法 * OthreUtils.java * 该响应是否支持数据范围字段 */public static boolean isSupportRange(final HttpResponse response) {    if (response == null) return false;    Header header = response.getFirstHeader("Accept-Ranges");    if (header != null) {        return "bytes".equals(header.getValue());    }    header = response.getFirstHeader("Content-Range");    if (header != null) {        String value = header.getValue();        return value != null && value.startsWith("bytes");    }    return false;}/** * OthreUtils.java * 从响应中获取文件名 */public static String getFileNameFromHttpResponse(final HttpResponse response) {    if (response == null) return null;    String result = null;    Header header = response.getFirstHeader("Content-Disposition");    if (header != null) {        for (HeaderElement element : header.getElements()) {            NameValuePair fileNamePair = element.getParameterByName("filename");            if (fileNamePair != null) {                result = fileNamePair.getValue();//获取文件名                // try to get correct encoding str                result = CharsetUtils.toCharset(result, HTTP.UTF_8, result.length());//重新编码                break;            }        }    }    return result;}
1.1.2.3.1.2.1.1 执行downloadHandler.handleEntity(entity, this, fileSavePath, autoResume,responseFileName);根据响应获得文件
/** * FileDownloadHandler.java * 1.先检查时候是续传,如果是构造追加输出流否则直接构造输出流 * 2.构造响应输入流,同时构造文件输出流 * 3.更新状态,如果处于任务取消状态直接返回当前文件 * 4.循环逐个读取输入写出到文件,同时更新状态,如果取消状态立即返回文件 * 5.强制更新状态,关闭流资源 * 6.重命名字段不为空,新建文件去重命名获得文件 * 7.返回文件 * */public File handleEntity(HttpEntity entity,                         RequestCallBackHandler callBackHandler,                         String target,                         boolean isResume,                         String responseFileName) throws IOException {    if (entity == null || TextUtils.isEmpty(target)) {        return null;    }    File targetFile = new File(target);    if (!targetFile.exists()) {        File dir = targetFile.getParentFile();        if (dir.exists() || dir.mkdirs()) {            targetFile.createNewFile();//生成文件        }    }    long current = 0;    BufferedInputStream bis = null;    BufferedOutputStream bos = null;    try {        FileOutputStream fileOutputStream = null;        if (isResume) {//续传            current = targetFile.length();//记录已存在文件大小            fileOutputStream = new FileOutputStream(target, true);//文件流追加到文件后面        } else {            fileOutputStream = new FileOutputStream(target);        }        long total = entity.getContentLength() + current;//获取文件总长度        bis = new BufferedInputStream(entity.getContent());//获取输入流        bos = new BufferedOutputStream(fileOutputStream);        if (callBackHandler != null && !callBackHandler.updateProgress(total, current, true)) {//去强制更新进度,如果任务取消返回            return targetFile;//任务状态已取消,返回当前文件        }        byte[] tmp = new byte[4096];        int len;        while ((len = bis.read(tmp)) != -1) {            bos.write(tmp, 0, len);//写文件            current += len;//记录文件长度            if (callBackHandler != null) {                if (!callBackHandler.updateProgress(total, current, false)) {//去强制更新进度,如果任务取消返回                    return targetFile;//任务状态已取消,返回当前文件                }            }        }        bos.flush();        if (callBackHandler != null) {            callBackHandler.updateProgress(total, current, true);//去强制更新进度        }    } finally {//最后关闭流        IOUtils.closeQuietly(bis);        IOUtils.closeQuietly(bos);    }    if (targetFile.exists() && !TextUtils.isEmpty(responseFileName)) {//重命名文件        File newFile = new File(targetFile.getParent(), responseFileName);        while (newFile.exists()) {//避免文件名重复,新建直到文件不重复            newFile = new File(targetFile.getParent(), System.currentTimeMillis() + responseFileName);        }        return targetFile.renameTo(newFile) ? newFile : targetFile;//修该命名    } else {        return targetFile;    }}
1.1.2.3.1.2.1.2 downloadHandler.handleEntity(entity, this, charset);处理获得的字符串数据
  /*** StringDownloadHandler.java * 执行步骤: *1.获得数据总长度,更新状态 * 2.获取响应数据输入流,指定特定编码 * 3.循环组装获得的字符串,并更新状态 * 4.关闭流返回字符串 * */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();//返回字符串}
1.1.2.3.1.2.2 HttpUtils.sHttpCache缓存类
public final static HttpCache sHttpCache = new HttpCache();private final static int DEFAULT_CACHE_SIZE = 1024 * 100;// string lengthprivate final static long DEFAULT_EXPIRY_TIME = 1000 * 60; // 60 seconds /** * HttpCache(HttpCache.DEFAULT_CACHE_SIZE, HttpCache.DEFAULT_EXPIRY_TIME); */public HttpCache() {    this(HttpCache.DEFAULT_CACHE_SIZE, HttpCache.DEFAULT_EXPIRY_TIME);}public HttpCache(int strLength, long defaultExpiryTime) {    this.cacheSize = strLength;    HttpCache.defaultExpiryTime = defaultExpiryTime;    mMemoryCache = new LruMemoryCache<String, String>(this.cacheSize) {        @Override        protected int sizeOf(String key, String value) {//覆写方法返回每个缓存的大小            if (value == null) return 0;            return value.length();        }    };} /** * LruMemoryCache.java 构造方法 */public LruMemoryCache(int maxSize) {    if (maxSize <= 0) {        throw new IllegalArgumentException("maxSize <= 0");    }    this.maxSize = maxSize;    //第三个参数为 true , 意味着 按访问顺序构造最近最少访问的内部链表.    //这样在内部的链表中, 最近最少访问的排在最前面, 最多访问的排在后面. 迭代整个值是按链表从后往前的方式    this.map = new LinkedHashMap<K, V>(0, 0.75f, true);//第一个参数是你要设置的初始大小;而程序内部实际的初始大小是1;的确如你所言,后面如果需要增大长度,按照capacity*loadFactor取整后增长;    this.keyExpiryMap = new KeyExpiryMap<K, Long>(0, 0.75f);//ConcurrentHashMap子类}//存储为缓存public void put(String url, String result) {    put(url, result, defaultExpiryTime);}public void put(String url, String result, long expiry) {    if (url == null || result == null || expiry < 1) return;    mMemoryCache.put(url, result, System.currentTimeMillis() + expiry);}
1.1.2.3.1.2.2.1 mMemoryCache.put(url, result, System.currentTimeMillis() + expiry);缓存数据
/** * Caches {@code value} for {@code key}. The value is moved to the head of * the queue. * * @return the previous value mapped by {@code key}. * 执行步骤: * 1.同步计算添加缓存后目前缓存总大小 * 2.检查缓存是否超过最大值 */public final V put(K key, V value, long expiryTimestamp) {    if (key == null || value == null) {        throw new NullPointerException("key == null || value == null");    }    V previous;    synchronized (this) {        putCount++;        size += safeSizeOf(key, value);//叠加缓存总大小        previous = map.put(key, value);        keyExpiryMap.put(key, expiryTimestamp);//保存过期时间        if (previous != null) {//这个缓存前面已经存在            size -= safeSizeOf(key, previous);//删除这个计算重复的大小        }    }    if (previous != null) {        entryRemoved(false, key, previous, value);//通知已存在的指已被替换    }    trimToSize(maxSize);//检查缓存是否超出最大值    return previous;}/** * @param maxSize the maximum size of the cache before returning. May be -1 *                to evict even 0-sized elements. */private void trimToSize(int maxSize) {    while (true) {        K key;        V value;        synchronized (this) {            if (size <= maxSize || map.isEmpty()) {//当前缓存大小未超过最大值                break;            }            //超过最大缓存数,去删除最旧的数据            Map.Entry<K, V> toEvict = map.entrySet().iterator().next();            key = toEvict.getKey();            value = toEvict.getValue();            map.remove(key);            keyExpiryMap.remove(key);            size -= safeSizeOf(key, value);            evictionCount++;        }        entryRemoved(true, key, value, null);    }}

三 致敬原作者,谢谢作者辛苦付出,代码受益匪浅。

传送门

四 其他讲解:

架构讲解:传送门

五 源码注释

源码详细注释:传送门

0 0
原创粉丝点击