android - 屏幕旋转和 rxjava 后查看实例 null

标签 android android-recyclerview kotlin rx-java2

问题

我正在使用 Kotlin 合成绑定(bind)、RxJava、Retrofit 和 MVP 模式开发 Android 应用程序。在此应用程序中,有一个屏幕应该从 API 获取一些数据,并使用返回的数据填充 RecyclerView

所有这些每次都有效,直到我尝试旋转屏幕...一旦重新创建 Activity 和 Fragment 并尝试访问 RecyclerView,应用程序就会崩溃,并出现以下异常:

10-25 18:20:00.368 4580-4580/com.sample.app E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.sample.app, PID: 4580
    java.lang.IllegalStateException: recyclerView must not be null
        at com.sample.app.features.atendimento.ClientesAtendimentosFragment.onAtendimentosLoaded(ClientesAtendimentosFragment.kt:56)
        at com.sample.app.features.atendimento.ClienteAtendimentoActivity.showClientesAtendimento(ClienteAtendimentoActivity.kt:126)
        at com.sample.app.features.atendimento.ClienteAtendimentoPresenter$getClientesAtendimento$3.accept(ClienteAtendimentoPresenter.kt:38)
        at com.sample.app.features.atendimento.ClienteAtendimentoPresenter$getClientesAtendimento$3.accept(ClienteAtendimentoPresenter.kt:18)
        at io.reactivex.internal.observers.ConsumerSingleObserver.onSuccess(ConsumerSingleObserver.java:63)
        at io.reactivex.internal.operators.single.SingleDoAfterTerminate$DoAfterTerminateObserver.onSuccess(SingleDoAfterTerminate.java:71)

当我尝试调用此方法时会发生这种情况:

override fun onAtendimentosLoaded(atendimentos: List<UsuarioAtendimento>) {
    recyclerView.visibility = View.VISIBLE
    recyclerView.adapter = ClienteAtendimentoAdapter(atendimentos, this)
}

由于某种原因,recyclerView变量为空,应用程序崩溃了,即使Activity和Fragment都已​​成功创建并且已经在手机上可见(我什至可以在崩溃之前看到加载屏幕)。

那么为什么我的 View 变得为空,即使 Activity 和 Fragment 已经可见?

编辑:我还添加了 MVP 文件以帮助了解此处发生的情况。

演讲者

class ClienteAtendimentoPresenter(private val repository: ClienteAtendimentoRepository): ClienteAtendimentoContract.Presenter {

    private var view: ClienteAtendimentoContract.View? = null

    private var disposable: Disposable? = null

    override fun getClientesAtendimento() {
        try {
            disposable = repository.getClientesEmAtendimento()
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .doOnSubscribe {
                        view?.hideErrors()
                        view?.showLoading()
                    }
                    .doAfterTerminate {
                        view?.hideLoading()
                    }
                    .subscribe(
                            {
                                view?.showClientesAtendimento(it)
                            },
                            {
                                handleError(it)
                            }
                    )
        } catch (e: NoConnectionException) {
            view?.hideLoading()
            handleError(e)
        }
    }

    override fun stop() {
        view = null
        disposable?.dispose()
    }

    override fun attachView(view: BaseView<BasePresenter>) {
        if (view is ClienteAtendimentoContract.View)
            this.view = view
    }
}

查看

class ClienteAtendimentoActivity : BaseActivity(), ClienteAtendimentoContract.View {

    override val presenter: ClienteAtendimentoContract.Presenter by inject()

    private lateinit var mSectionsPagerAdapter: SectionsPagerAdapter

    inner class SectionsPagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) {
        private val fragAtendimento = ClientesAtendimentosFragment.newInstance()
        private val fragEspera = ClientesAtendimentosFragment.newInstance()

        override fun getItem(position: Int): Fragment {
            return if (position == 0) fragAtendimento else fragEspera
        }

        override fun getCount(): Int = 2
    }

    private fun setupTabs() {
        mSectionsPagerAdapter = SectionsPagerAdapter(supportFragmentManager)

        // Set up the ViewPager with the sections adapter.
        container.adapter = mSectionsPagerAdapter

        container.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabs))
        tabs.addOnTabSelectedListener(TabLayout.ViewPagerOnTabSelectedListener(container))
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_cliente_atendimento)

        // ...

        setupTabs()
    }

    override fun onResume() {
        super.onResume()
        presenter.attachView(this)
        presenter.getClientesAtendimento()
    }

    override fun onPause() {
        super.onPause()
        presenter.stop()
    }

    // ...

    override fun showClientesAtendimento(atendimentos: AtendimentosLista) {
        val atendimentosFrag = mSectionsPagerAdapter.getItem(0) as ClientesAtendimentosFragment
        val filaEsperaFrag = mSectionsPagerAdapter.getItem(1) as ClientesAtendimentosFragment

        atendimentosFrag.onAtendimentosLoaded(atendimentos.atendimentos) // This is where I'm getting the error.
        filaEsperaFrag.onAtendimentosLoaded(atendimentos.espera)
    }

    // ...

}

