提问人:mspnr 提问时间:3/27/2021 最后编辑:Filipmspnr 更新时间:6/18/2021 访问量:117
Android 重度渲染的最佳做法是什么
What are best practices for Android heavy rendering
问:
问题
- 在 Android 视图中进行大量渲染的最佳做法是什么?
- 建议的方法是否指向正确的方向?
- 有哪些替代方案?
描述
我的 Android 应用程序滚动浏览 。一个屏幕的渲染最多可能需要 500 毫秒。最终图像应显示给用户。heavy rendered content
Heavy rendering
每秒可以触发多次(最多 20 次),只需手指拖动即可。
一般来说,只有最后的电话很重要。可以丢弃中间未处理的调用。但是,如果中间调用已处理,则在呈现下一个/最后一个调用时向用户显示结果是有意义的。
显然,如果你放在主线程中,UI将被冻结,用户体验将受到影响。heavy rendering
当前实现
想法
繁重的渲染以及“最后调用很重要”的要求让你可以考虑使用单独的线程和 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 放在主线程上,则屏幕闪烁,
- 如果渲染和输出中间>最终都放置在计算线程中,则在渲染过程中仅显示屏幕的一部分。
更新:
这句话对我帮助很大:https://stackoverflow.com/a/57610119/10475643
看来,这条线导致了整个小规模冲突:
mCanvasIntermediate.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
因为不能很好地与硬件加速配合使用。这导致闪烁和部分屏幕输出。最后,我把两个都放在线程上。只要我删除或关闭硬件加速,闪烁就会消失。PorterDuff.Mode.CLEAR
drawBitmap
drawColor
答: 暂无答案
评论