猿问

如何修改函数内的 var 值?

我想设置这个名为 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(name child 的值),当前输出:“”


红糖糍粑
浏览 151回答 3
3回答

侃侃无极

监听器是异步的,如果你把 println 语句放在username =行下面,它就会打印。事实上,继续做吧;观察时间戳;哪个先打印?空的还是回调里面的?var正在被回调修改,但println在 Firebase 发出其值之前很久(在计算机时代,即)首先执行。此外,我会颠倒行的顺序mDatabase。您本质上是在请求一个值,然后听取结果;结果可能已经发出。您应该先添加侦听器,然后再请求数据。更新:如果我需要另一个回调的值怎么办?欢迎来到异步编程的世界:-)你描述的是一组独立的异步操作。您需要值 A 和值 B,但是在获得值 A 之前您无法获得值 B。两者都是异步的并且需要时间,但是您在主线程上没有时间,或者更确切地说,您有 ~16ms计算、测量和绘制屏幕,以便操作系统可以保持每秒 60 帧的速度。这不是很多时间,也是异步编程存在的部分原因!简而言之,您想要的是一个对象的实例,一旦操作完成就可以调用它。在常规同步函数中,每个语句都在另一个语句之后执行,并且在前一个语句未完成之前不会执行任何语句;因此,所有语句都是阻塞语句。例如:var times = 2var message = "Hello"var world = "World"println("The message is $message $times")println(world)将打印:The message is Hello 2World这是因为执行点会从一行走到另一行,等待上一行执行。如果一个操作需要时间,线程将被阻塞(无法执行任何其他操作)直到该操作完成并且执行点可以移动到下一条指令。可以想象,iOS 和 Android(以及 Windows、macOS、Linux 等)中的主线程无法被阻止,否则操作系统将无法响应您的触摸和其他发生的事情(例如,手机,如果用户界面没有响应并且您无法点击“接听”),则无法处理来电。这就是为什么我们使用其他“线程”来卸载不是超快的东西。这伴随着思维方式的改变以及正确的计划,因为现在事情变得更加复杂了。让我们看一个简单的例子(一些伪代码,所以请原谅任何明显的错误,这只是为了说明这一点,而不是写一个解决方案)。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 WorldHello如您所见,除了整整一秒钟,主线程没有响应外,没有任何变化。例如,如果您要在 Android 上运行它,它会工作,但您的应用程序在 Thread.sleep 期间不会有一秒钟的响应。一秒快,试试10秒;在决定需要 ANR(应用程序未响应)对话框之前,这超过了 Android 操作系统对主线程无响应的 5 秒限制;这就是臭名昭著的“看起来 XXX 应用程序没有响应,等待或关闭”。你能做什么?最初,如果你有太多的回调(其中回调 A 在回调 B 完成之前无法执行,而回调 B 在回调 C 完成之前无法执行),并且你开始像那样嵌套它们,你最终会陷入臭名昭著的回调地狱(在 Javascript中) ,但适用于任何语言/平台)。基本上跟踪所有这些异步回调并确保在响应到来时,您的下一个回调已准备就绪,等等是一件痛苦的事情,并且它会引入指数级的复杂性,例如,如果回调 C 在中间失败,现在您必须让回调 B 知道 C 失败了,因此它也必须失败,这反过来又必须让回调 A(原来的!)知道 B 失败了,因此 A 必须对此做些什么,A 是否需要知道 B 因为 C 而失败了吗?还是 A 只关心 B 和 B,而 B 失败背后的原因无关紧要?好吧,正如您所看到的,即使谈论这个也会变得复杂和混乱,我什至没有涵盖其他同样复杂的可能场景。我在这里想说的并不是说你不应该使用回调;而是说你不应该使用回调。而是您必须仔细计划何时何地使用它们。Kotlin 有一些替代方案可以通过使用协程来减少/消除回调地狱,但这些是一个中等高级的主题,它还需要对组件和部件的设计方式进行根本性的改变。总而言之,对于您的用例,请记住 OOP 的黄金法则:制作做很少事情的小型具体类,并将它们做好。if ()如果您需要在各处开始添加太多,那么您很可能会在各处混合业务逻辑、随机决策和“whatabout”案例。假设您有一个处理位置数据并将其上传到服务器的类。您可能会想:在 Activity/Fragment(或 ViewModel)中编写所有代码;很快就变得一团糟。使用静态方法(或单例模式)创建 LocationUtils;已经一团糟,但也很难测试和模拟。如果您需要不止一种类型的处理怎么办?或者如果你想将它们存储在数据库中,你是否要添加更多的静态方法?创建一个小的 LocationProcessor 类,它接收两个点(纬度/经度)在一个小函数中进行处理,并返回处理后的数据,然后创建另一个名为 LocationUploader 的类,它从处理器接收干净的输入,并将其上传到服务器. 这些类都不应该考虑“如果我没有权限怎么办,如果用户关闭位置怎么办”等等。这些问题超出了一个类的责任范围,该类的目的是处理位置坐标,别无其他。应该有其他类负责。请记住,小班级、小职责== 在单个文件中无需担心。结论?好吧,此时有更好的答案可以为您提供所需内容的复制粘贴版本;我相信你今天必须从这堵文字墙中拿出的概念是,为了编写现代的、可测试的、简单的功能代码,你计划事情的方式必须发生变化。长话短说:当事情不是同步的时候,你需要保持一些东西(一个对象)准备好被回调(因此名称回调),监听(或观察)(因此我们称它们为监听器或观察器),发射某物(通常称为 Observable,因为它可以被“观察”)。祝你好运!

慕森卡

正如 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                }    })

哈士奇WWW

是的,侦听器是异步的,只有在 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)        }    })}
随时随地看视频慕课网APP

相关分类

Java
我要回答