是否可以使用后台线程绘制画布?

时间:2013-07-24 09:17:03

标签: android multithreading canvas

我正在创建一个人脸识别应用程序,它会在检测到脸部时绘制矩形。目前,所有的计算都是在主线程上完成的,因为它耗尽了大量的CPU。 我决定在不同的线程上运行计算。现在问题是,是否可以从后台线程本身绘制画布?或者我必须将参数发送到主线程,然后从那里绘制画布?

2 个答案:

答案 0 :(得分:6)

是的,从另一个线程而不是从UI线程渲染画布!这将改善和优化绩效。

以下是我编写的一些代码:

package com.webstorms.framework;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.view.SurfaceView;

public class RenderView extends WSObject implements Runnable {

    Bitmap gameScreen;
    SurfaceView surfaceView;
    Thread gameloop;
    boolean running;

    int sleepTime;
    int numberOfFramesSkipped;
    int maxFrameSkips;
    long beginTime;
    long endTime;
    long lastTime;
    int differenceTime;
    int framePeriod;
    Canvas frameBuffer;
    int frameCount;

    int realFPS;
    int setFPS;

    /**
     * This class is the game loop that will update and render the game.
     * 
     */

    public RenderView(Game game, Bitmap gameScreen, int fps, int maxFrameSkips) {
        super(game);
        this.gameScreen = gameScreen;
        surfaceView = new SurfaceView(game);
        this.setFPS = fps;
        this.framePeriod = 1000/this.setFPS;
        this.maxFrameSkips = maxFrameSkips;
        lastTime = System.currentTimeMillis();
        beginTime = System.currentTimeMillis();

    }

    public SurfaceView getView() {
        return this.surfaceView;

    }

    @Override
    public void run() {
        while(running) {
            if(this.surfaceView.getHolder().getSurface().isValid()) {

                beginTime = System.currentTimeMillis();
                this.getGame().getInput().update(); // Synchronize input and call all attached listeneres
                this.getGame().getCurrentScreen().update();
                this.renderFrameBuffer();

                // Frame Per Second Count
                frameCount++;

                if(lastTime + 1000 < System.currentTimeMillis()) {
                    WSLog.d(Game.GAME_ENGINE_TAG, this, "REAL FPS: " + frameCount);
                    this.realFPS = frameCount;
                    lastTime = System.currentTimeMillis();
                    frameCount = 0;

                }

                endTime = System.currentTimeMillis();
                differenceTime = (int) (endTime - beginTime);
                sleepTime = (int) (framePeriod - differenceTime);

                if(sleepTime > 0) {
                    try {
                        Thread.sleep(sleepTime);

                    } 
                    catch (InterruptedException exception) {
                        exception.printStackTrace();

                    }

                }
                else {
                    while(sleepTime < 0 && numberOfFramesSkipped < this.maxFrameSkips) {
                        WSLog.d(Game.GAME_ENGINE_TAG, this, "Game thread is only updating the update method and is not rendering anything");
                        this.getGame().getCurrentScreen().update();
                        sleepTime += framePeriod;
                        numberOfFramesSkipped++;

                    }

                }

            }

        }


    }

    public int getRealFPS() {
        return this.realFPS;

    }

    public int getSetFPS() {
        return this.setFPS;

    }

    private void renderFrameBuffer() {
        // Update the current virtual screen image
        this.getGame().getCurrentScreen().render();
        // Render the current virtual screen to the real phone screen
        frameBuffer = this.surfaceView.getHolder().lockCanvas();
        if(frameBuffer != null) { // Fix for mysterious bug ( FATAL EXCEPTION: Thread)
            frameBuffer.drawBitmap(this.gameScreen, null, this.getGame().getWSScreen().getGameScreendst(), null);
            this.surfaceView.getHolder().unlockCanvasAndPost(frameBuffer);

        }
        else {
            WSLog.e(Game.GAME_ENGINE_TAG, this, "Surface has not been created or otherwise cannot be edited");

        }

    }

    public void resume() { 
        this.running = true;
        gameloop = new Thread(this);
        gameloop.start();      

    }   

    public void pause() { 
        this.running = false;
        running = false;                        
        while(true) {
            try {
                gameloop.join();
                break;
            } 
            catch (InterruptedException e) {
                // retry
            }

        }

    }


}

创建renderview对象时,请为第一个参数传递活动的引用。

在您的活动中也这样做:

this.setContentView(this.renderView.getView());

答案 1 :(得分:0)

这是没有问题的。它可以帮助您优化渲染。