Android 重度渲染的最佳做法是什么

What are best practices for Android heavy rendering

提问人:mspnr 提问时间:3/27/2021 最后编辑:Filipmspnr 更新时间:6/18/2021 访问量:117

问:

问题

  • 在 Android 视图中进行大量渲染的最佳做法是什么?
  • 建议的方法是否指向正确的方向?
  • 有哪些替代方案?

描述

我的 Android 应用程序滚动浏览 。一个屏幕的渲染最多可能需要 500 毫秒。最终图像应显示给用户。heavy rendered content

Heavy rendering每秒可以触发多次(最多 20 次),只需手指拖动即可。

一般来说,只有最后的电话很重要。可以丢弃中间未处理的调用。但是,如果中间调用已处理,则在呈现下一个/最后一个调用时向用户显示结果是有意义的。

显然,如果你放在主线程中,UI将被冻结,用户体验将受到影响。heavy rendering

当前实现

想法

繁重的渲染以及“最后调用很重要”的要求让你可以考虑使用单独的线程和 RxJava Flowable

这个想法是有两个位图:

  • intermediate bitmap- 在线程中使用它,让我们花尽可能多的时间来绘制图片heavy rendering
  • final bitmap- 使用它来存储最终图片,并在应用程序调用 onDraw 函数时显示给用户

步骤:

  1. 触发处理后,将缓冲调用,并且仅处理最后一个可用调用
  2. 渲染执行到(这是一个漫长的过程)intermediate bitmap
  3. 渲染完成后,被转移到 (这应该很快)intermediate bitmapfinal bitmap
  4. 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.CLEARdrawBitmapdrawColor

Android RX-Java OnDraw 重计算

评论


答: 暂无答案