Surfaceview和UI线程交互和锁定游戏变量

时间:2016-08-19 12:29:15

标签: java android multithreading surfaceview

我在线程上有一个surfaceview更新/在画布上绘制。我认为由于surfaceHolder.lockCanvas(null),UI线程(用户交互)和surfaceview线程没有交互。我认为这些线程的逻辑是同步的。但是,当我使用用户触摸事件和surfaceview线程更新逻辑时,它们似乎在某些情况下同时运行。 请参阅以下日志,更新后应该ACTION_MOVE结束。

由于这个问题,无法从UI线程安全地更新游戏变量(基于用户难事),然后在表面视图上更新画布。如何解决这个问题?是否可以锁定/同步游戏变量,以便我们可以在两个线程之间安全地访问它们?

 D/myApp﹕ ACTION_MOVE start
 D/myApp﹕ ACTION_MOVE
 D/myApp﹕ ACTION_MOVE end
 D/myApp﹕ update
 **D/myApp﹕ ACTION_MOVE start
 D/myApp﹕ ACTION_MOVE
 D/myApp﹕ update
 D/myApp﹕ ACTION_MOVE end**
 D/myApp﹕ ACTION_MOVE start
 D/myApp﹕ ACTION_MOVE
 D/myApp﹕ ACTION_MOVE end
 D/myApp﹕ update
 D/myApp﹕ ACTION_MOVE start
 D/myApp﹕ ACTION_MOVE
 D/myApp﹕ ACTION_MOVE end
 D/myApp﹕ update
 D/myApp﹕ ACTION_MOVE start
 D/myApp﹕ ACTION_MOVE
 D/myApp﹕ ACTION_MOVE end
 D/myApp﹕ update
 D/myApp﹕ update
 D/myApp﹕ ACTION_MOVE start
 D/myApp﹕ ACTION_MOVE
 D/myApp﹕ ACTION_MOVE end
 D/myApp﹕ ACTION_MOVE start
 D/myApp﹕ ACTION_MOVE
 D/myApp﹕ ACTION_MOVE end
 D/myApp﹕ update




public class GameThread extends Thread {

    private SurfaceHolder surfaceHolder;
    private GameViewSurface surface;
    private boolean running = false;
    private Context GameContext;
    private int gameState;
    public static final int STATE_PAUSE = 1;
    public static final int STATE_RUNNING = 2;

    private int GameSurfaceWidth = 1;
    private int GameSurfaceHeight = 1;

    public GameThread(SurfaceHolder holder, Context context, GameViewSurface GameSurface) {
        surfaceHolder = holder;
        surface = GameSurface;
        GameContext = context;
    }

    public void setRunning(boolean run) {
        running = run;
    }

    public void startGame() {
        synchronized (surfaceHolder) {
            setState(STATE_RUNNING);
        }
    }

    public void pause() {
        synchronized (surfaceHolder) {
            if (gameState == STATE_RUNNING) setState(STATE_PAUSE);
        }
    }

    public synchronized void restoreState(Bundle savedState) {
        synchronized (surfaceHolder) {
            setState(STATE_PAUSE);
        }
    }

    public void setState(int stateToSet) {
        synchronized (surfaceHolder) {
            // TODO Message Handling
        }
    }

    public Bundle saveState(Bundle map) {
        synchronized (surfaceHolder) {
            if (map != null) {

            }
        }
        return map;
    }

    public void setSurfaceSize(int width, int height) {
        // synchronized to make sure these all change atomically
        synchronized (surfaceHolder) {
            GameSurfaceWidth = width;
            GameSurfaceHeight = height;
        }
    }

    public void unpause() {
        setState(STATE_RUNNING);
    }

    @Override
    public void run() {
        while (running) {
            Canvas canvas=null;
            try {

                if(!surfaceHolder.getSurface().isValid())
                    continue;

                surface.update();

                canvas = surfaceHolder.lockCanvas(null);
                synchronized (surfaceHolder) {

                    surface.doDraw(canvas);
                }
            } catch(Exception p){

                Log.d("myApp", p.getMessage());

            }finally {
                if (canvas != null) {
                    try {
                        surfaceHolder.unlockCanvasAndPost(canvas);
                    }catch (Exception e){}
                }
            }
        }
    }
}



