Android多線程:如何正確使用AsyncTask?

前言

?Android沿用了Java的線程模型,除了Thread外,Android還實現(xiàn)了AsyncTask、HandlerThread、IntentService,它們的底層實現(xiàn)也是線程。
?根據(jù)官網(wǎng)消息,Android R已正式棄用AsyncTask,那為什么我還繼續(xù)寫這篇文章?原因很簡單,雖然被棄用了,但是Android的源碼中仍然有用到AsyncTask的地方,從這點出發(fā)我們?nèi)匀恍枰肁syncTask的簡單使用。
?本文講的是AsyncTask

相關(guān)文章閱讀
HandlerThread
IntentService


1 使用步驟

?Async是一種輕量級的異步任務(wù)類,它可以在線程池中執(zhí)行后臺任務(wù),然后把把執(zhí)行結(jié)果反饋給主線程。
①AsyncTask<Params, Progress, Result>是一個抽象類,我們需要繼承它并實現(xiàn)關(guān)鍵方法。

/**
* public abstract class AsyncTask<Params, Progress, Result>
* Params:參數(shù)類型
* Progress:任務(wù)執(zhí)行進度類型
* Result:返回結(jié)果類型
*/
class MyTask : AsyncTask<String, Int, String>() {
    /**
     * 在主線程中執(zhí)行
     * 在異步任務(wù)執(zhí)行之前
     * 可用于做一些準備工作
     * */
    override fun onPreExecute() {
        super.onPreExecute()
    }

    /**
     * 在線程池中執(zhí)行,params標識異步輸入?yún)?shù)
     * */
    override fun doInBackground(vararg params: String?): String {
        //更新任務(wù)進度
        publishProgress()
    }

    /**
     * 在主線程中執(zhí)行
     * 當(dāng)后臺任務(wù)執(zhí)行進度改變時調(diào)用
     * */
    override fun onProgressUpdate(vararg values: Int?) {
        super.onProgressUpdate(*values)
    }

    /*
    * 在主線程中執(zhí)行
    * 在異步任務(wù)執(zhí)行之后
    * result為doInBackground的返回值
    * */
    override fun onPostExecute(result: String?) {
        super.onPostExecute(result)
    }
    /*
     *異步任務(wù)被取消時調(diào)用,此時onPostExecute就不會被調(diào)用
     * */
    override fun onCancelled() {
        super.onCancelled()
    }
}

②頁面布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".TaskActivity">

    <Button
        android:id="@+id/downloadBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="下載"
        app:layout_constraintBottom_toTopOf="@+id/downloadPb"
        app:layout_constraintEnd_toStartOf="@+id/downloadCancel"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ProgressBar
        android:id="@+id/downloadPb"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/downloadPercent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="還沒開始哦~"
        app:layout_constraintBottom_toTopOf="@+id/downloadPb"
        app:layout_constraintEnd_toEndOf="@+id/downloadCancel"
        app:layout_constraintStart_toStartOf="@+id/downloadBtn"
        app:layout_constraintTop_toBottomOf="@+id/downloadBtn" />

    <Button
        android:id="@+id/downloadCancel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="26dp"
        android:layout_marginLeft="26dp"
        android:text="取消下載"
        app:layout_constraintBottom_toBottomOf="@+id/downloadBtn"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/downloadBtn"
        app:layout_constraintTop_toTopOf="@+id/downloadBtn" />
</androidx.constraintlayout.widget.ConstraintLayout>

③調(diào)用execute()方法執(zhí)行異步任務(wù)。

 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        myTask = MyTask()
        downloadBtn.setOnClickListener {
            myTask.execute()
        }
        downloadCancel.setOnClickListener {
            myTask.cancel(true)
        }
    }
效果圖

④完整代碼

class TaskActivity : AppCompatActivity() {
    private lateinit var myTask: MyTask
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        myTask = MyTask()
        downloadBtn.setOnClickListener {
            myTask.execute()
        }
        downloadCancel.setOnClickListener {
            myTask.cancel(true)
        }
        //為AsyncTask設(shè)置線程池,收斂線程
        val ex = Executors.newFixedThreadPool(1)
        myTask.executeOnExecutor(ex)
    }

    inner class MyTask : AsyncTask<String, Int, String>() {
        /**
         * 在主線程中執(zhí)行
         * 在異步任務(wù)執(zhí)行之前
         * 可用于做一些準備工作
         * */
        override fun onPreExecute() {
            downloadPercent.text = "準備下載。。。"
        }

        /**
         * 在線程池中執(zhí)行,params標識異步輸入?yún)?shù)
         * */
        override fun doInBackground(vararg params: String?): String {
            //更新任務(wù)進度
            var progress = 0;
            while (progress < 99) {
                progress += 1
                publishProgress(progress)
                Thread.sleep(160)
            }
            return ""
        }

        /**
         * 在主線程中執(zhí)行
         * 當(dāng)后臺任務(wù)執(zhí)行進度改變時調(diào)用
         * */
        override fun onProgressUpdate(vararg values: Int?) {
            downloadPb.progress = values[0]!!
            downloadPercent.text = "${values[0]!!}%"
        }

        /*
        * 在主線程中執(zhí)行
        * 在異步任務(wù)執(zhí)行之后
        * result為doInBackground的返回值
        * */
        override fun onPostExecute(result: String?) {
            downloadPercent.text = "加載完畢"
        }

        /*
         *異步任務(wù)被取消時調(diào)用,此時onPostExecute就不會被調(diào)用
         * */
        override fun onCancelled() {
            downloadPercent.text = "取消了"
            downloadPb.progress = 0
        }
    }
}

2 AsyncTask的優(yōu)缺點

2.1 優(yōu)點

  • AsyncTask是一個輕量級的異步任務(wù)類,內(nèi)部封裝了Handler和Thread,可以自動實現(xiàn)線程的切換。
  • AsyncTask可以設(shè)置我們自己的線程池,達到收斂線程的目的。

2.2 缺點及注意點

  • AsyncTask被聲明為內(nèi)部類時,若不是靜態(tài)內(nèi)部類,可能會發(fā)生Activity銷毀時AsyncTask還持有Activity的引用導(dǎo)致Activity無法被回收。
  • 在OnDestory時調(diào)用AsyncTask的cancel方法取消異步功能
  • Android默認旋轉(zhuǎn)就會創(chuàng)建實例,因此屏幕旋轉(zhuǎn)時會導(dǎo)致之前的MyTask對象改變,Google提供了三種方案:
    ①對于少量數(shù)據(jù): 通過onSaveInstanceState(),保存有關(guān)應(yīng)用狀態(tài)的數(shù)據(jù)。 然后在 onCreate() 或 onRestoreInstanceState() 期間恢復(fù) Activity 狀態(tài)。
    ②對于大量數(shù)據(jù):用 Fragment 保留需要回復(fù)的對象。
    ③在Manifest中設(shè)置configChanges,不重啟Activity。
<activity
    android:name=".ConfigChangesTestActivity"
    android:configChanges="screenSize|orientation" >
</activity>

總結(jié)

?過去很多年見過與用到AsyncTask的次數(shù)并不多,因為AsyncTask存在很多明顯的問題,使用過程中總是需要特別留意,而我們還可以使用RxJava進行替代,因此對于使用AsyncTask并無太強烈的想法。直到今天AsyncTask被棄用,谷歌建議使用kotlin協(xié)程進行替代,可以說這天我等了很久,啊哈哈哈~就這樣吧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容