###二. AsyncTask使用方法
首先先来看个例子,这是之前写过的一个gif图片下载的异步任务。使用AsyncTask要注意它的***三个参数***和***五个方法***。
- 三个参数
第一个,代表执行AsyncTask传入的参数;
第二个,代表执行后台任务的进度;
第三个,代表执行完任务返回的结果。 - 五个方法
(1)onPreExecute:在ui线程调用,后台操作执行前调用;经常用于显示一个进度条。
(2)doInBackground:做的是耗时操作,并返回执行结果。同时,可以在doInBackground中调用publishProgress来发布进度单位。
(3)onPostExecute:接受执行结果。
(4)onProgressUpdate:当publishProgress调用完该方法被执行,一般用于控制进度条的进度。
public class GifImageDownLoader extends AsyncTask<String, Void, File> {
private static final String foldPath = Environment.getExternalStorageDirectory() + "/CTRIP/cache/";
private ICallBack<File, String> mCallBack;
public GifImageDownLoader(ICallBack<File, String> callBack) {
this.mCallBack = callBack;
}
@Override
protected File doInBackground(String... params) {
String[] url = params[0].split("/");
String gifName = url[url.length - 1];
File f = new File(foldPath + gifName);
if (!f.exists()) {
f = downLoadGifImage(params[0], gifName);
}
return f;
}
@Override
protected void onPostExecute(File f) {
if (isCancelled() || f == null) {
mCallBack.fail(null);
return;
}
mCallBack.success(f, null);
}
private File downLoadGifImage(String gifUrl, String gifName) {
File gifImageFile = null;
InputStream is = null;
FileOutputStream fos = null;
try {
URL downLoadUrl = new URL(gifUrl);
HttpURLConnection conn = (HttpURLConnection) downLoadUrl.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
if (conn.getResponseCode() == 200) {
File fold = new File(Environment.getExternalStorageDirectory() + "/CTRIP/cache");
if (!fold.exists()) {
fold.mkdirs();
}
is = conn.getInputStream();
gifImageFile = new File(foldPath, gifName);
fos = new FileOutputStream(gifImageFile);
int len = 0;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.flush();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (fos != null) {
fos.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return gifImageFile;
}
}
###三. AsyncTask内部原理
Android主线程肩负绘制UI界面和即使响应用户操作的重任,所以为了避免用户点击按钮没有反应,应该把耗时操作放在子线程进行。
- AsyncTask本质是一个静态的线程池,其子类实现的异步任务都提交到静态线程池中进行。
- 线程池的工作线程执行doInBackground(mParams)方法执行异步任务。
- 当任务状态改变后,子线程向UI线程发送消息,AsyncTask内部的handler相应这些消息,并调用相关回调函数。
###四. 线程池ThreadPoolExecutor
创建线程池一般不直接new一个线程池对象,因为需要配置一堆东西,我们来看下其构造方法:
所以官方也不推荐我们这样创建线程池,而是推荐使用Executors的工厂方法来创建线程池,Executors是官方提供的一个工厂类,它里面封装了众多功能的线程池,从而使得我们创建线程池非常简便,主要提供了五种线程池:
#####1. newFixedThreadPool()
创建一个固定线程数量的线程池,该线程池中的线程数量都不变,即不会创建新的线程,也不会销毁已经创建好的线程,自始至终都是那几个固定的线程工作。
如果有空闲线程则处理任务,如果没有则将任务放到一个队列中,一旦线程空闲了则按FIFO的方式处理队列中的任务。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
#####2. newCachedThreadPool()
该线程池中的线程数量不固定,可根据实际情况调整。
如果有新任务提交,当没有空闲线程则创建新线程,如果有空闲线程则复用该空闲线程,当空闲线程空闲时间超过配置的等待时间则被销毁。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
#####3. newSingleThreadPool()
只有一个线程的线程池。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
#####4. newScheduledThreadPool()
可定时执行某任务的线程池。
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
#####5. newSingleThreadScheduledPool()
只有一个线程的可定时执行某任务的线程池。
可以看到工厂方法最终都是通过ThreadPoolExecutor来创建线程池。在它的构造方法的参数中,最重要的是BlockingQueue任务队列,不同线程池的任务队列实现方法肯定不同。
而线程池的使用也很简单,当创建好线程池后,只需要调用其execute方法即可。
####AsyncTask源码解析
AsyncTask的源码其实非常短,只有700多行。我们先来看AsyncTask的构造方法。
#####AsyncTask构造方法
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
- workRunnable是一个Callable,大家可能对Callable不熟悉,它其实跟Runnable使用非常像,只不过Callable有返回值而Runnable没有。
- FutureTask是Callable的包装类。
####execute方法
execute方法其实执行的是executeOnExecutor方法。
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
可以看到executeOnExecutor传入的第一个参数是sDefaultExecutor,这是一个线程池。我们稍后来分析这个线程池,我们先来看下executeOnExecutor里都做了什么。
- 首先是判断AsyncTask当前的执行状态,只有当前状态是Status.PENDING才可继续使用;
- 接着设置当前状态为RUNNING状态。
- 然后调用onPreExecute方法。
- 接着为mWorker设置参数,并执行该任务。
#####AsyncTask中线程池SerialExecutor
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
可以看到SerialExecutor就是将异步任务一个一个放入队列里,然后再去执行。不过还需要注意的是execute方法加上了synchronized修饰,从而保证AsyncTask中的任务是串行执行的。
###五. 内部handler
当任务执行完通过internalHandler讲执行结果同步给主线程。
private static class InternalHandler extends Handler {
public 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;
}
}
}
###六. 最后
以上就是我对AsyncTask源码的分析,后续当我有新的理解时还会继续更新。至于为什么想到要去看AsyncTask的源码了呢?主要是因为最近想要改进一下我们的缓存机制,希望能在合适的时机清理一下缓存,我们用AsyncTask比较多,也知道它是封装了线程池和handler,但具体怎么实现的还真没有系统地读过。
好了,时间不早了,现在是夜里十二点十五,大家晚安。如有任何疑问可以邮件联系我zhshan@ctrip.com。