使用GlobalScope进行Kotlin SQL调用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用GlobalScope进行Kotlin SQL调用相关的知识,希望对你有一定的参考价值。

休息后,我试图完成我的第一个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数据库的最佳和最安全的方法吗?如果没有,那么正确的方法是什么?

答案

AsyncTask和Coroutines的组合毫无意义。两者都是在后台线程上执行某些操作的方法。特别是Thread.sleep()反对协同程序的主要思想:“非阻塞线程”。

协同程序和用户界面的一个很好的解释是:https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md#structured-concurrency-lifecycle-and-coroutine-parent-child-hierarchy

我刚刚调整了示例的主要部分,以便了解如何使用:

//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
    }                
}
另一答案

使用GlobalScope是可能的,但这不是最好的方法。您应该使用本地CoroutineScope。请参阅Roman Elizarov撰写的这篇文章:https://medium.com/@elizarov/structured-concurrency-722d765aa952

另一答案

经过一周的阅读并试图找到满足我需求的正确解决方案后,我发现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检索的主管。

希望这可以帮助某人,因为它花了一个星期的阅读来解决它。

以上是关于使用GlobalScope进行Kotlin SQL调用的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin协程GlobalScope.launch无法引用

Kotlin协程GlobalScope.launch无法引用

如何启动Kotlin协程

如何启动Kotlin协程

kotlin 协程万字协程 一篇完成kotlin 协程进阶

Kotlin SelectClause0