fragment

class ClientesAtendimentosFragment : Fragment(), AtendimentosFragmentCallback, OnClickAtendimentoCallback {

    private lateinit var recyclerView: RecyclerView

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

        recyclerView = rootView.findViewById(R.id.recyclerView) // I did this to test if Kotlin synthetic binding was the problem.

        return rootView
    }

    // ...

    override fun onAtendimentosLoaded(atendimentos: List<UsuarioAtendimento>) {
        recyclerView.visibility = View.VISIBLE // App is crashing here.
        recyclerView.adapter = ClienteAtendimentoAdapter(atendimentos, this)
    }

    // ...
}

到目前为止我尝试过的内容

按照 this answer 上的建议,我尝试让 fragment 保留实例,但没有成功。

我还尝试放弃 Kotlin 合成绑定(bind),并将 recyclerView 变量设置为在 onCreateView() 方法内膨胀的 lateinit var,如下面的代码 fragment 所示:

private lateinit var recyclerView: RecyclerView

//...

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

    recyclerView = rootView.findViewById(R.id.recyclerView)

    return rootView
}

但现在我收到以下错误:

10-25 18:32:42.261 10572-10572/com.sample.app E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.sample.app, PID: 10572
    io.reactivex.exceptions.UndeliverableException: kotlin.UninitializedPropertyAccessException: lateinit property recyclerView has not been initialized
        at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:367)
        at io.reactivex.internal.observers.ConsumerSingleObserver.onSuccess(ConsumerSingleObserver.java:66)
        at io.reactivex.internal.operators.single.SingleDoAfterTerminate$DoAfterTerminateObserver.onSuccess(SingleDoAfterTerminate.java:71)
        at io.reactivex.internal.operators.single.SingleDoOnSubscribe$DoOnSubscribeSingleObserver.onSuccess(SingleDoOnSubscribe.java:77)
        at io.reactivex.internal.operators.single.SingleObserveOn$ObserveOnSingleObserver.run(SingleObserveOn.java:81)
        at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:119)
        at android.os.Handler.handleCallback(Handler.java:739)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:145)
        at android.app.ActivityThread.main(ActivityThread.java:6939)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
     Caused by: kotlin.UninitializedPropertyAccessException: lateinit property recyclerView has not been initialized
        at com.sample.app.features.atendimento.ClientesAtendimentosFragment.onAtendimentosLoaded(ClientesAtendimentosFragment.kt:59)

编辑:我做了什么Xite suggested on his answer ,并且我发现由于某种原因,当我旋转屏幕时,新的 Rx 调用仍在尝试使用旧的 Fragment 引用,即使新的 Fragment 引用已成功创建并已显示。我不确定我应该在哪里清理它,或者为什么它会这样,也许它与 supportFragmentManager 有关?

最佳答案

首先,尝试对其进行调试,并在旋转之前和旋转之后在 onAtendimentosLoaded() 中查找 fragment (this) 的哈希码。假设您没有使用 setRetainInstance,它们应该是不同的。如果它们相同,则说明您没有正确清除某些内容,很难判断到底是什么导致了问题,因为我们没有相关的 fragment 、 Activity 和演示者代码。

关于android - 屏幕旋转和 rxjava 后查看实例 null,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52997700/

相关文章:

java - 添加从我的 RecycleView 中选择的项目

android - Chipdrawable 自定义字体文本被裁剪

android - 修复了 Jetpack Compose 中 LazyColumn 内的网格?

java - 覆盖未在 Android 中调用的方法

Kotlin Context 具有高阶函数?

Kotlin 将字符串自动转换为 Enum

android - 如何更改 Recycler 项目颜色?

android - 删除项目后 Recyclerview 不刷新

java - 当单击 RecyclerView 中的一行中的某个按钮时,如何从 Activity 类中检测?

javafx - 带有 TestFX 的 TornadoFX 在每个 TestCase 后关闭 View