AsyncTask类是我们在开发中经常需要用到的一个类,这个类有个最大的好处就是将耗时的操作和UI操作分开来处理,这样在这一个类中我们就可以很方便的处理耗时操作和UI操作了。
先上一个简单的例子来看看基本的用法。
public class MainActivity extends AppCompatActivity {
private ImageView mImageView;
private static final String URLSTRING = "https://img3.doubanio.com/view/photo/photo/public/p2360705951.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = (ImageView) this.findViewById(R.id.imageview);
new MyAsyncTask().execute();
}
class MyAsyncTask extends AsyncTask<Void, Integer, Bitmap> {
@Override
protected Bitmap doInBackground(Void... voids) {
URL url = null;
HttpURLConnection conn = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
url = new URL(URLSTRING);
conn = (HttpURLConnection) url.openConnection();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
InputStream ins = conn.getInputStream();
int length = 0;
byte[] data = new byte[1024];
while ((length = ins.read(data, 0, data.length)) != -1) {
bos.write(data, 0, length);
}
byte[] buffer = bos.toByteArray();
return BitmapFactory.decodeByteArray(buffer, 0, buffer.length);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
conn.disconnect();
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
mImageView.setImageBitmap(bitmap);
}
}
}
这个例子很简单,就是加载一个网络图片,并且显示到ImageView上去。
效果如下:
下面源码的分析也从如何操作耗时操作和如何操作UI操作两个方面来分析AsyncTask。
- 耗时操作
对于耗时操作,AsyncTask内部是使用ThreadPool线程池来操作的。直接上源码
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 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>(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);
CPU_COUNT、CORE_POOL_SIZE、MAXIMUM_POOL_SIZE、KEEP_ALIVE这些常量包含了cpu数量、线程池数量、最大线程池数量、同时或者的最多的线程数量,这些都是用于构造线程池执行器的参数。sThreadFactory是一个线程构造器,sPoolWorkQueue是一个容量为128的阻塞队列。这些参数最后构造了THREAD_POOL_EXECUTOR,通过这个线程池执行器,耗时的任务就可以通过阻塞队列依次的被执行。
- UI线程操作
UI线程的操作是通过构造了一个关联UI线程的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;
}
}
}
在InternalHandler类的构造方法中,传递一个Looper.getMainLooper()参数,就可以将这个Handler关联到UI线程上,所有Handler中的处理也就分发到了UI线程上。所以我们可以利用这个Handler来执行UI操作。
现在我们可以大致得出一个结论:耗时的操作是通过线程池THREAD_POOL_EXECUTOR执行的;UI的操作是通过InternalHandler类操作的。
对于AsyncTask的使用最简单的莫过于上面的new MyAsyncTask().execute(),这个调用涉及到构造方法和executor()两个方法,下面就通过这两个方法的执行来彻底梳理一下AsyncTask的流程。
- 构造方法
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(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) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
从代码中可以看到,在构造方法中,生成了一个WorkRunnable对象,WorkRunnable这个类实际上就是一个实现了Callable接口的抽象类,在call()方法中,我们调用了doInBackground()方法,并且将最终的结果通过postResult()分发出去;Callable接口并不是我们需要的,因为线程池需要Runnable的接口,所以又生成了一个FutureTask的对象用来将Callable接口转换成Runnable接口。所以构造方法的目的是生成一个mFuture的FutureTask对象。
- executor方法
executor方法最后调用的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;
}
在这个方法中,最终调用了exec.execute(mFuture),exec就是sDefaultExecutor,最终调用了线程池ThreadPoolExecutor中的executor方法执行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;
}
postResult方法调用了internalHandler中的消息处理。
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
result.mTask指的就是AsynTask这个对象,结果就是调用了finish()方法。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
最后执行了onPostExecute()这个方法,并且是通过InternalHandler在UI线程完成的。
最后总结一下:在构造方法中,将doInBackground()方法最终包装成一个FutureTask类,然后通过 execute()方法,将这个方法传递到线程池中执行;等执行完毕之后,通过InternalHandler将这个结果发送到AsyncTask的finish()方法中执行,在finish方法中执行onPostExecutor()方法。大致流程就是这样~~