[译] 使用AsyncTask的光明和黑暗面之二

AsyncTask的具体实现

实现AsyncTask很简单:创建一个子类,重写doInBackground进行后台执行,并在后台任务执行之前,执行期间和执行之后为任何所需的UI更新添加帮助器方法。 尽管它很简单,但是还有一些要考虑的事项:

  • 避免内存泄漏

只要工作线程是活动的,它所引用的所有对象都保存在内存中,

因此,AsyncTask应该声明为一个独立的或静态的内部类,以便工作线程避免对外部类的隐式引用

  • 与Context及其生命周期的耦合

AsyncTask通常向可能引用Context的UI线程发出更新,该UI线程通常是指是具有View视图的Activity。但是您应该避免在AsyncTask中引用View视图,这样View视图就不会在不需要的时候保存在内存中。因此,AsyncTask应该声明为一个静态内部类,该类持有对相关Context的引用。当不再需要引用时,通过将其设置为null来删除引用。

  • 取消策略

合理运用取消策略中断或取消正在执行的后台任务

Example:Download Images

这个示例显示了如何在Activity中使用AsyncTask从网络下载图像。界面由一个进度条(一个mprogressbar,它显示下载的图像的数量)和一个Layout(使用mlayoutImages,它的子元素构成下载的图像)组成。

在Activity创建时,下载任务开始执行,并在Activity销毁时取消下载任务。

class FileDownloadActivity:AppCompatActivity() {
    private lateinit var mFileDownLoaderTask:DownloadTask

