在学习AsyncTask之前先看看有助于更好的理解:
AsyncTask是什么?
以下是官方对AsyncTask描述:
AsyncTask enables proper and easy use of the UI thread. This class allows to* perform background operations and publish results on the UI thread without* having to manipulate threads and/or handlers.
AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler} and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the <code>java.util.concurrent</code> package such as {@link Executor}, {@link ThreadPoolExecutor} and {@link FutureTask}.
大概意思是:
AsyncTask能够让使用UI线程更加的简单,它不需要操作线程和Handler就可以执行后台操作并且在UI线程发布结果。
AsyncTask被设计用于使用Thread和Handler一个帮助类,这个帮助类使我们不需要建立一个通用线程框架,AsyncTasks理想情况下只适用于短时间操作(至多几秒),如果你需要使线程长时间运行,高度推荐使用由concurrent包提供的API,比如说Executor,ThreadPoolExecutor,FutureTask。
一句话理解并概括AsyncTask: AsyncTask使得异步任务以及之后的更新UI实现起来更加简单,代码更简洁。
AsyncTask的定义、3个泛型参数和4个核心方法
定义
public abstract class AsyncTask<Params, Progress, Result> {
...
}
AsyncTask是一个抽象的泛型类,它有3个泛型参数,分别为Params、Progress和Result。
泛型参数
在继承时我们可以为AsyncTask类指定三个泛型参数,这三个参数的作用:
- Params
在调用AsyncTask.execute(Params... params)时需要传入的参数,以及在doInBackground(...params)接收的参数。 - Progress
后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度类型。 - Result
当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
如果不需要某个参数,可以将其设置为Void类型。
核心方法
- onPreExecute()
在主线程中执行,会在doInBackground开始执行之间调用,用于进行一些界面上的初始化操作,比如显示进度条对话框等。 - doInBackground(Params...)
在线程池中执行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回。在执行过程中可以调用publishProgress(Progress...values)来更新进度信息。 - onProgressUpdate(Progress...)
在主线程中执行,当在doInBackground中调用了publishProgress(Progress...)方法后,这个方法就很快会被调用,方法中携带的参数就是在doInBackground中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。 - onPostExecute(Result)
在主线程中执行。当doInBackground执行完毕并通过return语句进行返回结果后,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。
这里我产生了一些疑问:
- onPreExecute、doInBackground、onPostExecute这些方法是在那被调用的;
AsyncTask的基本用法
现在写一个下载一张图片并显示到ImageView的Demo。先创建一个类MyAsyncTask继承AsyncTask,因为AsyncTask是抽象类,其中doInBackground方法必须重写。
public class MyAsyncTask extends AsyncTask<String,Void,Bitmap> {
private static final String Tag = "MyAsyncTask";
private ImageView mImageView;
public MyAsyncTask(ImageView imageView){
this.mImageView = imageView;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.e(Tag,"onPreExecute ----------> " + Thread.currentThread().getName());
}
@Override
protected Bitmap doInBackground(String... params) {
Log.e(Tag,"doInBackground ----------> " + Thread.currentThread().getName());
try {
URL url = new URL(params[0]);
URLConnection urlConnection = url.openConnection();
InputStream inputStream = urlConnection.getInputStream();
return BitmapFactory.decodeStream(inputStream);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
Log.e(Tag,"onPostExecute ----------> " + Thread.currentThread().getName());
mImageView.setImageBitmap(bitmap);
}
}
这里我们把AsyncTask的第一个泛型参数指定为String,表示在执行AsyncTask的时候需要传入参数到doInBackground的方法参数。第二个泛型参数指定为Void,表示不需要使用进度显示。第三个泛型参数指定为Bitmap,则表示doInBackground返回值和onPostExecute方法参数。
分析AsyncTask的源码
Android2.3.7(API10)和Android8.0(API26)的ThreadPoolExecutor参数对比
Android2.3.7(API10)的AsyncTask
public abstract class AsyncTask<Params, Progress, Result> {
private static final String LOG_TAG = "AsyncTask";
private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 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>(10);
private static final ThreadPoolExecutor sExecutor
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
...
}
Android8.0(API26)的AsyncTask
public abstract class AsyncTask<Params, Progress, Result> {
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
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;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
}
把两者对比来看可以得出:
- Android2.3.7(API10)的AsyncTask其核心线程数是5个,线程池允许创建的最大线程数为128。
- Android8.0(API26)的AsyncTask其核心线程和线程池允许创建的最大线程数都是CPU的核数计算出来的。
两者对比的优缺点:
- Android2.3.7(API10)最多能同时容纳138个任务(线程池允许创建的最大线程数为128+阻塞队列容量10),当提交第139个任务时就会执行饱和策略,默认抛出RejectedExecutionException异常。
Android8.0(API26)的AsyncTask
AsyncTask构造方法:
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
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);
}
}
};
}
AsyncTask方法中使用到了concurrent 包中的Callable、Future、FutureTask等类,想要了解它们请到Java线程:Runnable、Callable、Future、FutureTask
AsyncTask中实例化了两个对象,分别是WorkerRunnable和FutureTask。
WorkerRunnable:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result>
{
Params[] mParams;
}
- WorkerRunnable实现了Callable接口,在call方法中调用了doInBackground(mParams)来处理任务并得到结果,并最终调用postResult将结果投递出去。
- WorkRunnable保存了params参数,params就是执行excute方法传入的可变参数(数组),它的具体泛型类型由实例化AsyncTask时的第一个参数确定。
FutureTask:
- 在初始化FutureTask把WorkerRunnable作为参数传入。
了解FutureTask请到Java线程:Runnable、Callable、Future、FutureTask
AsyncTask构造方法只是实例化两个对象,并未执行其方法,这两个方法什么时候执行,请往下看。
AsyncTask的execute方法
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
execute方法接收一个可变参数,最终会调用executeOnExecutor。
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代码可以知道,先调用了onPreExecute()方法,接下来会调用 exec.execute方法,并将mFuture(FureTask)传进去。这里的exec是传进来的参数sDefaultExecutor。
接下来找下sDefaultExecutor在哪被定义的。
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
/**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
显然sDefaultExecutor就是SerialExecutor对象,它是一个串的线程池。
接下来看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();//B
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
SerialExecutor是AsyncTask私有静态内部类,并实现了Executor接口。
当调用SerialExecutor.execute方法时,传入的FutureTask会被Runnable对象包装,加入ArrayDeque双端队列中。当任务执行完或者当前没有活动的任务时都会执行scheduleNext方法。
scheduleNext主要干了以下事情:
- 从ArrayDeque双端队列取出Runnable对象(FutureTask任务)
- 并把FutureTask任务交给THREAD_POOL_EXECUTOR处理。
在注释B处可以看到执行了FutureTask的run方法,代码如下
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.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);
}
}
把关注点发在 c.call()上。c是什么?实际上是初始化mFuture对象时传入的mWorker对象了。
c.call()代码如下:
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);
}
};
这代码其实就是AsyncTask构造方法初始化WorkerRunnable部分。
我可以看到doInBackground在这里被调用,并且doInBackground返回值,传给了postResult方法并将结果投递出去。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
熟悉消息处理机制的小伙伴们应该明白这方法主要干了什么?没明白请到Android消息处理机制(Handler)
获取一个Message,Message的what是MESSAGE_POST_RESULT,并将result封装成AsyncTaskResult对象,并把message发送到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;
}
}
}
刚才发送的MESSAGE_POST_RESULT类型的Message,会在handleMessage中被处理。
执行result.mTask.finish(result.mData[0]);实际是调用AsyncTask.finish方法,如果isCancelled,回调onCancelled,否则回调onPostExecute。
到这里,我们已经把AsyncTask整个执行任务的过程走完了,主要的回调方法的作用、原理、以及何时会被调用都已经说到了。
AsyncTask主要流程图.pngAsyncTask的线程池
- AsyncTask封装了线程池和Handler。AsyncTask有两个线程池:
- SerialExecutor:主要用来处理排队,将任务串行处理。在SerialExecutor中调用scheduleNext方法时,将任务交给THREAD_POOL_EXECUTOR。
- ThreadPoolExecutor。用于真正的处理任务。
- AsyncTask对应的线程池SerialExecutor和ThreadPoolExecutor都是进程范围内共享的,都是static的,所以是AsyncTask控制着进程范围内所有的子类实例。
为AsyncTask设置线程池
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);//并行处理任务
asyncTask.executeOnExecutor(Executors.newCachedThreadPool());
Executor executor = new ThreadPoolExecutor(0,Integer.MAX_VALUE,0L, TimeUnit.SECONDS,new LinkedBlockingDeque<Runnable>());
asyncTask.executeOnExecutor(executor);
注意点
为什么asynctask不能执行长时间任务?
- AsyncTask的生命周期没有跟Activity的生命周期同步,容易导致内存泄露。
有了Thread为什么要有AsyncTask?
使用Thread有以下缺点:
- 不能返回任务执行结果
- 中途取消任务执行
- 如果要实现上面2点需要额外维护大量代码
AsyncTask已经帮我们解决使用Thread时的缺陷并且建立一个通用的框架,我们只需要重写一些方法就可以了,也就是对Handler和Thread相关操作进行封装。
cancel方法实现不是很好.
如果你调用了AsyncTask的cancel(false),doInBackground()仍然会执行到方法结束,只是不会去调用onPostExecute()方法。但是实际上这是让应用程序执行了没有意义的操作。那么是不是我们调用cancel(true)前面的问题就能解决呢?并非如此。如果mayInterruptIfRunning设置为true,会使任务尽早结束,但是如果的doInBackground()有不可打断的方法会失效,比如这个BitmapFactory.decodeStream() IO操作。但是你可以提前关闭IO流并捕获这样操作抛出的异常。但是这样会使得cancel()方法没有任何意义。
网友评论