【问题标题】:AsyncTask in Android with KotlinAndroid 中的 AsyncTask 与 Kotlin
【发布时间】:2017-11-15 11:04:47
【问题描述】:

如何使用 Kotlin 在 Android 中进行 API 调用?

我听说过Anko。但我想使用 Kotlin 提供的方法,比如在 Android 中我们有 Asynctask 用于后台操作。

【问题讨论】:

  • 你也可以在 Kotlin 中使用 AsyncTask

标签: android kotlin android-asynctask


【解决方案1】:

AsyncTask 是 Android API,而不是 Java 或 Kotlin 提供的语言功能。如果你愿意,你可以像这样使用它们:

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

    override fun onPreExecute() {
        super.onPreExecute()
        // ...
    }

    override fun onPostExecute(result: String?) {
        super.onPostExecute(result)
        // ...
    }
}

Anko 的 doAsync 并不是 Kotlin 真正“提供”的,因为 Anko 是一个使用 Kotlin 的语言特性来简化长代码的库。在这里检查:

如果您使用 Anko,您的代码将类似于:

doAsync {
    // ...
}

【讨论】:

  • 我们如何防止这个警告“可能发生泄漏使异步任务静态”?
  • @Killer 不要把它放在因为 AsyncTask 可能停留太久的类中(例如 Activity),除非你把它们放在检查中,并在你的时候取消 AsyncTask 的所有实例类应该被销毁。
【解决方案2】:

你可以很容易地获得与 Anko 类似的语法。如果你只是不想后台任务,你可以做类似的事情

class doAsync(val handler: () -> Unit) : AsyncTask<Void, Void, Void>() {
    override fun doInBackground(vararg params: Void?): Void? {
        handler()
        return null
    }
}

并像使用它一样

doAsync {
    yourTask()
}.execute()

【讨论】:

  • 我最近迁移到 Kotlin。所以我有一些疑问.. 当您从 Activity 或 Fragment 调用此 doAsync 时,如何确定不会发生内存泄漏。你会在哪里得到结果? Anko 的 doAsync 是生命周期感知的,对吧?我们如何才能将它引入 doAsyn?
  • @Mohanakrrishna 这是一个旧答案,我永远不会使用它。使用例如LiveData 和你所有的烦恼都没有了。因为它是 Kotlin - 使用协程,你所有的烦恼都没有了。
【解决方案3】:

这是一个示例,它还允许您更新显示给用户的任何 UI 或进度。

异步类

class doAsync(val handler: () -> Unit) : AsyncTask<Void, Void, Void>() {
    init {
        execute()
    }

    override fun doInBackground(vararg params: Void?): Void? {
        handler()
        return null
    }
}

简单用法

doAsync {
    // do work here ...

    myView.post({
        // update UI of myView ...
    })
}

【讨论】:

  • 谢谢伙计。这对使用 Kotlin 的人来说就像一个魅力。拯救了我的一天。
【解决方案4】:
package com.irontec.kotlintest

import android.os.AsyncTask
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.Menu
import android.view.MenuItem
import android.widget.TextView
import kotlinx.android.synthetic.main.activity_main.*
import org.json.JSONObject
import java.io.BufferedInputStream
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL


class MainActivity : AppCompatActivity() {

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

        GetWeatherTask(this.text).execute()
    }

    class GetWeatherTask(textView: TextView) : AsyncTask<Unit, Unit, String>() {

        val innerTextView: TextView? = textView

        override fun doInBackground(vararg params: Unit?): String? {
            val url = URL("https://raw.githubusercontent.com/irontec/android-kotlin-samples/master/common-data/bilbao.json")
            val httpClient = url.openConnection() as HttpURLConnection
            if (httpClient.responseCode == HttpURLConnection.HTTP_OK) {
                try {
                    val stream = BufferedInputStream(httpClient.inputStream)
                    val data: String = readStream(inputStream = stream)
                    return data
                } catch (e: Exception) {
                    e.printStackTrace()
                } finally {
                    httpClient.disconnect()
                }
            } else {
                println("ERROR ${httpClient.responseCode}")
            }
            return null
        }

        fun readStream(inputStream: BufferedInputStream): String {
            val bufferedReader = BufferedReader(InputStreamReader(inputStream))
            val stringBuilder = StringBuilder()
            bufferedReader.forEachLine { stringBuilder.append(it) }
            return stringBuilder.toString()
        }

        override fun onPostExecute(result: String?) {
            super.onPostExecute(result)

            innerTextView?.text = JSONObject(result).toString()

            /**
             * ... Work with the weather data
             */

        }
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.menu_main, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        val id = item.itemId
        if (id == R.id.action_settings) {
            return true
        }
        return super.onOptionsItemSelected(item)
    }
}

