4

我正在为动态壁纸中的动画拍摄。这是代码。它几乎遵循 CubeWallpaper 演示:

    void drawFrame() {
        final SurfaceHolder holder = getSurfaceHolder();
    final BufferedInputStream buf;
    final Bitmap bitmap, rbitmap;

        Canvas c = null;
        try {
            c = holder.lockCanvas();
            if (c != null) {
        try {
        buf = new 
            BufferedInputStream(assets.
                    open(folder+"/"
                         +imageList[ilen++])
                    );
        bitmap = BitmapFactory.
            decodeStream(buf);
        rbitmap = Bitmap.createBitmap
            (bitmap,
             0,0,imageWidth,imageHeight,
             transMatrix,false);
        c.drawBitmap(rbitmap,
                 paddingX,
                 paddingY,
                 null);
        if ( ilen >= imageCount ) ilen=0;
        }
        catch (Exception e) { e.printStackTrace(); }
            }
        } finally {
            if (c != null) holder.unlockCanvasAndPost(c);
        }

        // Reschedule the next redraw
        mHandler.removeCallbacks(mDrawCube);
        if (mVisible) {
            mHandler.postDelayed(mDrawCube, fps);
        }
    }

其中“transMatrix”是之前预定义的缩放和旋转矩阵。

它应该以 30fps 的速度渲染,但它当然不会那样做。我最初的猜测是 BufferedInputStream 是一个因素。当我与位图一起使用时,我可能应该缓存其中的一些。但还有其他想法吗?我是否必须将 OpenGL hack 用于动态壁纸?

4

2 回答 2

4

是的,BufferedInputStreamandBitmapFactory根本不应该出现drawFrame()。您在每一帧上加载和创建资源,这是一种巨大的浪费。就像你说的,尽可能多地缓存,如果你发现在绘图过程中需要加载更多,使用一个单独的线程来做,这样就不会减慢绘图速度。

于 2011-05-29T04:52:34.597 回答
3

我遇到了同样的问题:动态壁纸背景下的画布渲染速度很慢。

我同意其他人的说法,即在渲染时不应该做任何 cpu/io 繁重的工作,例如加载图像,尤其是在 UI 线程上。

但是,您还应该注意一件事。您在渲染帧后请求重绘 (mHandler.postDelayed(...))。如果您需要 30 fps 并因此请求在 (1000 / 30) 33ms 内重绘,那么它不会导致每秒 30 帧。为什么?

假设将所有东西渲染到画布上需要 28 毫秒。完成后,您在 33 毫秒后请求重绘。也就是说,重绘之间的时间间隔是 61 毫秒,相当于每秒 16 帧。

您有两种选择来解决它:

1) 将 mHandler.postDelayed(...) 代码放在 drawFrame(...) 方法的开头。这看起来不错,但它有一些缺点:如果您的实际 FPS 非常接近实际设备上可能的最大 FPS - 换句话说,UI 线程一直忙于您的画布渲染 - 那么就没有时间UI 线程做其他事情。这并不一定意味着您的 LWP 或主屏幕会滞后,但您(您的 LWP)可能会开始错过一些触摸事件(就像我的 LWP 所做的那样)。

2) 更好的解决方案是在创建表面时启动一个单独的线程并将其传递给 SurfaceHolder 的引用。在这个单独的线程中进行渲染。该线程中的渲染方法如下所示:

private static final int DESIRED_FPS = 25;
private static final int DESIRED_PERIOD_BETWEEN_FRAMES_MS = (int) (1000.0 / DESIRED_FPS + 0.5);



@Override
public void run() {
    while (mRunning) {
        long beforeRenderMs = SystemClock.currentThreadTimeMillis();            
        // do actual canvas rendering
        long afterRenderMs = SystemClock.currentThreadTimeMillis();
        long renderLengthMs = afterRenderMs - beforeRenderMs;
        sleep(Math.max(DESIRED_PERIOD_BETWEEN_FRAMES_MS - renderLengthMs, 0));
    }
}
于 2012-06-25T13:58:02.873 回答