首页 > 解决方案 > 可在 ViewHolder 内部流动

问题描述

我又开始用 android 编程了,因为在过去的 4 年里它发生了很大的变化,我有点困惑。我正在尝试使用 ViewHolder 中的 RxJava 和 Flowable 从服务器异步加载我的数据。我正在使用第三方库来处理我在适配器MultiViewAdaptor中的视图。这是我的活页夹中的代码

class FooBinder : ItemBinder<Foo, FooBinder.ViewHolder>() {

        @Inject
        protected lateinit var requestFavoriteUseCase: SendFooFavoriteUseCase

        @Inject
        protected lateinit var compositeDisposable: CompositeDisposable

        @Inject
        protected lateinit var retrieveFooFavoriteCountUseCase: RetrieveFooFavoriteCountUseCase

        override fun create(inflater: LayoutInflater, parent: ViewGroup) =
            ViewHolder(inflater.inflate(R.layout.item_foo_content, parent, false))

        override fun bind(holder: ViewHolder, item: Foo) {
            holder.itemView.foo_content_creator.text = with(item.creator) { "$firstName $lastName" }

            GlideApp.with(holder.itemView)
                .load(item.creator.avatar)
                .dontAnimate()
                .placeholder(R.drawable.ic_action_person)
                .error(R.drawable.ic_action_person)
                .into(holder.itemView.foo_content_profile)

            retrieveFooFavoriteCountUseCase
                    .execute(item.id)
                    .applyComputationScheduler()
                    .subscribe { count ->
                        holder.itemView.foo_content_like_count.text = "$count"
                    }
                    .addTo(compositeDisposable)
        }

        inner class ViewHolder(view: View) : ViewHolder<Foo>(view) {

            private var favorite = false

            init {

                view
                    .foo_content_button_layout
                    .clicks()
                    .flatMap {
                        favorite = !favorite
                        requestFavoriteUseCase.execute(FavoriteVM(item.id, favorite)).toObservable()
                    }
                    .subscribe {
                        view.foo_content_like_count.text = it.toString()

                        var res = R.drawable.ic_action_like_default
                        if (favorite) {
                            res = R.drawable.ic_action_like_enabled
                        }
                        view.foo_content_like_icon.setImageResource(res)
                    }
                    .addTo(compositeDisposable)
            }

        }

    }

如您所见,我必须在不理想的绑定方法中调用服务器,并且每次用户滚动时都会调用它,我也无法取消订阅它们,直到 Activity 被销毁并调用compositeDisposable这意味着我将为一个视图提供多个一次性:(

我的问题是,如何在 ViewHolder 中使用 observable 并在屏幕中未显示时停止喂它?

标签: androidkotlinandroid-recyclerviewrx-javarx-java2

解决方案


你能做的最好的事情是:

  1. 依靠onViewAttachedToWindowonViewDetachedToWindow控制您的订阅生命周期(订阅/处置)。
  2. 创建/检索您的可观察对象onBindViewHolder
  3. 在父视图(片段/活动)中取消您的回收器上的适配器以强制适配器调用onViewDetachedToWindow所有窗口以确保您没有泄漏任何内容(例如片段,onDestoryView调用中list.adapter = null
  4. 添加delaySubscription(例如 400 毫秒)到您的 observables 以减少快速滚动时的滞后(当列表在动画上滑动时,您不想订阅任何内容)
  5. 确保你的 observables 的第一次发射是同步的,这样缓存的数据可以在屏幕旋转后布局之前传递。这样您将避免在旋转过程中出现动画故障,并且 Android 将能够正确恢复 View 状态。

第 5 点可能有点令人困惑,因此有一些资源可以更广泛地解释这一点:

所以基本上同步发射意味着在订阅方法返回之前订阅可观察的项目将被发射。例如,这种行为可以通过在缓存后不修改订阅发射线程的链中的某种缓存(如 replay(1)、startWith() 等)来实现。

例子:

Observable.create(2)
    .subscribeOn(Schedulers.computation())
    .observeOn(AndroidSchedulers.mainThread())
    .startWith(0)
    .subscribe(println(it))

println("after subscribe")

假设我们订阅了一个主线程。在这种情况下,我们将打印:

    0
    after subscribe
    2

如您所见,即使这两个项目都将在主线程上发出,其中一个项目将在订阅时立即到达,而第二个项目将发布在循环器上。


推荐阅读