如何将 android.support.v7.util.DiffUtil 与 RxJava、LiveData 一起使用而不生成跳过的 ### 帧
How to employ android.support.v7.util.DiffUtil with RxJava, LiveData and not generate Skipped ### frames
我当前的 Android 应用程序使用 LiveData 填充我的 recyclerView。
一些用户有 1000 项要显示。
我还允许我的用户search/filter 保存在 recyclerView 中的项目。
列表初始显示正常
然而,当用户开始 search/filter 从 1000 到 1 或 2 个项目的列表时,用户体验确实变得非常差,UI 在 10 秒内没有响应。
我的 RecyclerView 适配器使用 android.support。v7.util.DiffUtil 来管理列表项更改。
我尝试在我的 activity onChange 方法中使用 RxJava 来在后台线程上执行 diffing
Observable.just(newItems)
.doOnSubscribe(compositeDisposable::add)
.subscribeOn(Schedulers.io())
.switchMap(new Function<List<ItemUI>, ObservableSource<DiffUtil.DiffResult>>() {
@Override
public ObservableSource<DiffUtil.DiffResult> apply(final List<ItemUI> itemUIs) {
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new GenericDiffUtilCallback<>(adapter.getCurrentItems(), newItems));
return Observable.just(diffResult);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<DiffUtil.DiffResult>() {
@Override
public void accept(final DiffUtil.DiffResult diffResult) {
adapter.updateList(diffResult, articles);
}
});
我的 Adapter up[dateList 方法类似于此
public void updateList(final DiffUtil.DiffResult diffResult, final List<ItemUI> newItems) {
this.items.clear();
this.items.addAll(newItems);
diffResult.dispatchUpdatesTo(this);
}
我仍然看到 logcat 条这样的消息
Choreographer: Skipped 200 frames! The application may be doing too much work on its main thread.
我犯了什么错误,仍然看到跳帧?
是否可以区分大型列表更改并仍然具有响应性UI?
理论上,发布的代码应该不会在主线程上执行差异。
尝试以下操作:
Observable.just(newItems)
.observeOn(Schedulers.io()) // <------------------------------------
.map(new Function<List<ItemUI>, DiffUtil.DiffResult>() {
@Override
public DiffUtil.DiffResult apply(final List<ItemUI> itemUIs) {
return DiffUtil.calculateDiff(
new GenericDiffUtilCallback<>(
adapter.getCurrentItems(), itemUIs));
}
})
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(compositeDisposable::add) // <------------------------
.subscribe(new Consumer<DiffUtil.DiffResult>() {
@Override
public void accept(final DiffUtil.DiffResult diffResult) {
adapter.updateList(diffResult, articles);
}
});
你真的不需要switchMap
。另外如果你真的想让那个dispose生效,你必须尽可能地向下移动它,否则原始问题中的位置将不会影响它下面的diff计算。
我还会检查您是否无意中 运行 陷入线程问题,例如 this blog
中描述的情况
编辑:
如果您要更改源,可以使用 switchMap
,但也可以使用 fromCallable
:
.switchMap(itemUIs -> {
return Observable.fromCallable(() ->
DiffUtil.calculateDiff(new GenericDiffUtilCallback<>(
adapter.getCurrentItems(), itemUIs))
)
.subscribeOn(Schedulers.computation());
})
我当前的 Android 应用程序使用 LiveData 填充我的 recyclerView。
一些用户有 1000 项要显示。
我还允许我的用户search/filter 保存在 recyclerView 中的项目。
列表初始显示正常
然而,当用户开始 search/filter 从 1000 到 1 或 2 个项目的列表时,用户体验确实变得非常差,UI 在 10 秒内没有响应。
我的 RecyclerView 适配器使用 android.support。v7.util.DiffUtil 来管理列表项更改。
我尝试在我的 activity onChange 方法中使用 RxJava 来在后台线程上执行 diffing
Observable.just(newItems)
.doOnSubscribe(compositeDisposable::add)
.subscribeOn(Schedulers.io())
.switchMap(new Function<List<ItemUI>, ObservableSource<DiffUtil.DiffResult>>() {
@Override
public ObservableSource<DiffUtil.DiffResult> apply(final List<ItemUI> itemUIs) {
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new GenericDiffUtilCallback<>(adapter.getCurrentItems(), newItems));
return Observable.just(diffResult);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<DiffUtil.DiffResult>() {
@Override
public void accept(final DiffUtil.DiffResult diffResult) {
adapter.updateList(diffResult, articles);
}
});
我的 Adapter up[dateList 方法类似于此
public void updateList(final DiffUtil.DiffResult diffResult, final List<ItemUI> newItems) {
this.items.clear();
this.items.addAll(newItems);
diffResult.dispatchUpdatesTo(this);
}
我仍然看到 logcat 条这样的消息
Choreographer: Skipped 200 frames! The application may be doing too much work on its main thread.
我犯了什么错误,仍然看到跳帧?
是否可以区分大型列表更改并仍然具有响应性UI?
理论上,发布的代码应该不会在主线程上执行差异。
尝试以下操作:
Observable.just(newItems)
.observeOn(Schedulers.io()) // <------------------------------------
.map(new Function<List<ItemUI>, DiffUtil.DiffResult>() {
@Override
public DiffUtil.DiffResult apply(final List<ItemUI> itemUIs) {
return DiffUtil.calculateDiff(
new GenericDiffUtilCallback<>(
adapter.getCurrentItems(), itemUIs));
}
})
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(compositeDisposable::add) // <------------------------
.subscribe(new Consumer<DiffUtil.DiffResult>() {
@Override
public void accept(final DiffUtil.DiffResult diffResult) {
adapter.updateList(diffResult, articles);
}
});
你真的不需要switchMap
。另外如果你真的想让那个dispose生效,你必须尽可能地向下移动它,否则原始问题中的位置将不会影响它下面的diff计算。
我还会检查您是否无意中 运行 陷入线程问题,例如 this blog
中描述的情况编辑:
如果您要更改源,可以使用 switchMap
,但也可以使用 fromCallable
:
.switchMap(itemUIs -> {
return Observable.fromCallable(() ->
DiffUtil.calculateDiff(new GenericDiffUtilCallback<>(
adapter.getCurrentItems(), itemUIs))
)
.subscribeOn(Schedulers.computation());
})