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

问题描述

问题

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

说明

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

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

通常只有最后一次调用才重要。可以丢弃中间未处理的呼叫。如果处理了中间调用,则在渲染下一个/最后一个调用时向用户显示结果是有意义的。

显然,如果您将 heavy rendering 放入主线程,UI 将被冻结并且用户体验会受到影响。

当前实现

想法

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

这个想法是有两个位图:

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

步骤:

  1. 触发处理后,调用被缓冲,只处理最后一个可用
  2. 渲染执行到intermediate bitmap(这是一个漫长的过程)
  3. 渲染完成后,intermediate bitmap 被转移到 final 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放到主线程,屏幕就会闪烁,
  • 如果渲染和输出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 (将#修改为@)