    companion object{
    
        // @1
        var DOWNLOAD_URLS:Array = arrayOf(
            "https://storage.googleapis.com/spec-host-backup/mio-components%2Fassets%2F1E7lXZfa3b5CAyF5bA33u2luiX3s08Scf%2Fbnb-limited.png",
            "https://storage.googleapis.com/spec-host-backup/mio-components%2Fassets%2F1Mf9DGzjrs8ky6_pgjc1SBKLpF5mZd9GP%2Fbnb-consistent.png",
            "https://storage.googleapis.com/spec-host-backup/mio-components%2Fassets%2F1BC1wiAWjQ9wD5rn9EbxDLvUsxOwkcQ8f%2Fbnb-convenient.png",
            "https://storage.googleapis.com/spec-host-backup/mio-design%2Fassets%2F0B3wFuHgbfPkzeXY2V2VKdm45Vzg%2Faccessibility-hierarchy-1-do.png",
            "https://storage.googleapis.com/spec-host-backup/mio-design%2Fassets%2F1hn9Ffe7iuhpl4HpJsIC6ikz0IPMwEfcJ%2Ftheming-color-surfacesbgs.png",
            "https://storage.googleapis.com/spec-host-backup/mio-design%2Fassets%2F13Oed330QwZjx6LCPqXlIiWPzVI_fZZh8%2Fcolor-colorsystem-schemecreation-secondary-baseline-1.png",
            "https://storage.googleapis.com/spec-host-backup/mio-design%2Fassets%2F1jTwR_tLfYC3x-B1bD8hN7Nza9x2y1Kny%2Ftheming-color-oncolors.png",
            "https://storage.googleapis.com/spec-host-backup/mio-design%2Fassets%2F1PqH1prDsSruO48qhgq6ZkuG-GSVVixMR%2Fcolor-colorsystem-schemecreation-accessibility-2.png",
            "https://storage.googleapis.com/spec-host-backup/mio-design%2Fassets%2F1Jo9yjAaKpYl3jijuskjU6_pfmML7n7B5%2Fcolor-colorsystem-schemecreation-altprimarysecondary-3.png",
            "https://storage.googleapis.com/spec-host-backup/mio-design%2Fassets%2F181H5cwjIitZNB5aMubkSHjqfBAr8mos2%2Fcolor-colorsystem-schemecreation-altprimarysecondary-4.png",
            "https://storage.googleapis.com/spec-host-backup/mio-design%2Fassets%2F1gNgK8xWBxDYQSuNvK1MoS3kFfPbetcgs%2Fcolor-colorsystem-schemecreation-altprimarysecondary-5.png",
            "https://storage.googleapis.com/spec-host-backup/mio-design%2Fassets%2F1SDGX3-el7fSZgwPwWVnMr7ri00_YV-Al%2Fcolor-colorsystem-schemecreation-altprimarysecondary-6.png",
            "https://storage.googleapis.com/spec-host-backup/mio-design%2Fassets%2F1Cr__9NRDY9K--yaovyZdora4YWV5bICC%2Fcolor-colorsystem-schemecreation-dataviz.png",
            "https://storage.googleapis.com/spec-host-backup/mio-design%2Fassets%2F12Tq1c4gZumASdmV4-Gz3Z2hsiqsrMOF_%2Fcasestudies-crane-productarch-tabsbackdrop-mobile.png"
        )

        
        //@4
        class DownloadTask(var activity: Activity):AsyncTask(){
            private var mCount = 0
            val TASK_TAG = "FileDownloadActivity"

            override fun onPreExecute() {
                super.onPreExecute()
                //@5
                activity.progress_bar.visibility = View.VISIBLE
                activity.progress_bar.progress = 0
            }

            override fun doInBackground(vararg urls: String?): Void? {
                Log.d(TASK_TAG,"doInBackground")
                for(index in urls.indices){
                    //11
                    Log.i("count",index.toString())
                    //@6
                    var bitmap = downloadFile(urls[index])
                    //@7
                    publishProgress(bitmap)
                }
                return null
            }

            //@8
            override fun onProgressUpdate(vararg values: Bitmap?) {
                super.onProgressUpdate(*values)
                Log.d(TASK_TAG,"onProgressUpdate")
                if(activity!=null){
                    activity.progress_bar.progress = ++mCount
                    var imageView = ImageView(activity)
                    imageView.setImageBitmap(values[0])
                    activity.layout_images.addView(imageView)
                }
            }

            override fun onPostExecute(result: Void?) {
                super.onPostExecute(result)
                Log.d(TASK_TAG,"onPostExecute")
                //@9
                activity.progress_bar.visibility = View.GONE
            }

            override fun onCancelled() {
                super.onCancelled()
                Log.d(TASK_TAG,"onCancelled")
                //@10
                activity.progress_bar.visibility = View.GONE
            }

            private fun downloadFile(url: String?): Bitmap? {
                var bitmap: Bitmap? = null
                try {
                    bitmap = BitmapFactory.decodeStream(URL(url).content as InputStream)
                } catch (e: MalformedURLException) {
                    e.printStackTrace()
                } catch (e: IOException) {
                    e.printStackTrace()
                }

                return bitmap
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_file_download)

        progress_bar.max = DOWNLOAD_URLS.size

        mFileDownLoaderTask = DownloadTask(this)
        //@2
        mFileDownLoaderTask.execute(*DOWNLOAD_URLS)
    }

    override fun onDestroy() {
        super.onDestroy()
        //@3
        mFileDownLoaderTask.cancel(true)
    }
}

@1--  图片的URL

@2--  将url传递给doInBackground。

@3--  当活动被销毁时,AsyncTask和它的引用被取消

@4-- 创建一个AsyncTask:将一个字符串对象数组传递给doInBackgroud,并在后台执行期间返回Bitmap对象。后台线程没           有将结果传递给UI线程,因此最后一个参数被声明为Void。

@5-- 在后台任务执行之前显示进度条。

@6-- 通过网络下载图像并将其分配给Bitmap

@7-- 将图像发送到UI线程。

@8-- 通过更新进度条和显示新图像来响应后台线程发送的进度更新。

@9 @10-- 删除进度条。

@11-- 这里我们打印执行的url的索引来判断是否取消成功

如果您点击FILEDOWNLOADACTIVITY按钮,开始下载图片,在下载任务还没完成时,返回到MainActvity,打印出的结果如下图所示:

[译] 使用AsyncTask的光明和黑暗面之二_第1张图片

看到这,很显然,我们以为.cancel(true)就会结束掉我们开启的正在执行的异步任务,但是实际上并没有结束掉我们想要结束的异步任务。原因在上一节已经说过了。

所以要想正确的结束掉异步任务,还记得最强取消策略的,您应该添加如下的@12、@13处所示的代码:

override fun doInBackground(vararg urls: String?): Void? {
                Log.d(TASK_TAG,"doInBackground")
                 for(index in urls.indices){
                        //@12
                      try{
                            for(index in urls.indices){
                                //@13
                                if(!isCancelled){
                                    Log.i("count",index.toString())
                                    val bitmap = downloadFile(urls[index])
                                    publishProgress(bitmap)
                                }
                            }
                       }catch (e:InterruptedException){
                            //do nothing
                            Log.i("count","exception")
                        }
                  }
                return null
            }

[译] 使用AsyncTask的光明和黑暗面之二_第2张图片

你可能感兴趣的:([译] 使用AsyncTask的光明和黑暗面之二)