我想设置一个名为userName的变量,该变量应在ValueEventListener中获取一个新值。但是,在函数中设置新值时,它不会更改。输出仍然是“”
private fun getName(){
var userName = ""
val user = fbAuth.currentUser
var uid = user!!.uid
mDatabase = FirebaseDatabase.getInstance().getReference("users")
mDatabase.addValueEventListener(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
TODO("not implemented")
}
override fun onDataChange(snapshot: DataSnapshot) {
userName = snapshot.child(uid).child("name").getValue().toString()
}
})
println(userName)
}
预期输出:John(孩子名字的值), 当前输出:“”
答案 0 :(得分:2)
侦听器是异步的,如果将println语句放在username =
行的下面,则它将打印。
实际上,请继续执行该操作;观察时间戳;哪个先打印?空的还是回调中的一个?
var 正在被回调修改,但是println
首先执行,很久之前(在计算机时间,即Firebase发出它的值)。
此外,我将颠倒mDatabase
行的顺序。
本质上,您是在要求一个值,然后监听结果。结果可能已经发出。您应该先添加侦听器,然后请求数据。
欢迎来到异步编程的世界:-)
您描述的是一组独立的异步操作。您需要值A和值B,但是只有拥有值A才能获得值B。两者都是异步的,需要时间,但是您在主线程上没有时间,或者,您有〜16ms计算,测量和绘制屏幕,以便操作系统可以保持每秒60帧的速度。这不是很多时间,而是异步编程存在的部分原因!
This other answer已经提供了您所需的工作示例。 This other external link有一个更具体的Observer Listener模式示例。
简而言之,您想要的是一个对象的实例,可以在操作完成后调用它。
在常规同步函数中,每个语句都在另一个语句之后执行,直到上一个语句未完成才执行任何语句;因此,所有语句都是 blocking 语句。
例如:
var times = 2
var message = "Hello"
var world = "World"
println("The message is $message $times")
println(world)
将打印:
The message is Hello 2
World
这是因为执行点将从一行移到另一行,等待上一行执行。如果一个操作花费时间,则该线程将被阻止(无法执行其他任何操作),直到该操作完成并且执行点可以移至下一条指令为止。
您可以想象,iOS和Android(以及Windows,macOS,Linux等)中的主线程无法被阻止,否则OS将无法响应您的触摸和发生的其他事情(例如例如,在移动电话上,如果UI没有响应并且您无法点击“ answer”,则无法处理传入的电话。)
这就是为什么我们使用其他“线程”来卸载速度不是很快的事情的原因。伴随着思维方式的改变以及正确的计划,因为事情现在变得更加复杂。
让我们看一个简单的示例(一些伪代码,因此要承担任何明显的明显错误,这只是为了说明要点,而不是编写解决方案)。
fun main() {
var hello = "Hello"
var message = thisTakesTime()
println("The message is $hello $message")
println(hello)
}
fun thisTakesTime(): String {
// do something that takes 1 second (1000 ms)
Thread.sleep(1000)
return "World"
}
这将打印
The message is Hello World
Hello
如您所见,除了一整秒的时间,主线程没有响应,其他都没有改变。例如,如果要在Android上运行此程序,它将运行,但是在Thread.sleep期间,您的应用程序将在一秒钟内不响应。一秒钟很快,请尝试10秒钟;在决定是否需要ANR(应用程序无响应)对话框之前,这超出了Android操作系统5秒的限制,主线程无法响应;这就是臭名昭著的“看来XXX应用程序没有响应,正在等待或关闭”。
你能做什么?最初,如果您有太多的回调(其中回调A在回调B完成之前无法执行,而回调B在回调C完成之前无法执行),并且开始像这样嵌套它们,则您会陷入臭名昭著的{{3 }}(使用Javascript,但对任何语言/平台均有效)。
基本上跟踪所有这些异步回调,并确保在响应到达时 ,您的下一个回调已准备就绪,以此类推,这很痛苦,例如,如果回调,它将引入指数级复杂性C在中间失败,现在您必须让回调B知道C失败,因此它也必须失败,这又必须让回调A(原始!)知道B失败,因此A要对此做些什么,A是否需要知道B因C而失败了?还是A仅只关心B和B,而B失败的原因无关紧要?
嗯,正如您所看到的,即使谈论这件事也变得复杂和混乱,我什至没有涵盖同样复杂的其他可能情况。
我想在这里说的不是您不应该使用回调。这是您必须仔细计划在哪里以及何时使用它们。
Kotlin可以使用Callback-Hell来减少/消除回调地狱,但这是一个比较高级的主题,它还要求在设计组件和零件的方式上进行根本性的改变。
总而言之,在您的用例中,请记住OOP的黄金法则:制作一些很小的具体类,并且做得很好。如果您需要在各处开始添加过多的if ()
,那么您很可能在各地混合使用业务逻辑,随机决策和“关于”情况。
想象一下,您有一个处理位置数据并将其上载到服务器的类。
您可能会想:
好吧,在这一点上,有更好的答案,它将为您提供所寻找内容的复制粘贴版本;我相信您今天必须从这堵墙中删除的概念是,为了编写现代,可测试且简单的功能代码,必须改变计划方式。
长话短说:当事情不同步时,您需要准备好一些东西(一个对象)准备好被回调(因此叫回叫),监听(或观察)(因此我们称它们为监听者或观察者),某物的发射(通常称为Observable,因为它可以被“观察”)。
祝你好运!
答案 1 :(得分:0)
是的,侦听器是异步的,只有在onDataChange方法中打印变量时,侦听器才起作用。
但是,您可以使用回调策略来等待Firebase返回数据。像这样:
interface MyCallback {
fun onCallback(value: String )
}
fun readData(myCallback: MyCallback){
mDatabase.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
userName = snapshot.child(uid).child("name").getValue().toString()
myCallback.onCallback(value)
}
})
}
fun test(){
readData(object: MyCallback {
override fun onCallback(value : String) {
println(value)
}
})
}
答案 2 :(得分:0)
正如Martin所说,这是一个异步操作,您应该在异步过程完成后处理文本输出:
mDatabase.addValueEventListener(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
TODO("not implemented")
}
override fun onDataChange(snapshot: DataSnapshot) {
userName = snapshot.child(uid).child("name").getValue().toString()
println(userName) //--> Asynchronous request has ended, show the name
}
})