Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/195.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 为什么我可以在同一个线程中操作UI和access数据库?_Android_Multithreading_Kotlin - Fatal编程技术网

Android 为什么我可以在同一个线程中操作UI和access数据库?

Android 为什么我可以在同一个线程中操作UI和access数据库?,android,multithreading,kotlin,Android,Multithreading,Kotlin,刚刚发现了我对线程理解的差距。我有以下功能,可以将一些结果保存到数据库中并离开活动: private fun leave() { GlobalScope.launch { prepareForSaving() println("thread:${Thread.currentThread()}") gameData.save.id = JigsawDatabase(this@GameActivity).save

刚刚发现了我对线程理解的差距。我有以下功能,可以将一些结果保存到数据库中并离开活动:

private fun leave() {
        GlobalScope.launch {
            prepareForSaving()
            println("thread:${Thread.currentThread()}")
            gameData.save.id = JigsawDatabase(this@GameActivity).savesDao().upsert(gameData.save).toInt()
            val resIntent = Intent()
            val res=Gson().toJson(result)
            resIntent.putExtra("gameResult",res)
            setResult(0, resIntent)
            finish()
        }
}
从一个侧面看,
println
prints
thread:thread[DefaultDispatcher-worker-1,5,main]
表示其工作线程。但我仍然可以访问UI,因为
finish()
工作正常。完全混乱。
UPD: 在将
GlobalScore
更改为我的viewModel的coroutineScope时出现异常:

private fun leave() {
    model.viewModelScope.launch {
    gameData.save.id = 
    JigsawDatabase(this@GameActivity).savesDao().upsert(gameData.save).toInt()// throws Cannot access database on the main thread
    ...
    finish()
    }
}
即使我将上下文指定为model.viewModelScope.launch(Dispatchers.IO)它仍然可以访问UI,这对我来说毫无意义;DR Android上有一些硬检查,用于访问不应该在UI线程上执行的内容,但对于不应该在工作线程上执行的内容,没有太多硬检查

解释

这里有几件事。关于协同路由,如果您确实使用了
GlobalScope
或任何作用域,而没有指定它将在哪个调度程序上运行,那么它将在
dispatcher.Default
上运行,后者是计算线程

GlobalScope.launch {
    // This is why you print thread:Thread[DefaultDispatcher-worker-1,5,main] here
    println("thread:${Thread.currentThread()}")
}
现在,工作线程确实可以访问/更新您的数据库

gameData.save.id = JigsawDatabase(this@GameActivity).savesDao().upsert(gameData.save).toInt()
正如您所提到的,您也在更新您调用
finish()
的UI,并且不会出现崩溃

这是因为并非所有的方法(大多数视图方法实际上不检查此项)都进行检查,以确保它们不是从主线程访问的。只有几件事能证明这一点

以实时数据的
setValue
方法为例,说明所做的事情

@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

private static void assertMainThread(String methodName) {
    if (!ArchTaskExecutor.getInstance().isMainThread()) {
        throw new IllegalStateException("Cannot invoke " + methodName + " on a background"
                + " thread");
    }
}
这实际上是显式检查,以确保您在
main线程上而不是在worker线程上调用此更新,这是大多数UI元素没有的检查。它们可能会以运行时难以调试的方式失败

调用数据库时获取异常

当您切换到
viewModel
范围时,可能已将其设置为
Dispatchers.Main

在这一点上,如果您想调用显式进行了检查以确保在工作线程(如房间数据库)上调用的东西,您将遇到此崩溃

Cannot access database on the main thread since it may potentially lock the UI for a long periods of time.

finish()
可以从任何线程调用。请注意,使用
GlobalScope
是一种代码气味—这主要是针对书籍示例和博客帖子。通常,有一个更好的
CoroutineScope
可供使用。我尝试设置其中一个按钮的alpha-也可以。
android上是否仍然只有一个UI线程?
是。在iOS、Windows、macOS和Linux上。很少有人尝试使用多线程UI。据我所知,它做得不好,我们到了。您的viewodelScope设置在哪个调度程序上运行?我想我可以用不同的方式来表述这个问题,比如
为什么我可以在同一个线程中操作UI和访问数据库?
看起来确实像是安全检查只存在于修改显示列表的方法中。我得到的
只是创建视图层次结构的原始线程可以触摸其视图。
添加removeView调用时出现异常。这很有意义。谢谢!这正是我想要的答案。非常感谢。