问题描述
问题
- 在 Android 视图中进行大量渲染的最佳做法是什么?
- 建议的方法是否指向正确的方向?
- 有哪些替代方案?
说明
我的 Android 应用滚动浏览 heavy rendered content
。渲染一个屏幕最多可能需要 500 毫秒。最终图像应显示给用户。
Heavy rendering
每秒可以触发多次(最多 20 次),只需通过手指拖动即可。
通常只有最后一次调用才重要。可以丢弃中间未处理的呼叫。如果处理了中间调用,则在渲染下一个/最后一个调用时向用户显示结果是有意义的。
显然,如果您将 heavy rendering
放入主线程,UI 将被冻结并且用户体验会受到影响。
当前实现
想法
重渲染和“最后一次调用很重要”的要求让您可以考虑使用单独的线程和 RxJava Flowable
。
这个想法是有两个位图:
-
intermediate bitmap
- 在线程中使用它,让heavy rendering
花费尽可能多的时间来绘制图片 -
final bitmap
- 使用它来存储最终图片并在应用调用 onDraw 函数时显示给用户
步骤:
- 触发处理后,调用被缓冲,只处理最后一个可用
- 渲染执行到
intermediate bitmap
(这是一个漫长的过程) - 渲染完成后,
intermediate bitmap
被转移到final bitmap
(这个应该很快) -
final bitmap
会在应用想要刷新屏幕的任何时候绘制在屏幕上(这也应该很快)。步骤 3 和 4 应synchronized
以防止闪烁。
架构
示意图如下:
intermediate --> final --> screen
bitmap bitmap bitmap
[long] [quick] [quick]
(Rendering thread) ? (--- Main thread ---)
|---------- sync1? ----------|
|------- sync2 ------|
代码
技术上可以这样做(也是示意图,省略了一些细节):
// trigger initialization
void init() {
mCanvasIntermediate = new Canvas(mBitmapIntermediate);
mCanvasFinal = new Canvas(mBitmapFinal);
rendering = PublishSubject.create();
rendering
.toFlowable(BackpressureStrategy.LATEST)
.onBackpressureLatest()
.observeOn(Schedulers.computation(),false,1)
.flatMapSingle(canvas -> Single.create(emitter -> {
canvas.drawBitmap(...);
emitter.onSuccess(result);
}))
.observeOn(AndroidSchedulers.mainThread()) // <-- comment to put invalidate to the thread
.subscribe(result -> {
mCanvasIntermediate.drawColor(Color.TRANSPARENT,PorterDuff.Mode.CLEAR);
mCanvasFinal.drawBitmap(mBitmapIntermediate,...);
invalidate();
});
}
// triggering
void triggerRendering() {
rendering.onNext(mCanvasIntermediate);
}
// view ondraw
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmapFinal,...);
}
问题
总的来说它有效,但并不完美:
- 如果渲染和输出intermediate->final都放在主线程中,渲染时屏幕会冻结,
- 如果只把输出intermediate->final放到主线程,屏幕就会闪烁,
- 如果渲染和输出intermediate->final都放在计算线程中,渲染过程中只会显示一部分屏幕。
更新:
这条评论对我帮助很大: https://stackoverflow.com/a/57610119/10475643
看来,这条线引起了整个小规模冲突:
mCanvasIntermediate.drawColor(Color.TRANSPARENT,PorterDuff.Mode.CLEAR);
因为 PorterDuff.Mode.CLEAR
不适用于 hardware acceleration。这导致闪烁和部分屏幕输出。最后,我将两个 drawBitmap
都放到了线程中。只要我删除 drawColor
或关闭硬件加速,闪烁就会消失。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)