Canvas.drawBitmap()间歇性地减慢,导致白色闪烁

时间:2012-06-19 01:13:47

标签: android android-canvas

我正在制作一个带滚动背景的动态壁纸。我有两个位图对象,我之间交替,以保持下一帧的先前绘制的像素。我在画布顶部绘制一个新行,然后调用drawBitmap将其余像素复制到画布上。

我正在使用Runnable对象来完成繁重的工作。它执行所需的所有复制和计算,然后锁定画布,在持有者上输入同步块,并对Canvas.drawBitmap(位图,rect,rect,paint)进行单次调用。屏幕上偶尔会出现白色闪烁,这似乎与高CPU活动有关。在使用traceview时,我发现drawBitmap操作,特别是Canvas.native_drawBitmap(),比正常时间长得多。通常它会在2-4毫秒内完成,但当我看到白色闪光时,它可能需要10到100毫秒。

private void draw() {
    SurfaceHolder holder = getSurfaceHolder();

    Canvas canvas = null;
    prepareFrame();
    try {
        canvas = holder.lockCanvas();
        synchronized (holder) {
            if (canvas != null) {
                drawFrame(canvas);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (canvas != null)
            holder.unlockCanvasAndPost(canvas);
    }
    afterDrawFrame();
    handler.removeCallbacks(drawRunner);
    if (visible) {
        handler.post(drawRunner);
    }
}

在Runnable的draw()中调用run()函数。

private void prepareFrame() {
    num++;
    if (num%2 == 0) {
        mainBmp = mainBmp1;
        mainCan.setBitmap(mainBmp1);
        mainCan.drawBitmap(mainBmp2, source, destination, null);
    } else {
        mainBmp = mainBmp2;
        mainCan.setBitmap(mainBmp2);
        mainCan.drawBitmap(mainBmp1, source, destination, null);
    }
}

prepareFrame()函数是我如何保持我绘制的先前像素。被称为源的Rect是底部大小不足全屏的一行,其中目标是顶部的一行短。 <{1}}中的drawBitmap()次呼叫永远不会超过2-4毫秒。

prepareFrame()

这个单一操作是在持有锁的情况下在画布上完成的。

private void drawFrame(Canvas can) {
    can.drawBitmap(mainBmp, source, destination,null);
}

然后下一个新的像素行被绘制到内存中的一个位图上。

我尝试使用private void afterDrawFrame() { ca.calcNextRow(); mainBmp.setPixels(ca.getRow(), 0, canWidth, 0, 0, canWidth, 1); } 的各种签名,但只发现它们平均较慢,但仍会导致异常的白色闪烁。

我的整体速度很棒。没有间歇性闪光,它的效果非常好。有人有关于如何消除闪光的建议吗?

1 个答案:

答案 0 :(得分:5)

很难确切地知道这里发生了什么,因为你没有包括定义或使用一些中心变量,如“mainCan”或“ca”。更完整的源参考将是伟大的。

但是...

可能发生的事情是,因为drawFrame(canvas)在holder上同步,但是

handler.post(drawRunner);

不是,当你在prepareFrame()中写入mainBmp的同时,你会尝试将mainBmp绘制到系统画布。

这个问题的最佳解决方案可能是某种双重缓冲,你可以在其中执行类似

的操作
1) Write to a temporary bitmap
2) Change the ref of that bitmap to the double buffer i.e. mainBmp = tempBitmap;

主要目标是永远不要对用于系统画布渲染的变量进行长时间写入,只需更改对象引用即可。

希望这有帮助。