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

rlo*_*ong 12 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

draw()函数run()在Runnable中调用.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

prepareFrame()功能是我如何保持我绘制的先前像素.被称为源的Rect是底部大小不足全屏的一行,其中目标是顶部的一行短.在drawBitmap()调用中prepareFrame()永远不会比2-4msec更长.

private void drawFrame(Canvas can) {
    can.drawBitmap(mainBmp, source, destination,null);
}
Run Code Online (Sandbox Code Playgroud)

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

private void afterDrawFrame() {
    ca.calcNextRow();
    mainBmp.setPixels(ca.getRow(), 0, canWidth, 0, 0, canWidth, 1);
}
Run Code Online (Sandbox Code Playgroud)

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

我尝试过使用各种签名,drawBitmap()但只发现它们平均较慢,但仍会导致异常的白色闪烁.

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

Eri*_*vic 5

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

但...

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

handler.post(drawRunner);
Run Code Online (Sandbox Code Playgroud)

事实并非如此,当您在prepareFrame()中写入mainBmp的同时,将尝试将mainBmp绘制到系统画布.

这个问题的最佳解决方案可能是某种双缓冲,你做的事情就像

1) Write to a temporary bitmap
2) Change the ref of that bitmap to the double buffer i.e. mainBmp = tempBitmap;
Run Code Online (Sandbox Code Playgroud)

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

希望这可以帮助.