休息片刻后,我试图完成我的第一个android应用程序,并将其转换为Kotlin。一切进展顺利,但我收到有关正在调用本地存储的SQL数据库的异步任务的警告,并且错误是异步调用应该是静态的,否则它将泄漏。
所以我打算做正确的事,到目前为止,我需要使用Globalscope.launch。
这是我用来在另一个线程上访问数据库的代码。
private class MyAsyncTask extends AsyncTask<String, String, String>
{
@Override protected String doInBackground (String... params)
{
//SQL tasks, open read and close database
}
@Override protected void onPostExecute(String result)
{
// Tasks on retrieved database.
}
@Override protected void onPreExecute()
{ }
@Override protected void onProgressUpdate(String... text) {}
}
我进行了Kotlin转换,并且产生了我收到的以下代码,该代码应该是静态的,否则会引起内存泄漏警告:
private inner class MyAsyncTask : AsyncTask<String, String, String>() {
override fun doInBackground(vararg params: String): String?
{
//SQL tasks, open read and close database
}
override fun onPostExecute(result: String)
{
// Tasks on retrieved database.
}
override fun onPreExecute() {}
override fun onProgressUpdate(vararg text: String)
{}
}
这就是我现在应该在Kotlin的另一个线程上执行SQL调用的方式
private inner class MyAsyncTask()
{
GlobalScope.launch {
//SQL tasks, open read and close database
}
Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
// Tasks on retrieved database.
}
GlobalScope.launch是调用本地存储的SQL数据库的最佳和最安全的方法吗?如果没有,正确的方法是什么?
答案 0 :(得分:2)
AsyncTask
和协程的组合没有任何意义。两者都是在后台线程上执行某些操作的方法。特别是Thread.sleep()
违反了协程的主要思想:“非阻塞线程”。
我刚刚修改了示例的主要部分,以使您了解如何使用:
//Create an own coroutine scope for your activity
class MainActivity : AppCompatActivity(), CoroutineScope {
protected lateinit var job: Job
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
job = Job()
}
//destroy all coroutines, when the activity is going down
override fun onDestroy() {
super.onDestroy()
job.cancel()
}
//start a new coroutine
fun loadDataFromSQL() = launch { // Is invoked in UI context with Activity's job as a parent
val data = withContext(Dispatchers.IO) { // runs in background
//sql access
}
//runs in UI thread
// display data
}
}
答案 1 :(得分:0)
可以使用GlobalScope,但这不是最好的方法。您应该使用本地CoroutineScope。参见Roman Elizarov的这篇文章:https://medium.com/@elizarov/structured-concurrency-722d765aa952
答案 2 :(得分:0)
经过一周的阅读并尝试找到适合我需要的正确解决方案后,我发现Ian Alexander的解决方案最有帮助。 @Rene的解决方案很好,但不是我真正需要的解决方案,但这确实为我提供了线索,非常感谢@Rene。
注意事项,这是针对Kotlin 1.3的,因此Android Studio可能建议您升级到更高版本。
步骤1。 确保您的build.gradle具有以下两项,因为Dispatch.Main都需要这两项
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0'
第2步。
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
protected val mySQLScope = CoroutineScope(Dispatchers.Main)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//MAIN UI setup
println("Setting up Activity Main")
//get SQL database loaded in background
getSQLDatabase()
}
fun getSQLDatabase() {
mySQLScope.launch {
val user = withContext(Dispatchers.IO){
getSQLTASK()
}
//Any code here is blocked till getSQLTASK is finished
println("getSQLTASK now finished")
//Retrieved Database Now Usable
}
}
suspend fun getSQLTASK(){
//Code here blocks the main coroutine but does not affect the main thread.
delay(2000)
println("In getSQLTASK")
//SQL DATABASE LOADED HERE
}
}
这可行,但是如果我要确保在用户交换到另一个应用程序时该过程停止,那么我将需要执行以下操作:
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
protected val coroutineSup = SupervisorJob()
protected val mySQLScope = CoroutineScope(Dispatchers.Main + coroutineSup)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//MAIN UI setup
println("Setting up Activity Main")
//get SQL database loaded in background
getSQLDatabase()
}
fun getSQLDatabase() {
mySQLScope.launch {
val user = withContext(Dispatchers.IO){
getSQLTASK()
}
//Any code here is blocked till getSQLTASK is finished
println("getSQLTASK now finished")
//Retrieved Database Now Usable
}
}
suspend fun getSQLTASK(){
//Code here blocks the main coroutine but does not affect the main thread.
delay(2000)
println("In getSQLTASK")
//SQL DATABASE LOADED HERE
}
@CallSuper
override fun onPause() {
super.onPause()
coroutineSup.cancel()
//now crash avoided if user leaves app.
}
}
这将添加一个管理器,如果该应用程序不再有效使用,它将取消SQL检索。
希望这对某人有帮助,因为花了一个星期的时间才能使我明白。