美文网首页
AsyncTask使用实例

AsyncTask使用实例

作者: 超级绿茶 | 来源:发表于2020-09-15 17:09 被阅读0次

    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

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

    运行流程:

    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是同步运行还是异步运行。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
    或关注微信公众号:

    相关文章

      网友评论

          本文标题:AsyncTask使用实例

          本文链接:https://www.haomeiwen.com/subject/bjvfyktx.html