如何在函数内修改var值?

时间:2019-08-05 20:04:49

标签: java android variables firebase-realtime-database kotlin

我想设置一个名为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(孩子名字的值), 当前输出:“”

3 个答案:

答案 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 (),那么您很可能在各地混合使用业务逻辑,随机决策和“关于”情况。

想象一下,您有一个处理位置数据并将其上载到服务器的类。

您可能会想:

  1. 在Activity / Fragment(或ViewModel)中编写所有代码;很快变得一团糟。
  2. 使用静态方法(或单例模式)创建LocationUtils;一团糟,但也很难测试和嘲笑。如果您需要一种以上的处理方式怎么办?或者,如果要将它们存储在数据库中,又要添加更多静态方法呢?
  3. 创建一个小的LocationProcessor类,该类接收两个点(经/长),通过一个小函数进行处理,并返回处理后的数据,然后创建另一个名为LocationUploader的类,该类接收来自Processor的干净输入并上载它到服务器。这些类都不应该考虑“如果我没有权限,如果用户关闭位置该怎么办”,等等。这些问题超出了旨在处理位置坐标的类的职责范围,别无其他。应该有其他类负责。请记住,小类,小责任 ==减少了在单个文件中的麻烦。

结论?

好吧,在这一点上,有更好的答案,它将为您提供所寻找内容的复制粘贴版本;我相信您今天必须从这堵墙中删除的概念是,为了编写现代,可测试且简单的功能代码,必须改变计划方式。

长话短说:当事情不同步时,您需要准备好一些东西(一个对象)准备好被回调(因此叫回叫),监听(或观察)(因此我们称它们为监听者或观察者),某物的发射(通常称为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
                }
    })