2016-08-19 49 views
0

我有一個線程在畫布上更新/繪製表面視圖。我認爲由於surfaceHolder.lockCanvas(null),UI線程(用戶交互)和surfaceview線程沒有交互。我認爲這些線程的邏輯是同步的。但是當我迷戀用戶觸摸事件和表面查看線程更新邏輯時,他們似乎在某些情況下同時運行。 請參閱以下日誌,應該在更新後發生ACTION_MOVE結束。Surfaceview和UI線程交互和鎖定遊戲變量

由於這個問題,沒有辦法從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; 
    } 


} 
+0

很難說沒有你的代碼的例子......也許你需要重新考慮你的設計。 UI線程應該直接更新變量嗎?也許你可以使用事件隊列? –

+0

感謝您的評論。我認爲你的意思是有一個列表來收集所有事件並按順序在線程邏輯中處理它。但這也是一個變數,我們如何鎖定它,否則我們會得到同樣的問題。 我已經添加了代碼。 GameViewSurface具有update和doDraw,它在Game線程中執行。 DoDraw使用用戶觸摸向下移動和來自UI線程的手勢在畫布上繪製。 – pats

+0

您沒有添加任何代碼:-) –

回答