«Lateinit никогда не инициализировался», хотя на самом деле он инициализируется внутри ListenerForSingleValueEvent - PullRequest
1 голос
/ 14 марта 2019

Обновлен код в Edit 2

Edit:
Я нашел этот , который, я думаю, решает ту же проблему, но мне трудно понять, что именно здесь происходит, так как это происходит на 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:

Прочитав ответ Алекса Мамо на этот вопрос и тот, который он прикрепил, я попытался соответствующим образом изменить свой код, но все равно не могу заставить его работать.

Вот текущий код (элементы разные, так как я работал над своим кодом, но это точно такой же случай)

* Я попытался поместить cal для readData с разделом аргументов или после него, но ни одна из них не работает для меня, и я продолжаю получать сообщение об ошибке, что imageObject не был инициализирован, когда я пытаюсь использовать его в onViewCreated

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

            }


        })

    }
}

А это интерфейс:

interface MyImageCallBack {
    fun onCallback(value: Images)
}

Ответы [ 3 ]

1 голос
/ 14 марта 2019

API Firebase имеют значение asynchronous, что означает, что функция onDataChange() возвращается сразу после ее вызова, а обратный вызов из Task, который она возвращает, будет вызван через некоторое время.Там нет никаких гарантий о том, сколько времени это займет.Таким образом, получение этих данных может занять от нескольких сотен миллисекунд до нескольких секунд.

Поскольку этот метод немедленно возвращается, значение вашего userProfile объекта, который вы получаете из базы данных и пытаетесь использовать его вне обратного вызова, еще не будет заполнено.

По сути, вы пытаетесь использовать значение синхронно из API, который является асинхронным.Это не очень хорошая идея.Вы должны обращаться с API асинхронно, как и предполагалось.Таким образом, в этом случае это не имеет ничего общего с lateinit.

. Быстрое решение этой проблемы - переместить следующие строки кода:

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

и использовать их только внутри обратного вызова.Таким образом, данные, поступающие из базы данных, будут доступны.

Если вы хотите использовать их вне обратного вызова, я рекомендую вам посмотреть последнюю часть моего ответа из этого сообщения , в котором я объяснил, как это можно сделать с помощью пользовательского обратного вызова.Это для Cloud Firestore, но те же правила применяются в случае базы данных Firebase в реальном времени.

1 голос
/ 14 марта 2019

В вашем случае, val uri = userProfile?.image запустите до userProfile = p0.getValue(Users::class.java), поэтому возникла эта проблема.

Что-то, что я думаю, помогает

  • code2 ниже code1 не означает, что code2 работает после code1 в время выполнения .
  • "Lateinit никогда не инициализировался" - это ошибка времени выполнения произошла, когда вы пытаетесь использовать переменную, когда она не инициализируется во время выполнения не время компиляции.
  • addListenerForSingleValueEvent is asynonymous
0 голосов
/ 14 марта 2019

Например, в следующем коде я получу сообщение о том, что userProfile никогда не инициализировался.

Проблема

Когда вызывается onDataChange, userProfile инициализируется. Но перед этим вы получаете доступ к userProfile. Поэтому возникло неинициализированное исключение.

Решение

переместите setupViews на onDataChange, попробуйте этот код

override fun onDataChange(p0: DataSnapshot) {
    userProfile = p0.getValue(Users::class.java).also {
        Picasso.get().load(it.image).into(profilePicture)
        profileName.text = it.name
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...