public class GameViewSurface extends SurfaceView implements SurfaceHolder.Callback {


    public GameViewSurface(Context context) {
        super(context);

        getHolder().addCallback(this);
        gameThread = new GameThread(getHolder(), context, this);

        setFocusable(true);
    }

    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        if (!hasWindowFocus) gameThread.pause();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
       // gameThread.setSurfaceSize(width, height);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

        gameThread.setRunning(true);
        gameThread.start();
        createGameHandler();
    }

    public void createGameHandler()
    {
        gameData = new Game(backColor);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        boolean retry = true;
        gameThread.setRunning(false);
        while (retry) {
            try {
                gameThread.join();
                retry = false;
            } catch (InterruptedException e) {
            }
        }
    }


    //@Override
    public void doDraw(Canvas canvas) {

            canvas.drawBitmap(gameDataGen.board, mPosX, mPosY, null);


    }

    public void playSound() {
        if (enableSound && !sp.isPlaying())
            sp.start();
    }

    public void update()
    {
        try {

            if (waitTillLoad || isExit || finished)
                return;
            if (gameData != null) {
                gameData.update();

            }
        }catch (Exception ex)
        {

        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        viewHeight = h;
        viewWidth = w;
    }




    @Override
    public boolean onTouchEvent(MotionEvent event) {

        if(waitTillLoad ||finished || !isTouchable || bypassParent)
           return super.onTouchEvent(event);

        scaleDetector.onTouchEvent(event);
        boolean handled = false;
        int xTouch;
        int yTouch;
        int actionIndex = event.getActionIndex();

        // get touch event coordinates and make transparent circle from it
        switch (event.getActionMasked()) {

            case MotionEvent.ACTION_DOWN:
                Log.d("myApp", "ACTION_DOWN start");
                xTouch = (int) event.getX(0);
                yTouch = (int) event.getY(0);

                if (scaleDetector.isInProgress()) {
                    break;
                 }

                 mLastTouchX = xTouch;
                 mLastTouchY = yTouch;

                this.selectedIndex = findClusterFromXY(xTouch, yTouch);
                if (selectedIndex >= 0) {

                    Log.d("myApp", "ACTION_DOWN selectedIndex >");

                    gameData.setTopIndex(this.selectedIndex);
                    gameData.clearMotionData();
                    gameData.addMotionEvent(new Point(xTouch, yTouch));

                }
                handled = true;
                Log.d("myApp", "ACTION_DOWN end");
                break;


            case MotionEvent.ACTION_MOVE:

                xTouch = (int) event.getX();
                yTouch = (int) event.getY();
                gameData.moveStart = true;
                gameData.moveEnd = false;



                    gameData.setMovingClusterData(gameData.currentCluster);
                    gameData.addMotionEvent(new Point(xTouch, yTouch));
                    _previousMouseX = xTouch;
                    _previousMouseY = yTouch;


                Log.d("myApp", "ACTION_MOVE end");
                gameData.moveEnd = true;
                break;

            case MotionEvent.ACTION_UP:
                Log.d("myApp", "ACTION_UP start");
                handled = processActionUp(event, true);

                handled = true;
                Log.d("myApp", "ACTION_UP end");
                break;

        }

        return super.onTouchEvent(event);
    }


    protected void onComplete() {
        if(solveFragment != null)
        {
            finished= true;
            solveFragment.onComplete();
        }
    }

    public GameThread getThread() {
        return gameThread;
    }


}

1 个答案:

答案 0 :(得分:0)

以下页面中列出的方法为我工作。

http://www.rbgrn.net/content/342-using-input-pipelines-your-android-game

使用正确锁定的管道(队列)来存储当前用户事件。然后在Surfaceview线程执行时逐个处理此列表中的项目。