Kotlin Фрагмент NullPointerException - PullRequest
3 голосов
/ 12 июля 2020

Написание программы в Kotlin и использование фрагмента для отображения списка пользователей, которые заполняют RecyclerView из Firebase, но иногда получают исключение NullPointerException, отправляют журнал и код

Журнал:

java.lang.NullPointerException
        at com.fb.hc.fragments.UsersFragment$retrieveAllUser$1.onDataChange(UsersFragment.kt:108)
        at com.google.firebase.database.core.ValueEventRegistration.fireEvent(com.google.firebase:firebase-database@@19.3.0:75)
        at com.google.firebase.database.core.view.DataEvent.fire(com.google.firebase:firebase-database@@19.3.0:63)
        at com.google.firebase.database.core.view.EventRaiser$1.run(com.google.firebase:firebase-database@@19.3.0:55)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:237)
        at android.app.ActivityThread.main(ActivityThread.java:7807)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1047)

Код:

class UsersFragment : Fragment() {

    private var userAdapter: UsersAdapter? = null
    private var mUsers: List<Users>? = null
    ....

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

        mUsers = ArrayList()

        retrieveAllUser()

        return view
    }

    private fun retrieveAllUser() {

        val firebaseUserID = FirebaseAuth.getInstance().currentUser!!.uid
        val refUser = FirebaseDatabase.getInstance().reference.child("Users")

        refUser.addValueEventListener(object : ValueEventListener{
            override fun onDataChange(p0: DataSnapshot)
            {
                (mUsers as ArrayList).clear()

                if (topicCompleteView.text.toString() == "") {

                    for (snapshot in p0.children)
                    {
                        val user: Users? = snapshot.getValue(Users::class.java)
                        if (!(user?.getUID()).equals(firebaseUserID) && user != null) {
                            (mUsers as ArrayList<Users>).add(user)
                        }
                    }

                    if (mUsers.isNullOrEmpty()) {


                    } else {

                        // error line
                        userAdapter = UsersAdapter(context!!, mUsers as ArrayList<Users>, false)
                        recyclerView.adapter = userAdapter

                    }

                 }
            }

1 Ответ

3 голосов
/ 12 июля 2020

Вы выполняете асинхронный ввод-вывод непосредственно во фрагменте. Затем вы пытаетесь обновить пользовательский интерфейс фрагмента, когда эта работа завершена.

Проблема в том, что иногда ваш фрагмент будет уничтожен до завершения этой работы (например, пользователь нажал кнопку НАЗАД). В этом случае context будет null, а context!! будет ... плохим.

Тактическое исправление состоит в том, чтобы обновлять пользовательский интерфейс, только если у вас есть контекст:

activity?.let {
    userAdapter = UsersAdapter(it, mUsers as ArrayList<Users>, false)
    recyclerView.adapter = userAdapter
}

Лучшее исправление, с достаточным запасом, - полностью убрать этот ввод-вывод из фрагмента. Если пользователь запускает изменение конфигурации (вращает экран, переключает темный режим и т. Д. c.), Ваш новый фрагмент не будет получать результаты ввода-вывода из исходного фрагмента. Было бы лучше переместить этот ввод-вывод в ViewModel, который использует ваш фрагмент, где вы сделаете эти результаты доступными через LiveData или аналогичный механизм. Ваш фрагмент наблюдал бы LiveData и применил бы результаты. Это не только исправляет ошибку sh, но также лучше обрабатывает изменения конфигурации.

...