AsyncTask使用实例
AsyncTask是Android SDK提供的异步任务处理类。最初的名称是UserTask,在Android 1.5以后被正式引入。官方建议用AsyncTask来代替Handler+Thread的模式,因为AsyncTask的本质是对Handler和线程池的封装,从而更方便的实现在子线程中处理数据,然后在主线程中刷新界面。同时在内部使用线程池也避免线程在反复创建和销毁时的性能损失。
AsyncTask类的定义如下:
public abstract class AsyncTask<Params, Progress, Result> {
...
}
从定义来看,AsyncTask是抽象类,这说明我们在使用前需要先定义一个继承了AsyncTask的子类,并在子类中重写相关的方法。
由于AsyncTask定义了三个泛型,用于异步任务在不同阶段接收不同的数据,所以我们在定义继承类时需要明确实现这些泛型的具体类型:
- Params:任务启动时的参数类型,例如接口的Url或文件路径。
- Progress:任务执行中的进度值类型。
- Result:任务执行完毕后的返回值,例如接口返回的JSON字符串或File对象。
我们也可以不指定类型直接写在Void即可,例如:
class MyTask : AsyncTask<Void, Void, Void>() {
override fun doInBackground(vararg params: Void?): Void {
...
}
}
上面的例子定义了一个最简单的AsyncTask的继承类,只重写了doInBackground方法。
现在我们来说一下AsyncTask的运行原理;由于AsyncTask的主要作用是处理异步任务,所以必然会涉及到线程方面的事,可以通过下面的图示说明其动作机制:
asynctask.png
上图中黄色部分的方法运行在主线程,蓝色部分运行在子线程。
运行流程:
- 先实例化AsyncTask的子类,并调用execute方法启动任务。
- 通过onPreExecute方法实现任务的前期工作,例如:弹个进度框。
- 在doInBackground方法执行耗时/后台任务,此方法运行在子线程上。
- 在doInBackground方法内可以通过publishProgress方法将处理的进度值发送到主线程。
- onProgressUpdate方法在接到进度值后在主线程刷新界面。
- 在doInBackground方法执行完毕或取消任务后onPostExecute方法会被响应,在此方法中可以将处理结果刷新到界面上。
上述流程中可以通过AsyncTask的实例的cancel方法可以取消正在运行中的任务,取消成功后时会响应onCancelled方法而不响应onPostExecute方法。
execute是异步任务的启动方法(或称为入口方法),此外还有executeOnExecutor方法也可以启动任务,这里看一个源码的定义:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) {
...
}
可以看出execute内部调用的就是executeOnExecutor方法。executeOnExecutor方法需要传入一个线程池和一个不定长的参数;
- 线程池决定了多个AsyncTask是同步运行还是异步运行。execute默认为同步运行。
- 不长定义参数是传给doInBackground方法调用。
AsyncTask内部有两个线程池:
1.THREAD_POOL_EXECUTOR,异步线程池,多个AsyncTask实例同时执行。
2.SERIAL_EXECUTOR,同步线程池,多个AsyncTask实例一个个的执行。
先看下源码中同步线程池的定义如下:
@UnsupportedAppUsage
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();
看到这里你应该就明白了吧,原本execute就是把sDefaultExecutor作为参数传给executeOnExecutor,而sDefaultExecutor就是同步线程池。即多个AsyncTask运行的时候是在线程池上排着队一个个的运行的。
那如果要改成并行运行,即多个AsyncTask能同时运行该怎么改?只需要按下如方法启动任务即可:
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
THREAD_POOL_EXECUTOR的定义如下:
/**
* 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,
new SynchronousQueue<Runnable>(), sThreadFactory);
threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
最后说一下AsyncTask在使用时注意事项:
- AsyncTask的实例是一次性的,execute方法在多次调用时会抛异常。
- 在Activity或Fragment中使用AsyncTask时要记得在退出时取消正在执行的任务。
- 不要手动调用AsyncTask运行流程中的方法。
- 当多个AsyncTask实例启动时默认为串行运行(一个个排队)。
下面通过实例来演示下:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="32dp"
android:orientation="vertical">
<ProgressBar
android:id="@+id/progress1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100"
android:progress="40"
style="?android:attr/progressBarStyleHorizontal" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp">
<Button
android:id="@+id/btnStart1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="启动" />
<TextView
android:id="@+id/tvTask1"
android:layout_width="wrap_content"
android:layout_gravity="end|center_vertical"
android:layout_height="wrap_content"
android:text="0%" />
</FrameLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="32dp"
android:orientation="vertical">
<ProgressBar
android:id="@+id/progress2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100"
android:progress="40"
style="?android:attr/progressBarStyleHorizontal" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp">
<Button
android:id="@+id/btnStart2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="启动" />
<TextView
android:id="@+id/tvTask2"
android:layout_width="wrap_content"
android:layout_gravity="end|center_vertical"
android:layout_height="wrap_content"
android:text="0%" />
</FrameLayout>
</LinearLayout>
</LinearLayout>
asynctask_xml.png
MainActivity.kt
private const val TAG = "test"
class MainActivity : AppCompatActivity() {
private var task1: MyAsyncTask? = null
private var task2: MyAsyncTask? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnStart1.setOnClickListener {
// 因为AsyncTask的执行是一次性的,所以每次点击按钮时都实例
if ((it as TextView).text == "启动") {
task1 = MyAsyncTask(progress1, btnStart1, tvTask1).apply {
execute()
// executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) // 并行运行
}
} else
task1?.cancel(true) // 取消正在运行中的任务
}
btnStart2.setOnClickListener {
if ((it as TextView).text == "启动")
task2 = MyAsyncTask(progress2, btnStart2, tvTask2).apply { execute() }
else
task2?.cancel(true)
}
}
override fun onDestroy() {
super.onDestroy()
// 退出时记得取消任务
task1?.let {
if (!it.isCancelled) it.cancel(true)
}
task2?.let {
if(!it.isCancelled) it.cancel(true)
}
}
class MyAsyncTask(val pbar: ProgressBar, val btn: Button, val tv: TextView) :
AsyncTask<Void, Int, String>() {
override fun onPreExecute() {
Log.i(TAG, "onPreExecute:${Thread.currentThread().name}")
super.onPreExecute()
pbar.progress = 0
pbar.max = 100
btn.text = "取消"
tv.text = "0"
}
override fun doInBackground(vararg params: Void?): String {
Log.i(TAG, "doInBackground:${Thread.currentThread().name}")
return try {
repeat(100) {
publishProgress(it + 1)
Thread.sleep(10)
}
"任务完成"
} catch (e: Exception) {
e.printStackTrace()
"任务出错"
}
}
override fun onProgressUpdate(vararg values: Int?) {
Log.i(TAG, "onProgressUpdate:${Thread.currentThread().name} - ${values.joinToString()}")
super.onProgressUpdate(*values)
tv.text = values[0].toString()
pbar.progress = values[0] ?: 0
}
override fun onPostExecute(result: String?) {
Log.i(TAG, "onPostExecute:${Thread.currentThread().name} - $result")
super.onPostExecute(result)
if (!isCancelled) {
tv.text = get()
btn.text = "启动"
}
}
override fun onCancelled(result: String?) {
Log.i(TAG, "onCancelled${Thread.currentThread().name} - $result")
super.onCancelled(result)
tv.text = if (result.isNullOrEmpty()) "任务取消" else result
btn.text = "启动"
}
}
}
下载源代码:AsynTaskDemo.rar: https://t00y.com/file/22686471-408875013
点击链接加入群聊【口袋里的安卓】:https://jq.qq.com/?_wv=1027&k=5z4fzdT
或关注微信公众号: