xUtils异步HTTP源码分析
来源:互联网 发布:淘宝2017官方版下载 编辑:程序博客网 时间:2024/05/17 22:00
xUtils是国内的一个牛人写得android开源框架,写得非常好。
HttpUtils模块是xUtils的一个网络模块,可以实现网络的异步请求,同步请求,文件下载,上传等操作,他的异步操作机制一直是我好奇的地方,所以翻开源码进行了分析,分析报告见下.
HttpUtils.java是这个网络模块的接口,所有的用户操作都是通过这个接口来交互,类原型如下:
public class HttpUtils { public final static HttpCache sHttpCache = new HttpCache(); private final DefaultHttpClient httpClient; private final HttpContext httpContext = new BasicHttpContext(); private HttpRedirectHandler httpRedirectHandler; public HttpUtils() { this(HttpUtils.DEFAULT_CONN_TIMEOUT, null); } public HttpUtils(int connTimeout) { this(connTimeout, null); } public HttpUtils(String userAgent) { this(HttpUtils.DEFAULT_CONN_TIMEOUT, userAgent); } 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); 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; } } } } }); } // ************************************ default settings & fields **************************** private String responseTextCharset = HTTP.UTF_8; private long currentRequestExpiry = HttpCache.getDefaultExpiryTime(); private final static int DEFAULT_CONN_TIMEOUT = 1000 * 15; // 15s private final static int DEFAULT_RETRY_TIMES = 3; private static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding"; private static final String ENCODING_GZIP = "gzip"; private final static int DEFAULT_POOL_SIZE = 3; private final static PriorityExecutor EXECUTOR = new PriorityExecutor(DEFAULT_POOL_SIZE); public HttpClient getHttpClient() { return this.httpClient; } // ***************************************** config ******************************************* public HttpUtils configResponseTextCharset(String charSet) { if (!TextUtils.isEmpty(charSet)) { this.responseTextCharset = charSet; } return this; } public HttpUtils configHttpRedirectHandler(HttpRedirectHandler httpRedirectHandler) { this.httpRedirectHandler = httpRedirectHandler; return this; } public HttpUtils configHttpCacheSize(int httpCacheSize) { sHttpCache.setCacheSize(httpCacheSize); return this; } public HttpUtils configDefaultHttpCacheExpiry(long defaultExpiry) { HttpCache.setDefaultExpiryTime(defaultExpiry); currentRequestExpiry = HttpCache.getDefaultExpiryTime(); return this; } public HttpUtils configCurrentHttpCacheExpiry(long currRequestExpiry) { this.currentRequestExpiry = currRequestExpiry; return this; } public HttpUtils configCookieStore(CookieStore cookieStore) { httpContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore); return this; } public HttpUtils configUserAgent(String userAgent) { HttpProtocolParams.setUserAgent(this.httpClient.getParams(), userAgent); return this; } public HttpUtils configTimeout(int timeout) { final HttpParams httpParams = this.httpClient.getParams(); ConnManagerParams.setTimeout(httpParams, timeout); HttpConnectionParams.setConnectionTimeout(httpParams, timeout); return this; } public HttpUtils configSoTimeout(int timeout) { final HttpParams httpParams = this.httpClient.getParams(); HttpConnectionParams.setSoTimeout(httpParams, timeout); return this; } public HttpUtils configRegisterScheme(Scheme scheme) { this.httpClient.getConnectionManager().getSchemeRegistry().register(scheme); return this; } public HttpUtils configSSLSocketFactory(SSLSocketFactory sslSocketFactory) { Scheme scheme = new Scheme("https", sslSocketFactory, 443); this.httpClient.getConnectionManager().getSchemeRegistry().register(scheme); return this; } public HttpUtils configRequestRetryCount(int count) { this.httpClient.setHttpRequestRetryHandler(new RetryHandler(count)); return this; } public HttpUtils configRequestThreadPoolSize(int threadPoolSize) { HttpUtils.EXECUTOR.setPoolSize(threadPoolSize); return this; } // ***************************************** send request ******************************************* 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); } public ResponseStream sendSync(HttpRequest.HttpMethod method, String url) throws HttpException { return sendSync(method, url, null); } public ResponseStream sendSync(HttpRequest.HttpMethod method, String url, RequestParams params) throws HttpException { if (url == null) throw new IllegalArgumentException("url may not be null"); HttpRequest request = new HttpRequest(method, url); return sendSyncRequest(request, params); } // ***************************************** download ******************************************* public HttpHandler<File> download(String url, String target, RequestCallBack<File> callback) { return download(HttpRequest.HttpMethod.GET, url, target, null, false, false, callback); } public HttpHandler<File> download(String url, String target, boolean autoResume, RequestCallBack<File> callback) { return download(HttpRequest.HttpMethod.GET, url, target, null, autoResume, false, callback); } public HttpHandler<File> download(String url, String target, boolean autoResume, boolean autoRename, RequestCallBack<File> callback) { return download(HttpRequest.HttpMethod.GET, url, target, null, autoResume, autoRename, callback); } public HttpHandler<File> download(String url, String target, RequestParams params, RequestCallBack<File> callback) { return download(HttpRequest.HttpMethod.GET, url, target, params, false, false, callback); } public HttpHandler<File> download(String url, String target, RequestParams params, boolean autoResume, RequestCallBack<File> callback) { return download(HttpRequest.HttpMethod.GET, url, target, params, autoResume, false, callback); } public HttpHandler<File> download(String url, String target, RequestParams params, boolean autoResume, boolean autoRename, RequestCallBack<File> callback) { return download(HttpRequest.HttpMethod.GET, url, target, params, autoResume, autoRename, callback); } public HttpHandler<File> download(HttpRequest.HttpMethod method, String url, String target, RequestParams params, RequestCallBack<File> callback) { return download(method, url, target, params, false, false, callback); } public HttpHandler<File> download(HttpRequest.HttpMethod method, String url, String target, RequestParams params, boolean autoResume, RequestCallBack<File> callback) { return download(method, url, target, params, autoResume, false, callback); } 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); if (params != null) { request.setRequestParams(params, handler); handler.setPriority(params.getPriority()); } handler.executeOnExecutor(EXECUTOR, request, target, autoResume, autoRename); return handler; } //////////////////////////////////////////////////////////////////////////////////////////////// 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; } private ResponseStream sendSyncRequest(HttpRequest request, RequestParams params) throws HttpException { SyncHttpHandler handler = new SyncHttpHandler(httpClient, httpContext, responseTextCharset); handler.setExpiry(currentRequestExpiry); handler.setHttpRedirectHandler(httpRedirectHandler); request.setRequestParams(params); return handler.sendRequest(request); }}
我们可以把这个类理解成一个对外接口包装类,真正的一些出来是通过HttpHandler来做的,我把HttpHandler的代码贴出来
public class HttpHandler<T> extends PriorityAsyncTask<Object, Object, Void> implements RequestCallBackHandler { private final AbstractHttpClient client; private final HttpContext context; private HttpRedirectHandler httpRedirectHandler; public void setHttpRedirectHandler(HttpRedirectHandler httpRedirectHandler) { if (httpRedirectHandler != null) { this.httpRedirectHandler = httpRedirectHandler; } } private String requestUrl; private String requestMethod; private HttpRequestBase request; private boolean isUploading = true; private RequestCallBack<T> callback; private int retriedCount = 0; private String fileSavePath = null; private boolean isDownloadingFile = false; private boolean autoResume = false; // Whether the downloading could continue from the point of interruption. private boolean autoRename = false; // Whether rename the file by response header info when the download completely. private String charset; // The default charset of response header info. 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 State state = State.WAITING; public State getState() { return state; } private long expiry = HttpCache.getDefaultExpiryTime(); public void setExpiry(long expiry) { this.expiry = expiry; } public void setRequestCallBack(RequestCallBack<T> callback) { this.callback = callback; } public RequestCallBack<T> getRequestCallBack() { return this.callback; } // 执行请求 @SuppressWarnings("unchecked") 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); } } } @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; } private final static int UPDATE_START = 1; private final static int UPDATE_LOADING = 2; private final static int UPDATE_FAILURE = 3; private final static int UPDATE_SUCCESS = 4; @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; } } @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) { throw new HttpException(statusCode, "maybe the file has downloaded completely"); } else { throw new HttpException(statusCode, status.getReasonPhrase()); } return null; } /** * cancel request task. */ @Override public 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(); } } private long lastUpdateTime; @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; } public enum State { WAITING(0), STARTED(1), LOADING(2), FAILURE(3), CANCELLED(4), SUCCESS(5); private int value = 0; State(int value) { this.value = value; } public static State valueOf(int value) { switch (value) { case 0: return WAITING; case 1: return STARTED; case 2: return LOADING; case 3: return FAILURE; case 4: return CANCELLED; case 5: return SUCCESS; default: return FAILURE; } } public int value() { return this.value; } } private static final NotUseApacheRedirectHandler notUseApacheRedirectHandler = new NotUseApacheRedirectHandler(); 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; } }}
public abstract class PriorityAsyncTask<Params, Progress, Result> implements TaskHandler { private static final int MESSAGE_POST_RESULT = 0x1; private static final int MESSAGE_POST_PROGRESS = 0x2; private static final InternalHandler sHandler = new InternalHandler(); public static final Executor sDefaultExecutor = new PriorityExecutor(); private final WorkerRunnable<Params, Result> mWorker; private final FutureTask<Result> mFuture; private volatile boolean mExecuteInvoked = false; private final AtomicBoolean mCancelled = new AtomicBoolean(); private final AtomicBoolean mTaskInvoked = new AtomicBoolean(); private Priority priority; public Priority getPriority() { return priority; } public void setPriority(Priority priority) { this.priority = priority; } /** * Creates a new asynchronous task. This constructor must be invoked on the UI thread. */ public PriorityAsyncTask() { 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()); } 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; } /** * Override this method to perform a computation on a background thread. The * specified parameters are the parameters passed to {@link #execute} * by the caller of this task. * <p/> * This method can call {@link #publishProgress} to publish updates * on the UI thread. * * @param params The parameters of the task. * @return A result, defined by the subclass of this task. * @see #onPreExecute() * @see #onPostExecute * @see #publishProgress */ protected abstract Result doInBackground(Params... params); /** * Runs on the UI thread before {@link #doInBackground}. * * @see #onPostExecute * @see #doInBackground */ protected void onPreExecute() { } /** * <p>Runs on the UI thread after {@link #doInBackground}. The * specified result is the value returned by {@link #doInBackground}.</p> * <p/> * <p>This method won't be invoked if the task was cancelled.</p> * * @param result The result of the operation computed by {@link #doInBackground}. * @see #onPreExecute * @see #doInBackground * @see #onCancelled(Object) */ @SuppressWarnings({"UnusedDeclaration"}) protected void onPostExecute(Result result) { } /** * Runs on the UI thread after {@link #publishProgress} is invoked. * The specified values are the values passed to {@link #publishProgress}. * * @param values The values indicating progress. * @see #publishProgress * @see #doInBackground */ @SuppressWarnings({"UnusedDeclaration"}) protected void onProgressUpdate(Progress... values) { } /** * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and * {@link #doInBackground(Object[])} has finished.</p> * <p/> * <p>The default implementation simply invokes {@link #onCancelled()} and * ignores the result. If you write your own implementation, do not call * <code>super.onCancelled(result)</code>.</p> * * @param result The result, if any, computed in * {@link #doInBackground(Object[])}, can be null * @see #cancel(boolean) * @see #isCancelled() */ @SuppressWarnings({"UnusedParameters"}) protected void onCancelled(Result result) { onCancelled(); } /** * <p>Applications should preferably override {@link #onCancelled(Object)}. * This method is invoked by the default implementation of * {@link #onCancelled(Object)}.</p> * <p/> * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and * {@link #doInBackground(Object[])} has finished.</p> * * @see #onCancelled(Object) * @see #cancel(boolean) * @see #isCancelled() */ protected void onCancelled() { } /** * Returns <tt>true</tt> if this task was cancelled before it completed * normally. If you are calling {@link #cancel(boolean)} on the task, * the value returned by this method should be checked periodically from * {@link #doInBackground(Object[])} to end the task as soon as possible. * * @return <tt>true</tt> if task was cancelled before it completed * @see #cancel(boolean) */ @Override public final boolean isCancelled() { return mCancelled.get(); } /** * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this * task should be interrupted; otherwise, in-progress tasks are allowed * to complete. * @return <tt>false</tt> if the task could not be cancelled, * typically because it has already completed normally; * <tt>true</tt> otherwise * @see #isCancelled() * @see #onCancelled(Object) */ public final boolean cancel(boolean mayInterruptIfRunning) { mCancelled.set(true); return mFuture.cancel(mayInterruptIfRunning); } @Override public boolean supportPause() { return false; } @Override public boolean supportResume() { return false; } @Override public boolean supportCancel() { return true; } @Override public void pause() { } @Override public void resume() { } @Override public void cancel() { this.cancel(true); } @Override public boolean isPaused() { return false; } /** * Waits if necessary for the computation to complete, and then * retrieves its result. * * @return The computed result. * @throws java.util.concurrent.CancellationException If the computation was cancelled. * @throws java.util.concurrent.ExecutionException If the computation threw an exception. * @throws InterruptedException If the current thread was interrupted * while waiting. */ public final Result get() throws InterruptedException, ExecutionException { return mFuture.get(); } /** * Waits if necessary for at most the given time for the computation * to complete, and then retrieves its result. * * @param timeout Time to wait before cancelling the operation. * @param unit The time unit for the timeout. * @return The computed result. * @throws java.util.concurrent.CancellationException If the computation was cancelled. * @throws java.util.concurrent.ExecutionException If the computation threw an exception. * @throws InterruptedException If the current thread was interrupted * while waiting. * @throws java.util.concurrent.TimeoutException If the wait timed out. */ public final Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return mFuture.get(timeout, unit); } /** * @param params The parameters of the task. * @return This instance of AsyncTask. * @throws IllegalStateException If execute has invoked. * @see #executeOnExecutor(java.util.concurrent.Executor, Object[]) * @see #execute(Runnable) */ public final PriorityAsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } /** * @param exec The executor to use. * @param params The parameters of the task. * @return This instance of AsyncTask. * @throws IllegalStateException If execute has invoked. * @see #execute(Object[]) */ 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; } /** * Convenience version of {@link #execute(Object...)} for use with * a simple Runnable object. See {@link #execute(Object[])} for more * information on the order of execution. * * @see #execute(Object[]) * @see #executeOnExecutor(java.util.concurrent.Executor, Object[]) */ public static void execute(Runnable runnable) { execute(runnable, Priority.DEFAULT); } /** * Convenience version of {@link #execute(Object...)} for use with * a simple Runnable object. See {@link #execute(Object[])} for more * information on the order of execution. * * @see #execute(Object[]) * @see #executeOnExecutor(java.util.concurrent.Executor, Object[]) */ public static void execute(Runnable runnable, Priority priority) { sDefaultExecutor.execute(new PriorityRunnable(priority, runnable)); } /** * This method can be invoked from {@link #doInBackground} to * publish updates on the UI thread while the background computation is * still running. Each call to this method will trigger the execution of * {@link #onProgressUpdate} on the UI thread. * <p/> * {@link #onProgressUpdate} will note be called if the task has been * canceled. * * @param values The progress values to update the UI with. * @see #onProgressUpdate * @see #doInBackground */ protected final void publishProgress(Progress... values) { if (!isCancelled()) { sHandler.obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } } private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } } 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 static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; } @SuppressWarnings({"RawUseOfParameterizedType"}) private static class AsyncTaskResult<Data> { final PriorityAsyncTask mTask; final Data[] mData; AsyncTaskResult(PriorityAsyncTask task, Data... data) { mTask = task; mData = data; } }}看两个点可以理清线索,首先整个后台操作是基于ThreadPoolExecutor线程池来做的,请求的入口方法是:
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;
}
然后exec.execute(new PriorityRunnable(priority, mFuture));传入的是一个PriorityRunnable对象,线程池在执行任务的时候调用的是PriorityRunnable对象的run方法,查看源码可以看出,PriorityRunnable的run方法其实是调用的mFuture的run方法如下:
public class PriorityRunnable extends PriorityObject<Runnable> implements Runnable {
public PriorityRunnable(Priority priority, Runnable obj) {
super(priority, obj);
}
@Override
public void run() {
this.obj.run();
}
}
那么mFuture这个是怎么跟其他勾起来的呢?其实在HttpHandler构造的时候,它的父类构造函数里面有如下代码:
public PriorityAsyncTask() {
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());
} 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);
}
}
};
}
这里看出mWorker和mFuture是在构造函数里面就new出来了,mWorker是这个任务的执行点,“postResult(doInBackground(mParams));”这句,但是在这里还看不出mFuture是怎么跟mWorker关系起来的,不用担心,我们去找FutureTask的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();//////////////////////////////这里调用的mWorker的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);
}
}
至此整个过程就非常清楚明了了,对应没有做过java开发的兄弟们,补一下基础知识:
在Java中,如果需要设定代码执行的最长时间,即超时,可以用Java线程池ExecutorService类配合Future接口来实现。 Future接口是Java标准API的一部分,在java.util.concurrent包中。Future接口是Java线程Future模式的实 现,可以来进行异步计算。
Future模式可以这样来描述:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时 间之后,我就便可以从Future那儿取出结果。就相当于下了一张订货单,一段时间后可以拿着提订单来提货,这期间可以干别的任何事情。其中Future 接口就是订货单,真正处理订单的是Executor类,它根据Future接口的要求来生产产品。
Future接口提供方法来检测任务是否被执行完,等待任务执行完获得结果,也可以设置任务执行的超时时间。这个设置超时的方法就是实现Java程 序执行超时的关键。
Future接口是一个泛型接口,严格的格式应该是Future<V>,其中V代表了Future执行的任务返回值的类型。 Future接口的方法介绍如下:
- boolean cancel (boolean mayInterruptIfRunning) 取消任务的执行。参数指定是否立即中断任务执行,或者等等任务结束
- boolean isCancelled () 任务是否已经取消,任务正常完成前将其取消,则返回 true
- boolean isDone () 任务是否已经完成。需要注意的是如果任务正常终止、异常或取消,都将返回true
- V get () throws InterruptedException, ExecutionException 等待任务执行结束,然后获得V类型的结果。InterruptedException 线程被中断异常, ExecutionException任务执行异常,如果任务被取消,还会抛出
CancellationException
- V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计 算超时,将抛出TimeoutException
- xUtils异步HTTP源码分析
- XUtils BitmapUtils 源码分析
- Xutils框架-HTTP源码详解
- android Xutils Http模块分析
- xUtils源码分析系列1
- XUtils源码分析之HttpUtils(一)
- android Xutils 数据库操作源码分析
- android-----XUtils框架之HttpUtils源码分析
- android-----XUtils框架之BitmapUtils源码分析
- Xutils 中BitmapUtils工具类的源码分析
- XUtils 源码分析(一)--网络操作模块
- XUtils 源码分析(二)--图片缓存模块
- XUtils 源码分析(三)--数据库操作模块
- Android 异步消息源码分析
- 异步消息机制源码分析
- 异步消息机制源码分析
- jquery异步机制源码分析
- xUtils的HTTP整理
- (一)MySQL_编译安装
- 使用@Controller注解为什么要配置<mvc:annotation-driven />
- pull解析器解析xml文件
- 怎么改变HDFS块大小
- Android TestCase问题
- xUtils异步HTTP源码分析
- yii框架自带demo中的blog与本地mysql数据库的交互实现(小白篇)
- hdoj 2054 A == B ?
- sqlserver2008连接问题,客户端能连接但telnet不通,程序也无法连接
- STM32开发板的Bootloader在哪里
- WPF实现射线效果动画
- [R]字符串函数
- 用户空间使用linux内核链表list编程
- ios8 sdk 推送兼容代码 registerForRemoteNotifications