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