如何使用 Kotlin 在 Android 中进行 API 调用?
我听说过安科.但是我想使用 Kotlin 提供的方法,就像在 Android 中我们有 Asynctask 进行后台操作一样。
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 的语言特性来简化长代码的库。在这里查看:
- https://github.com/Kotlin/anko/blob/d5a526512b48c5cd2e3b8f6ff14b153c2337aa22/anko/library/static/commons/src/Async.kt
如果你使用Anko,你的代码将类似于这样:
doAsync {
// ...
}
你可以得到一个与Anko类似的语法相当容易。如果您只是不想执行后台任务,则可以执行以下操作
class doAsync(val handler: () -> Unit) : AsyncTask<Void, Void, Void>() {
override fun doInBackground(vararg params: Void?): Void? {
handler()
return null
}
}
并像使用它一样使用
doAsync {
yourTask()
}.execute()
下面是一个示例,它还允许您更新向用户显示的任何 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 ...
})
}
AsyncTask
在 API 级别 30 中已弃用。为了实现类似的行为,我们可以使用 Kotlin 并发实用程序(协程(。
在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"
})
要使用viewModelScope
或lifecycleScope
将下一行添加到应用的 build.gradle 文件的依赖项中,请执行以下操作:
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$LIFECYCLE_VERSION" // for viewModelScope
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$LIFECYCLE_VERSION" // for lifecycleScope
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
在项目中避免内存泄漏的方式:
我为异步加载创建了一个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)
我总是使用这种形式:
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
方法。
我花了一整天的时间试图弄清楚如何取回异步任务产生的结果:协程是我的解决方案!!
首先,创建您的异步任务对象...不要忘记使用更正参数类型代替所有任何
@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() )
}
<</div>
div class="one_answers">如果您想在不使用 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)
我在可组合中使用LaunchedEffect
LaunchedEffect ("http_get") {
withContext (Dispatchers.IO) {
http_get() }}
并在回调中rememberCoroutineScope
val scope = rememberCoroutineScope()
Button (
onClick = {
scope.launch {
withContext (Dispatchers.IO) {
http_get() }}})
它似乎有效,但我不知道为什么。
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 ?: "")
}
}