【问题标题】:How can i modify a var value inside a function?如何修改函数内的 var 值?
【发布时间】:2019-08-05 20:04:49
【问题描述】:

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

【问题讨论】:

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


    【解决方案1】:

    监听器是异步的,如果你把 println 语句放在username = 行下面,它就会打印出来。

    事实上,继续做吧;观察时间戳;哪个先打印?空的还是回调里面的?

    var 正在被回调修改,但 println 首先执行,远在 Firebase 发出其值之前(在计算机时代)。

    另外,我会颠倒mDatabase 行的顺序。 您本质上是在请求一个值,然后监听结果;结果可能已经发出。您应该先添加侦听器,然后再请求数据。

    更新:如果我需要另一个回调的值怎么办?

    欢迎来到异步编程的世界:-)

    你描述的是一组独立的异步操作。您需要值 A 和值 B,但在获得值 A 之前无法获得值 B。两者都是异步的并且需要时间,但是您没有时间在主线程上,或者更确切地说,您有 ~16ms计算、测量和绘制屏幕,​​使操作系统能够跟上每秒 60 帧的速度。这不是很多时间,也是异步编程存在的部分原因!

    This other answer 已经提供了您需要的工作示例。 This other external link 有一个更具体的观察者侦听器模式示例。

    简而言之,你想要的是一个对象的实例,一旦操作完成就可以调用它。

    在一个常规的同步函数中,每条语句都在另一条语句之后执行,直到前一条语句没有完成,才会执行任何语句;因此,所有语句都是阻塞语句。

    例如:

    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 等)中的主线程无法被阻止,否则操作系统将无法响应您的触摸和其他发生的事情(例如例如,在手机上,如果 UI 没有响应并且您无法点按“接听”,则无法处理来电。

    这就是为什么我们使用其他“线程”来卸载不是超快的东西。这伴随着思维方式的改变和正确的计划,因为现在事情变得更加复杂了。

    让我们看一个简单的例子(一些伪代码,所以请承担任何明显的错误,这只是为了说明问题,而不是写解决方案)。

    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秒;这超过了 Android 操作系统对主线程无响应的 5 秒限制,然后才决定需要 ANR(应用程序无响应)对话框;这就是臭名昭著的“看起来 XXX 应用程序没有响应、等待或关闭”。

    你能做什么?

    最初,如果你有太多回调(回调 A 直到回调 B 完成才能执行,而回调 B 直到回调 C 完成才能执行),然后你开始像这样嵌套它们,你最终会陷入臭名昭著的 Callback-Hell (在 Javascript 中,但适用于任何语言/平台)。

    基本上跟踪所有这些异步回调并确保在响应到来时,您的下一个回调已准备就绪,等等是一件痛苦的事情,并且它会引入指数复杂性,例如回调C 在中间失败了,现在你必须让回调 B 知道 C 失败了,因此它也必须失败,这反过来又必须让回调 A(原来的!)知道 B 失败了,因此 A 有要对此做点什么,A 是否需要知道 B 因为 C 而失败?还是A只关心B和B,而B失败的原因无关紧要?

    好吧,正如您所看到的,即使谈论这个也会变得复杂和混乱,我什至没有涵盖同样复杂的其他可能的场景。

    我在这里想说的不是你不应该使用回调;而是你不应该使用回调。只是你必须仔细计划在何时何地使用它们。

    Kotlin 有替代方法可以通过使用 Coroutines 来减少/删除回调地狱,但这些是一个中等高级的主题,它还需要从根本上改变您设计组件和部件的方式。

    总而言之,对于您的用例,请记住 OOP 的黄金法则:创建小的具体类,做很少的事情,然后把它们做好。如果您需要开始在各处添加太多if (),那么您很可能会在各处混合业务逻辑、随机决策和“whatabout”案例。

    假设您有一个处理位置数据并将其上传到服务器的类。

    你可能会想:

    1. 在Activity/Fragment(或ViewModel)中编写所有代码;很快就会变得一团糟。
    2. 使用静态方法(或单例模式)创建 LocationUtils;已经一团糟,而且还很难测试和模拟。如果您需要不止一种类型的处理怎么办?或者如果你想将它们存储在数据库中,你会添加更多的静态方法吗?
    3. 创建一个小的 LocationProcessor 类,它接收两个点(纬度/经度)在一个小函数中进行处理,并返回处理后的数据,然后创建另一个名为 LocationUploader 的类,它接收来自处理器的干净输入并上传它到服务器。这些类都不应该考虑“如果我没有权限怎么办,如果用户关闭位置怎么办”等。这些问题超出了旨在处理位置坐标的类的职责,仅此而已。应该有其他类负责。请记住,小类,小职责 == 在单个文件中无需担心。

    结论?

    嗯,现在有更好的答案可以为您提供所需内容的复制粘贴版本;我相信您今天必须从这堵文字墙中汲取的概念是,为了编写现代、可测试和简单的功能代码,您必须改变计划事情的方式。

    长话短说:当事情不同步时,您需要保持某些东西(一个对象)准备好被回调(因此名称为回调),侦听(或观察)(因此我们称它们为侦听器或观察者),某些东西的发射(通常称为 Observable,因为它可以被“观察”)。

    祝你好运!

    【讨论】:

    • 首先,感谢您的帮助 :) 不幸的是,我想使用第一个 Listener 发出的数据作为另一个 Listener 的参数来发出其值。我知道,我可能应该在我的问题中提到这一点。你知道我可以解决这个问题的方法吗?再次感谢您的时间:)
    • 嗨尼克,欢迎来到 SO。您所描述的是从一开始就使用的观察者 - 听众模式的常见情况(不是那么多):)。我会更新这个问题,尽管this other answer -in kotlin- 可能会给你一个提示。 :)
    【解决方案2】:

    是的,监听器是异步的,只有在 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)
            }
        })
    
    }
    

    【讨论】:

      【解决方案3】:

      正如马丁所说,这是一个异步操作,您应该在异步过程完成后处理文本输出:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-26
        • 2019-05-28
        • 2023-02-04
        • 1970-01-01
        • 2013-02-18
        • 2021-09-24
        相关资源
        最近更新 更多