1)AsyncTask 用法
public abstract class AsyncTask<Params, Progress, Result> {
.....................
}
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long>; {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
由上述代码可知,AsyncTask是抽象类,需要类去继承并且子类需要指定3个泛型,它们分别对应doInBackground(),onProgressUpdate(),onPostExecute()的参数类型。它的核心方法:onPreExecute(),doInBackground(),onProgressUpdate(),onPostExecute(),publishProgress(),
这其中只有doInBackground()是在子线程执行,其余都在主线程中执行。
2)执行的顺序:
onPreExecute()–>doInBackground()–>publishProgress()onProgressUpdate()–>onPostExecute()
- onPreExecute()
这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。 - doInBackground(Params…)
这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress…)方法来完成。 - onProgressUpdate(Progress…)
当在后台任务中调用了publishProgress(Progress…)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。 - onPostExecute(Result)
当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。
3)AsyncTask 源码分析
1.从execute()开始:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
调用了executeOnExecutor(sDefaultExecutor, params)方法。
2.executeOnExecutor(sDefaultExecutor, params)
@MainThread
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是什么?
*mFuture是什么?runnable?
*/
exec.execute(mFuture);
return this;
}
由上可知,当前任务状态为RUNNING时,再次调用executeOnExecutor()会抛出IllegalStateException(“当前任务已经在执行”),当前任务状态为时FINISHED时,同样会抛出IllegalStateException异常,因为在AsyncTask中一个任务只能执行一次即使执行过程中被cancell掉 往下看调用了onPreExecute(),然后mWorker,mFuture是什么?先不管,再往后 exec.execute(mFuture),终于执行任务了。
3.exec是什么?
exec是我们传进来的sDefaultExecutor,那么sDefaultExecutor是什么?
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new 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);
}
}
}
到这可以得知sDefaultExecutor 其实是一个SerialExecutor 对象,调用它的execute(Runnable r)方法的时候,会调用ArrayDeque的offer()方法将传入的Runnable对象添加到队列的尾部,然后判断mActive对象是不是等于null,第一次运行当然是等于null了,于是会调用scheduleNext()方法。在这个方法中ArrayDeque的poll()会从队列的头部取值,并赋值给mActive对象,然后调用THREAD_POOL_EXECUTOR去执行取出的取出的Runnable对象。之后如何又有新的任务被执行,同样还会调用offer()方法将传入的Runnable添加到队列的尾部,但是再去给mActive对象做非空检查的时候就会发现mActive对象已经不再是null了,于是就不会再调用scheduleNext()方法。而是在finally语句块中调用了scheduleNext()方法。SerialExecutor 作用是让线程池严格的按照任务添加的顺序执行,一个结束,一个开始。
4.mFuture,mWorker是什么?
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(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);
}
}
};
}
mFuture 是一个FutureTask对象,实现了RunnableFuture接口,mWorker 是一个WorkerRunnable对象,实现了Callable接口。
FutureTask类:
public class FutureTask<V> implements RunnableFuture<V> {
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
}
现在来看FutureTask的run()方法
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
//c就是传入的mWorker
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//执行mWorker的call()方法
result = c.call();
//将ran设置为true
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
//ran为true执行set(result);
if (ran)
set(result);
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
由上可知在FutureTask的run()方法中调用了mWorker的call()方法,现在我们来看mWorker的run()
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
........
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
doInBackground()方法被调用,再由postResult(result)将结果返回
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
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:
// onProgressUpdate在此调用 result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
这段代码就是一个handler发送消息和接收消息的过程。
当msg.what==MESSAGE_POST_PROGRESS时,调用onProgressUpdate()方法,信息源是来自publishProgress()方法,正因如此,在doInBackground()方法中调用publishProgress()方法才可以从子线程切换到UI线程,从而完成对UI元素的更新操作。
当MESSAGE_POST_RESULT时, result.mTask就是AsyncTask,调用他的finish()方法,在这里调用了 onPostExecute(result)。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
到这mWorker 的run()执行完了,那么mFuture 的done()什么时候执行?回到FutureTask.run()方法,
//ran为true执行set(result);
if (ran) set(result);
mFuture 的 set(result);
protected void set(V v) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
outcome = v;
U.putOrderedInt(this, STATE, NORMAL); // final state
//注意这里
finishCompletion();
}
}
private void finishCompletion() {
..............此处省略
//执行done()
done();
..............此处省略
}
4)AsyncTask缺陷与解决
1.AsyncTask类包含一个全局静态的线程池THREAD_POOL_EXECUTOR,线程池的配置参数如下:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable>sPoolWorkQueue =new LinkedBlockingQueue<Runnable>(128);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR =
new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
当线程池中已有线程个数达到MAXIMUM_POOL_SIZE ,并且缓冲队列已满,如果此时在向线程提交任务,将会抛出RejectedExecutionException
解决方案:替换默认的RejectedExecutionHandler使用
ThreadPoolExecutor.DiscardOldestPolicy()代替。
在调用executeOnExecutor替换线程池。
注意:根据需求合理的选择和设置线程池。
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory, new ThreadPoolExecutor.DiscardOldestPolicy());
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
}
2.AsyncTask与他所在的activity 的生命周期不是一致的,activity 终止,AsyncTask并不会终止,它会以它自有的方式继续运行,即使你退出了整个应用程序,AsyncTask提前结束的唯一方法是通过调用AsyncTask.cancel()进行取消。由于不必要的后台线程会导致app阻塞的风险,或者内存泄露。当不再需要一个AsyncTask时,一定要取消它,防止在app执行期间引起任何问题。
而且即使调用了cancel() , 也未必能真正地取消任务。因为如果在doInBackgroud里有一个不可中断的操作,比如BitmapFactory.decodeStream(),那么这个操作会继续下去。
3.AsyncTask内存泄漏
如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露。
非静态内部类或非静态匿名类会隐式的持有外部类的引用,外部类就有可能发生泄漏。
4.屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。
5)AsyncTask使用注意事项
1.Task的实例必须在UI thread中创建。
/* Creates a new asynchronous task. This constructor must be *invoked on the UI thread. */
public AsyncTask() {
........省略
}
2.. execute方法必须在UI thread中调用 。( @MainThread修饰的方法必须在主线程调用)
@MainThread
public final AsyncTask<Params, Progress, Result>
execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}`
3.不要手动的去调用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个方法 .
4. 一个AsyncTask对象只能执行一次,否则会有异常抛出”java.lang.IllegalStateException: Cannot execute task: the task is already running”。
5. doInBackground()方法并不是在onPreExecute()方法做完全部操作才开始执行。