AsyncTask使用实例

2020-09-15  本文已影响0人  超级绿茶

AsyncTask是Android SDK提供的异步任务处理类。最初的名称是UserTask,在Android 1.5以后被正式引入。官方建议用AsyncTask来代替Handler+Thread的模式,因为AsyncTask的本质是对Handler和线程池的封装,从而更方便的实现在子线程中处理数据,然后在主线程中刷新界面。同时在内部使用线程池也避免线程在反复创建和销毁时的性能损失。

AsyncTask类的定义如下:

public abstract class AsyncTask<Params, Progress, Result> {
...
}

从定义来看,AsyncTask是抽象类,这说明我们在使用前需要先定义一个继承了AsyncTask的子类,并在子类中重写相关的方法。

由于AsyncTask定义了三个泛型,用于异步任务在不同阶段接收不同的数据,所以我们在定义继承类时需要明确实现这些泛型的具体类型:

    class MyTask : AsyncTask<Void, Void, Void>() {
        override fun doInBackground(vararg params: Void?): Void {
            ...
        }
    }

上面的例子定义了一个最简单的AsyncTask的继承类,只重写了doInBackground方法。

现在我们来说一下AsyncTask的运行原理;由于AsyncTask的主要作用是处理异步任务,所以必然会涉及到线程方面的事,可以通过下面的图示说明其动作机制:


asynctask.png

上图中黄色部分的方法运行在主线程,蓝色部分运行在子线程。

运行流程:

  1. 先实例化AsyncTask的子类,并调用execute方法启动任务。
  2. 通过onPreExecute方法实现任务的前期工作,例如:弹个进度框。
  3. 在doInBackground方法执行耗时/后台任务,此方法运行在子线程上。
  4. 在doInBackground方法内可以通过publishProgress方法将处理的进度值发送到主线程。
  5. onProgressUpdate方法在接到进度值后在主线程刷新界面。
  6. 在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内部有两个线程池:
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在使用时注意事项:

下面通过实例来演示下:
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
或关注微信公众号:

上一篇下一篇

猜你喜欢

热点阅读