【发布时间】:2018-10-25 20:35:10
【问题描述】:
问题
我正在使用 Kotlin 合成绑定、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 都已成功创建并且已经在手机上可见(我什至可以看到崩溃前的加载屏幕)。
那么为什么我的视图变为空,即使在 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)
}
// ...
}
片段
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 的建议,我尝试让片段保留实例,但没有奏效。
我还尝试放弃 Kotlin 合成绑定,并将 recyclerView 变量设置为 lateinit var,它在 onCreateView() 方法中膨胀,如下面的 sn-p:
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 引用,即使尽管新的已成功创建并已显示。我不确定我应该在哪里清理它,或者它为什么会这样,也许它与supportFragmentManager有关?
【问题讨论】:
-
您是否尝试过覆盖 Activity 清单标签中的旋转和 screenSize 配置更改?
-
我想知道您的演示者在您的 MVP 中是如何工作的:您的演示者是否也引用了视图?如果是这样,您的演示者可能会指向一个在轮换中被破坏的旧视图。你能分享你的演示者和片段附件吗?
-
演示者没有持有旧引用,我什至将 View 引用清空,并在 Activity 上的
onStart()方法之后重新附加它 -
@Mauker 感谢您分享代码,我确信它与您的
SectionsPagerAdapter有关,我已经发布了答案。我希望它有效!
标签: android android-recyclerview kotlin rx-java2