链接 - Github Irontec

【讨论】:

  • 这看起来很不对劲。即使 asynctask 是一个内部类...我看不到您在哪里检查 onPostExecute 中的活动状态?另外,不要一起使用 Asynctask。
【解决方案5】:

AsyncTask 在 API 级别 30 中已弃用。要实现类似的行为,我们可以使用 Kotlin concurrency utilities (coroutines)

CoroutineScope上创建扩展函数:

fun <R> CoroutineScope.executeAsyncTask(
        onPreExecute: () -> Unit,
        doInBackground: () -> R,
        onPostExecute: (R) -> Unit
) = launch {
    onPreExecute()
    val result = withContext(Dispatchers.IO) { // runs in background thread without blocking the Main Thread
        doInBackground()
    }
    onPostExecute(result)
}

现在它可以用于任何CoroutineScope 实例,例如ViewModel

class MyViewModel : ViewModel() {

      fun someFun() {
          viewModelScope.executeAsyncTask(onPreExecute = {
              // ...
          }, doInBackground = {
              // ...
              "Result" // send data to "onPostExecute"
          }, onPostExecute = {
              // ... here "it" is a data returned from "doInBackground"
          })
      }
  }

或在Activity/Fragment:

lifecycleScope.executeAsyncTask(onPreExecute = {
      // ...
  }, doInBackground = {
      // ...
      "Result" // send data to "onPostExecute"
  }, onPostExecute = {
      // ... here "it" is a data returned from "doInBackground"
  })

要使用viewModelScopelifecycleScope 将下一行添加到应用的build.gradle 文件的依赖项:

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$LIFECYCLE_VERSION" // for viewModelScope
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$LIFECYCLE_VERSION" // for lifecycleScope

