<code>AsyncTask</code>,异步任务,顾名思义。
简单说一下使用场景就是:
- 有耗时逻辑、数据操作
- 耗时在(最多)几秒以内
- 操作后需要更新UI层面的变化
<code>AsyncTask</code>是<code>Handler</code>和<code>Thread</code>的一种封装,但并不是一种通用的线程框架,官方推荐了引用<code>AsyncTask</code>的场景(在引言中所提及的),既然它并不通用,那么了解它到时何时应该用就变得尤为重要。
Talk it by use it
首先,要明确的是要实现一个<code>AsyncTask</code>需要做什么:
-
构造器:
private class MyTask extends AsyncTask<Void, Void, Void> { ... }
你会发现当我们要构造一个<code>AsyncTask</code>时,需要传入三个类型,三个类型分别代表的是<code>Params</code>, <code>Progress</code>, <code>Result</code>,下文再提用处。
-
可以<code>Override</code>的方法有:
onPreExecute() //执行前的准备doInBackground(Params... params) //耗时操作的逻辑处理 onProgressUpdate(Progress... progress) //进度的更新 onPostExecute(Result result) //结果的处理
现在意义就很明了了,<code>Params</code>指代的是耗时操作的参数,<code>Progress</code>用于更新进度,<code>Result</code>用于更新结果。显然<code>onProgressUpdate</code>和<code>onPostExecute</code>中是用于更新UI的变化的。
- 用法需要注意的地方
- You can specify the type of the parameters, the progress values, and the final value of the task, using generics
- The method <code>doInBackground()</code> executes automatically on a worker thread
- <code>onPreExecute()</code>, <code>onPostExecute()</code>, and <code>onProgressUpdate()</code> are all invoked on the UI thread
- The value returned by <code>doInBackground()</code> is sent to <code>onPostExecute()</code>
You can call <code>publishProgress()</code> at anytime in <code>doInBackground()</code> to execute <code>onProgressUpdate()</code> on the UI thread- You can cancel the task at any time, from any thread
<div align = "right">摘自官网文档 链接</div>
翻译
- 构造器的三个参数类型为泛型,可自定义
- <code>doInBackground()</code>自动在工作线程(即非UI线程) 中执行
- <code>onPreExecute()</code>,<code>onPostExecute()</code>,<code>onProgressUpdate()</code>全都在UI线程中执行
- <code>doInBackground()</code>的返回值作为<code>onPostExecute()</code>的参数,<code>publishProgress()</code>可以在<code>doInBackground()</code>中调用,AsyncTask内部会回调<code>onProgressUpdate()</code>,参数为<code>pulishProgress()</code>的参数
- 任何时候都可以从任意线程取消异步任务
怕翻译的不好所以贴了一份原文
用法也就是这样了,想了想我觉得也不用再做过多的赘述。
Now Talk about HOW
那么到底<code>AsyncTask</code>表现如何?
最明显的一点就是,它让耗时操作更新UI变得十分简单,开发者几乎不需要做太多的东西就可以完成这一目的,与此相比自己实现<code>Thread</code>还有<code>Hanlder</code>变得略为繁琐。
当然上述的是使用<code>AsyncTask</code>的优点,还是要说一说缺点的。
从起因开始说,一个<code>AsyncTask</code>,它的类的加载以及实例化需要在UI线程中实现,那么在这个实现过程中,任务的内部就会持有一个对上下文对象的引用(<code>Activity</code>),用于更新UI的操作,那么这个上下文对象的持有就需要开发者考虑引用是否会丢失或者改变。例如,屏幕旋转引起的Activity recreate、任务还未结束时退出当前Activity等等。这些情况会有可能导致程序直接crash,或者引起内存泄露,那么我们就必须兼顾到<code>AsyncTask</code>结束任务的处理。
官方文档中包含了这样的一段demo:
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");
}
}
我们可以看到在<code>doInBackground</code>中,每一次的循环最后都加入了一个<code>isCancelled()</code>的判断,为什么呢?虽然<code>AsyncTask</code>提供了<code>cancel(boolean)</code>方法,其中的参数可以控制任务是否应被中断,我手动写了个简单的循环在<code>doInBackground()</code>中
@Override
protected void onProgressUpdate(Void... values) {
Log.e("progress", progress + "");
}
@Override
protected Void doInBackground(Void... params) {
Log.e("Task", "in");
while (isRunning && progress < 10) {
progress++;
try {
Thread.sleep(500);
publishProgress();
if (isCancelled())
Log.e("Task", "cancel");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
然后我在执行到一半的时候就调用了<code>cancel(true)</code>,请注意是<code>true</code>!按道理讲,这样的调用过后,任务应当被中断,然后去回调<code>onCancel()</code>,然而真相是<code>doInBackground()</code>还是会继续执行,但是<code>publishProgress()</code>被赤裸裸的忽略掉了。也就是说,即便你在外部调用了<code>cancel()</code>方法,你也不能确保<code>doInBackground()</code>中到底会不会发生异常,因此官方的文档才会有上面的那段demo,在<code>doInBackground()</code>中判断任务是否已经被取消并做出相关操作,例如结束掉网络请求、终止文件的读取等等我就不再多举例。
然而最重点的应该是,在<code>onCancel()</code>中究竟应该如何操作——请注意,如果调用了<code>cancel()</code>方法,不论<code>doInBackground()</code>是否已经正常的执行完毕(<code>onCancel(Result)</code>的参数,并不能保证是一个不为空的值,并且Google推荐当开发者自己实现<code>onCancel(Result)</code>时,不要调用<code>super.onCancel(result)</code>),<code>onCancel()</code>是一定会被执行的,那么我们就需要在<code>onCancel()</code>当中考虑不再对不再需要驻留内存的对象保持引用,最常见的就是上下文对象了。
另外,我们还可以做的是,将对对象的引用更换为弱引用,也算是多加了一层保险了。