Firebase &引用;“Lateinit从未初始化”;而它实际上是在ListenerForSingleValueEvent中初始化的

Firebase &引用;“Lateinit从未初始化”;而它实际上是在ListenerForSingleValueEvent中初始化的,firebase,firebase-realtime-database,kotlin,Firebase,Firebase Realtime Database,Kotlin,在编辑2中更新了代码 编辑: 我发现我认为解决了同样的问题,但我很难理解这里到底发生了什么,因为它在Java中,我的Java不如我的kotlin。如有任何澄清,将不胜感激 每当我声明一个lateinit变量,然后从一个ListenerForSingleValueEvent初始化它时,我都会得到一个从未初始化过的错误。这就像初始化是受侦听器限制的。我最后做的是大量嵌套,但这会使代码非常混乱。这有什么原因吗?有办法绕过它吗 例如,在下面的代码中,我将得到一个错误,说明userProfile从未初始化

在编辑2中更新了代码

编辑:
我发现我认为解决了同样的问题,但我很难理解这里到底发生了什么,因为它在Java中,我的Java不如我的kotlin。如有任何澄清,将不胜感激

每当我声明一个lateinit变量,然后从一个ListenerForSingleValueEvent初始化它时,我都会得到一个从未初始化过的错误。这就像初始化是受侦听器限制的。我最后做的是大量嵌套,但这会使代码非常混乱。这有什么原因吗?有办法绕过它吗

例如,在下面的代码中,我将得到一个错误,说明
userProfile
从未初始化:

class ProfileFragment : Fragment() {

    lateinit var userProfile: Users

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?  = inflater.inflate(R.layout.fragment_profile, container, false)


    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)


        arguments?.let {
            val safeArgs = ProfileFragmentArgs.fromBundle(it)
            val userProfileUid = safeArgs.usersProfile
            val refUser = FirebaseDatabase.getInstance().getReference("users/$userProfileUid")

            refUser.addListenerForSingleValueEvent(object : ValueEventListener{
                override fun onCancelled(p0: DatabaseError) {

                }

                override fun onDataChange(p0: DataSnapshot) {
                    userProfile = p0.getValue(Users::class.java)
                }

            })
        }

        val uri = userProfile?.image
    Picasso.get().load(uri).into(profilePicture)
    profileName.text = userProfile?.name


    }
}
编辑2:

在阅读了Alex Mamo关于这个问题的答案和他所附的答案后,我试图相应地修改我的代码,但仍然无法正常工作

这是y当前代码(元素不同,因为我一直在编写代码,但情况完全相同)

*我尝试将cal for readData与arguments部分或之后放在一起,但这两种方法都不适用于我,并且我一直得到一个错误,即当我尝试在onViewCreated中使用imageObject时,imageObject未初始化

class ImageFullSizeFragment : androidx.fragment.app.Fragment() {

    lateinit var refImage: DatabaseReference
    lateinit var imageObject: Images

    override fun onAttach(context: Context) {
        super.onAttach(context)

        arguments?.let {
            val safeArgs = ImageFullSizeFragmentArgs.fromBundle(it)

            val imageId = safeArgs.imageId

            refImage = FirebaseDatabase.getInstance().getReference("/images/feed/$imageId")

            readData(object : MyImageCallBack {
                override fun onCallback(value: Images) {
                    imageObject = value
                }
            })
        }
    }



    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)


        val mainImage = view.findViewById<ImageView>(R.id.image_full_image)

        Picasso.get().load(Uri.parse(imageObject.image)).into(mainImage)
    }


    fun readData(myImagesCallback: MyImageCallBack) {


        refImage.addListenerForSingleValueEvent(object : ValueEventListener {
            override fun onCancelled(p0: DatabaseError) {

            }

            override fun onDataChange(p0: DataSnapshot) {

                val image = p0.getValue(Images::class.java)

                myImagesCallback.onCallback(image!!)

            }


        })

    }
}

在您的例子中,
val uri=userProfile?.image
userProfile=p0.getValue(Users::class.java)
之前运行

我觉得这有帮助

  • code2
    以下
    code1
    并不意味着它
    code2
    code1
    运行时运行
  • “Lateinit从未初始化”是运行时错误,当您尝试使用未在运行时非编译时初始化的变量时发生
  • addListenerForSingleValueEvent
    asynonymous
例如,在下面的代码中,我将得到一个错误,指出userProfile从未初始化

问题 调用
onDataChange
时,
userProfile
初始化。但在此之前,您可以访问
userProfile
。因此,发生了未初始化的异常

解决方案 将
setupview
移动到
onDataChange
,尝试此代码

override fun onDataChange(p0: DataSnapshot) {
    userProfile = p0.getValue(Users::class.java).also {
        Picasso.get().load(it.image).into(profilePicture)
        profileName.text = it.name
    }
}

Firebase API是
异步的
,这意味着调用
onDataChange()
函数后立即返回,它返回的
任务
的回调将在稍后调用。无法保证需要多长时间。因此,在数据可用之前,可能需要几百毫秒到几秒钟的时间

因为该方法会立即返回,所以从数据库中获取并试图在回调外部使用的
userProfile
对象的值还没有填充

基本上,您正在尝试同步使用来自异步API的值。那不是个好主意。您应该按照预期异步处理API。因此,在本例中,它与
lateinit
无关

此问题的快速解决方法是移动以下代码行:

val uri = userProfile?.image
Picasso.get().load(uri).into(profilePicture)
profileName.text = userProfile?.name
并仅在回调中使用它们。通过这种方式,来自数据库的数据将可用


如果你想在回调之外使用它,我建议你看一下我的anwser的最后一部分,我已经解释了如何使用自定义回调。这是针对Cloud Firestore的,但同样的规则适用于Firebase实时数据库。

谢谢,我假设如果它在它之后,那么它将在它之后运行。我会调查的。谢谢Alex,很抱歉回复晚了。我觉得我必须在回调之外使用它,因为我的片段承载着另一个片段,而子片段需要访问我从侦听器获得的对象。我看了你的另一个答案,看起来很清楚,很直截了当,但我肯定错过了什么,因为我无法让它发挥作用。介意告诉我我做错了什么吗?我用我的新代码添加了一个edit(元素不同,因为我一直在编写代码,但情况完全相同)。正如我看到的,您正在做与以前相同的事情。在
readData
函数中指定
imageObject=value
并不意味着它将能够简单地在这一行
Picasso.get().load(Uri.parse(imageObject.image)).into(mainImage)
中使用
imageObject
的值。要解决此问题,请将该行移到函数
readData
中或更改获取数据的逻辑。这就是异步编程的工作原理。很抱歉我来自澳大利亚,回复太晚了。哈哈。我将其标记为正确,因为它听起来是正确的,但我个人最终使用共享视图模型来传输数据。我感谢你的帮助!如果你也认为我的回答也会有助于未来的开发者,也请考虑投票表决。▲). 非常感谢,谢谢!
val uri = userProfile?.image
Picasso.get().load(uri).into(profilePicture)
profileName.text = userProfile?.name