源码解读
关于AsyncTask源码的解读我就不班门弄斧了,可参考郭大神的文章:http://blog.youkuaiyun.com/guolin_blog/article/details/11711405
知识点补充1:Callable、Future、FutureTask
Callable
callable和running类似,是一个接口:
public interface Runnable {
public abstract void run();
}
Callable是从Java 1.5开始提供的,位于java.util.concurrent包下:
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
可以看到,两者最大的区别就是Callable任务执行完毕之后可以得到任务执行结果。
Future
Future是一个接口:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Future提供了三种功能:
1)判断任务是否完成;
2)能够中断任务;
3)能够获取任务执行结果。
因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。
FutureTask
FutureTask类实现了RunnableFuture接口,RunnableFuture接口的实现如下:
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
可以看出RunnableFuture继承了Runnable接口和Future接口,所以FutureTask既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
FutureTask提供了2个构造器:
public FutureTask(Callable<V> callable) {
}
public FutureTask(Runnable runnable, V result) {
}
FutureTask实际上是一个任务的操作类,它并不启动新线程,只是在自己所在线程上操作,任务的具体实现是构造FutureTask时提供的,实现自Callable接口的对象,FutureTask不知道具体的任务是什么,它只知道如何调度任务,如:
如何启动任务:在FutureTask的run()方法中(实现自Runnable.run()),调用Callable.call()方法来启动任务,Callable.call()方法中是任务的具体实现;
如何取消任务:在cancel()里,中断执行任务的线程,记录任务结束状态,并调用done()方法来执行用户实现的操作;
如何返回任务的运行结果:如果任务还在执行,则阻塞线程(使用LockSupport.park()),直到任务结束才返回结果,用户可以通过get()方法来获取结果,同样当任务运行结束时,会调用done()来执行用户实现的操作。
使用FutureTask的好处是,更轻松的对任务进行管理,而不是像Runnable那样扔进线程后就啥也不能做了。
FutureTask模拟一个会计算账的过程,参考:http://blog.youkuaiyun.com/wuseyukui/article/details/49589159
知识点补充2:Executor
Executor是任务执行者,它不关心是什么任务,只关心如何执行任务。Executor是个Interface,具体如何执行任务要看怎么实现这个接口。
java.util.concurrent.Executor中被注释的代码中提供了一个Executor实现的例子:
class SerialExecutor implements Executor {
final Queue tasks = new ArrayDeque();
final Executor executor;
Runnable active;
SerialExecutor(Executor executor) {
this.executor = executor;
}
@Override
public synchronized void execute(final Runnable r) {
tasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (active == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((active = tasks.poll()) != null) {
executor.execute(active);
}
}
}
这个实现的意思是,严格按照用户提交的顺序来串行的执行任务(当前任务运行结束,再运行下一个)。Android的AsyncTask就使用了这个例子。当然用户也可以提供自己的Executor来改变AsyncTask的运行方式。
注意事项
AsyncTask提供了一种简便的异步处理机制,但是它又同时引入了一些令人厌恶的麻烦。一旦对AsyncTask使用不当,很可能对程序的性能带来负面影响,同时还可能导致内存泄露。
性能与内存泄漏
性能
假设按照顺序启动20个AsyncTask,一旦其中的某个AsyncTask执行时间过长,队列中的其他剩余AsyncTask都处于阻塞状态,必须等到该任务执行完毕之后才能够有机会执行下一个任务。这样就会因为无法把任务及时传递给工作线程而导致任务在主线程中被延迟。
为了解决上面提到的线性队列等待的问题,我们可以使用AsyncTask.executeOnExecutor()强制指定AsyncTask使用线程池并发调度任务。
内存泄漏
使用AsyncTask很容易导致内存泄漏,一旦把AsyncTask写成Activity的内部类的形式(非静态内部类会默认持有外部类的引用)就很容易因为AsyncTask生命周期的不确定而导致Activity发生泄漏。
如何取消一个AsyncTask的执行
我们是无法去彻彻底底地采用暴力的方法直接kill一个线程,所以我们不能直接去取消一个异步任务,但是我们可以通过调用cancel方法来发送一个取消异步任务的请求信号。
AsyncTaks有提供cancel()的方法,源代码如下:
// AsyncTask.java:
...
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
...
public final boolean isCancelled() {
return mCancelled.get();
}
...
这个方法实际上做了什么事情呢?
mCanceled的值被设置为true,表示该异步任务取消。mCanceled的值将会通过isCanceled方法回调传出去。
为了能够让一个线程更早的被销毁,程序中我们应该通过判断该值去终止线程中的操作,而不是去终止线程。
我们需要在回调方法中不断的添加程序是否被中止的判断逻辑,如下:
...
public class MainActivity extends Activity {
private MyAsyncTask mTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
mTask=new MyAsyncTask(tv, bar);
mTask.execute();
}
@Override
protected void onPause() {
if (mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING) {
/**
*cancel(true) 取消当前的异步任务,传入的true,表示当中断异步任务时继续已经运行的线程的操作
*但是为了线程的安全一般为让它继续设为true
* */
mTask.cancel(true);// 发出一个请求取消异步任务的信号
}
}
...
}
public class MyAsyncTask extends AsyncTask<Void, Integer, String> {
private TextView tv;
private ProgressBar bar;
public MyAsyncTask(TextView tv, ProgressBar bar) {
this.bar = bar;
this.tv = tv;
}
@Override
protected String doInBackground(Void... params) {
for (int i = 1; i < 101; i++) {
try {
if (isCancelled()) {// 如果为true那么说明已经有请求取消任务的信号,终止线程中一系列操作
break;
}
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
publishProgress(i);
}
return "下载完成";
}
@Override
protected void onProgressUpdate(Integer... values) {
if (isCancelled()) {// 如果为true那么说明已经有请求取消任务的信号,不再更新UI
return;
}
bar.setProgress(values[0]);
tv.setText("下载进度:" + values[0] + "%");
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(String result) {
if (isCancelled()) {// 如果为true那么说明已经有请求取消任务的信号,不再更新UI
return;
}
tv.setText(result);
super.onPostExecute(result);
}
@Override
protected void onCancelled() {
mVideoListAdapter.clear();
}
}
一旦任务被成功中止,AsyncTask就不会继续调用onPostExecute(),而是通过调用onCancelled()的回调方法反馈任务执行取消的结果。我们可以根据任务回调到哪个方法(是onPostExecute还是onCancelled)来决定是对UI进行正常的更新还是把对应的任务所占用的内存进行销毁等。
适用场景
源码片段:
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
...
}
从源码中可以看到,AsyncTask中handler使用的是Looper.getMainLooper(),也就是共用主线程的Looper和消息队列,如果异步任务过多、生命周期较长就会与主线程的界面绘制,事件传递等操作争抢系统资源,这就有可能影响到主界面的表现性能。由此可见AsyncTask提供一种简单便捷的机制。适用于当下立即需要启动,但是异步执行的生命周期短暂的使用场景。