休息后,我试图完成我的第一个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检索的主管。
希望这可以帮助某人,因为它花了一个星期的阅读来解决它。