【讨论】:

    【解决方案6】:

    这是我在项目中避免内存泄漏的方式:

    我为异步加载创建了一个abstract base Async Task

    import android.os.AsyncTask
    
    abstract class BaseAsyncTask(private val listener: ProgressListener) : AsyncTask<Void, Void, String?>() {
    
        interface ProgressListener {
            // callback for start
            fun onStarted()
    
            // callback on success
            fun onCompleted()
    
            // callback on error
            fun onError(errorMessage: String?)
    
        }
    
        override fun onPreExecute() {
            listener.onStarted()
    
        }
    
        override fun onPostExecute(errorMessage: String?) {
            super.onPostExecute(errorMessage)
            if (null != errorMessage) {
                listener.onError(errorMessage)
            } else {
                listener.onCompleted()
            }
        }
    }
    

    用法:

    现在每次我必须在后台执行一些任务时,我都会创建一个新的 LoaderClass 并使用我的 BaseAsyncTask 类扩展它,如下所示:

    class LoadMediaTask(listener: ProgressListener) : BaseAsyncTask(listener) {
    
        override fun doInBackground(vararg params: Void?): String? {
    
            return VideoMediaProvider().allVideos
        }
    }
    

    现在您可以在应用中的任何位置使用新的 AsyncLoader 类。

    以下是显示/隐藏进度条和处理错误/成功场景的示例:

       LoadMediaTask(object : BaseAsyncTask.ProgressListener {
                override fun onStarted() {
                    //Show Progrss Bar
                    loadingBar.visibility = View.VISIBLE
                }
    
                override fun onCompleted() {
                    // hide progress bar
                    loadingBar.visibility = View.GONE
                    // update UI on SUCCESS
                    setUpUI()
                }
    
                override fun onError(errorMessage: String?) {
                    // hide progress bar
                    loadingBar.visibility = View.GONE
                    // Update UI on ERROR
                    Toast.makeText(context, "No Videos Found", Toast.LENGTH_SHORT).show()
                }
    
            }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
    

    【讨论】:

      【解决方案7】:

      我总是使用这种形式:

      open class LoadingProducts : AsyncTask<Void, Void, String>() {
      
      private var name = ""
      
          override fun doInBackground(vararg p0: Void?): String {
      
              for (i in 1..100000000) {
                  if (i == 100000000) {
                      name = "Hello World"
                  }
              }
              return name
          }
      }
      

      您可以通过以下方式调用它:

      loadingProducts = object : LoadingProducts() {
              override fun onPostExecute(result: String?) {
                  super.onPostExecute(result)
      
                  Log.e("Result", result)
              }
          }
      
      loadingProducts.execute()
      

      我使用 open 以便可以调用 onPostExecute 方法获取结果。

      【讨论】:

        【解决方案8】:

        我花了一整天的时间试图弄清楚如何取回异步任务产生的结果:协程是我的解决方案!!!

        首先,创建您的 AsyncTask 对象...不要忘记使用更正参数类型而不是所有 Any

        @SuppressLint("StaticFieldLeak")
        class AsyncTaskExample(private var activity: MainActivity?) : AsyncTask<Any, Int, Any?>() {
        
            override fun onPreExecute() {
                super.onPreExecute()
                // do pre stuff such show progress bar
            }
        
            override fun doInBackground(vararg req: Any?): Any? {
        
                // here comes your code that will produce the desired result
                return result 
        
            }
        
            // it will update your progressbar
            override fun onProgressUpdate(vararg values: Int?) {
                super.onProgressUpdate(*values)
        
            }
        
        
            override fun onPostExecute(result: Any?) {
                super.onPostExecute(result)
        
                // do what needed on pos execute, like to hide progress bar
                return
            }
        
        }
        

        然后,调用它(在这种情况下,从主要活动)

        var task = AsyncTaskExample(this)
        var req = { "some data object or whatever" }
        
        GlobalScope.launch( context = Dispatchers.Main){
        
           task?.execute(req)
        }
        
        GlobalScope.launch( context = Dispatchers.Main){
        
           println( "Thats the result produced by doInBackgorund: " +  task?.get().toString() )
        }
        

        【讨论】:

          【解决方案9】:

          如果你想在不使用Anko的情况下这样做,正确的方法是使用以下方式

          open class PromotionAsyncTask : AsyncTask<JsonArray, Void, MutableList<String>>() {
          
          private lateinit var out: FileOutputStream
          private lateinit var bitmap: Bitmap
          private lateinit var directory: File
          private var listPromotion: MutableList<String> = mutableListOf()
          
          override fun doInBackground(vararg params: JsonArray?): MutableList<String> {
          
              directory = Environment.getExternalStoragePublicDirectory("Tambo")
          
              if (!directory.exists()) {
                  directory.mkdirs()
              }
          
              for (x in listFilesPromotion(params[0]!!)) {
                  bitmap = BitmapFactory.decodeStream(URL(x.url).content as InputStream)
                  out = FileOutputStream(File(directory, "${x.name}"))
          
                  bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
                  out.flush()
                  out.close()
          
                  listPromotion.add(File(directory, "${x.name}").toString())
              }
          
              return listPromotion
          }
          
          private fun listFilesPromotion(jsonArray: JsonArray): MutableList<Promotion> {
              var listString = mutableListOf<Promotion>()
          
              for (x in jsonArray) {
                  listString.add(Promotion(x.asJsonObject.get("photo")
                          .asString.replace("files/promos/", "")
                          , "https://tambomas.pe/${x.asJsonObject.get("photo").asString}"))
              }
          
              return listString}
          }
          

          其执行方式如下

          promotionAsyncTask = object : PromotionAsyncTask() {
                              override fun onPostExecute(result: MutableList<String>?) {
                                  super.onPostExecute(result)
                                  listFile = result!!
          
                                  contentLayout.visibility = View.VISIBLE
                                  progressLottie.visibility = View.GONE
                              }
                          }
                          promotionAsyncTask.execute(response!!.body()!!.asJsonObject.get("promos").asJsonArray)
          

          【讨论】:

            【解决方案10】:
              private fun updateUI(account: GoogleSignInAccount?) {
                if (account != null) {
                    try {
                        AsyncTaskExample().execute()
                    } catch (e: Exception) {
                    }
                }
            }
            inner class AsyncTaskExample : AsyncTask<String, String, String>() {
            
                        override fun onPreExecute() {
                            super.onPreExecute()
            
                        }
            
                        override fun doInBackground(vararg p0: String?): String {
            
                            var Result: String = "";
                            try {
                                googleToken = GoogleAuthUtil.getToken(activity, accountVal, "oauth2:https://www.googleapis.com/auth/userinfo.profile")
                                signOut()
                            } catch (e: Exception) {
                                signOut()
                            }
            
            
                            signOut()
            
                            return Result
                        }
            
                        override fun onPostExecute(result: String?) {
                            super.onPostExecute(result)
                            socialPrsenter.setDataToHitApiGoogleLogin(googleToken ?: "")
            
                        }
                    }
            

            【讨论】:

              猜你喜欢
              • 2021-09-21
              • 2018-04-22
              • 2018-01-30
              • 2018-10-12
              • 1970-01-01
              • 2018-01-04
              • 2020-02-11
              • 2015-06-01
              • 1970-01-01
              相关资源
              最近更新 更多