2010-11-20 47 views
3

我有一個菜單活動和從菜單啓動的遊戲活動。 我發起遊戲活動的一些(大部分)時間;所有輸入掛起幾(最多可達10個ISH)秒,然後在hyperspeed發揮出來,而我得到這個在logcat中:Android; 「關鍵調度超時...」

11-20 18:24:27.873: WARN/WindowManager(2473): Key dispatching timed out sending to southgrove.game/southgrove.game.Game 
11-20 18:24:27.873: WARN/WindowManager(2473): Previous dispatch state: {{KeyEvent{action=1 code=4 repeat=0 meta=0 scancode=28 mFlags=8} to Window{4866c7a0 southgrove.game/southgrove.game.Game paused=false} @ 1290273811209 lw=Window{4866c7a0 southgrove.game/southgrove.game.Game paused=false} [email protected] fin=false gfw=true ed=true tts=0 wf=false fp=false mcf=Window{4866c7a0 southgrove.game/southgrove.game.Game paused=false}}} 
11-20 18:24:27.873: WARN/WindowManager(2473): Current dispatch state: {{null to Window{4833d500 southgrove.game/southgrove.game.Game paused=false} @ 1290273867876 lw=Window{4833d500 southgrove.game/southgrove.game.Game paused=false} [email protected] fin=false gfw=true ed=true tts=0 wf=false fp=false mcf=Window{4833d500 southgrove.game/southgrove.game.Game paused=false}}} 

菜單活動:

package southgrove.game; 

import southgrove.game.R; 
import android.app.Activity; 
import android.app.AlertDialog; 
import android.app.Dialog; 
import android.content.DialogInterface; 
import android.content.Intent; 
import android.os.Bundle; 
import android.view.KeyEvent; 
import android.view.View; 
import android.view.View.OnClickListener; 

public class Menu extends Activity 
{ 

@Override 
public void onCreate(Bundle savedInstanceState) 
{ 
    super.onCreate(savedInstanceState); 

    setContentView(R.layout.menu); 

    View playButton = findViewById(R.id.play); 
    playButton.setOnClickListener(new OnClickListener() 
    { 
     public void onClick(View view) 
     { 
      startActivityForResult(new Intent(Menu.this, Game.class), 0); 
     } 
    }); 

    View testButton = findViewById(R.id.test); 
    testButton.setOnClickListener(new OnClickListener() 
    { 
     public void onClick(View view) 
     { 
      startActivityForResult(new Intent(Menu.this, Test.class), 0); 
     } 
    }); 

    View closeButton = findViewById(R.id.close); 
    closeButton.setOnClickListener(new OnClickListener() 
    { 
     public void onClick(View view) 
     { 
      showDialog(QUIT_DIALOG); 
     } 
    }); 

} 

@Override 
public boolean onKeyDown(int keyCode, KeyEvent event) 
{ 
    if (keyCode == KeyEvent.KEYCODE_BACK) 
    { 
     showDialog(QUIT_DIALOG); 
    } 

    return super.onKeyDown(keyCode, event); 
} 

@Override 
protected Dialog onCreateDialog(int id) 
{ 
    Dialog dialog; 

    switch (id) 
    { 
     case QUIT_DIALOG: 
      AlertDialog.Builder quitDialogBuilder = new AlertDialog.Builder(this); 
      quitDialogBuilder.setMessage("Exit the game?") 
        .setCancelable(false) 
        .setPositiveButton("Yes", new DialogInterface.OnClickListener() 
        { 
         public void onClick(DialogInterface dialog, int id) 
         { 
          Menu.this.finish(); 
         } 
        }) 
        .setNegativeButton("No", new DialogInterface.OnClickListener() 
        { 
         public void onClick(DialogInterface dialog, int id) 
         { 
          dialog.cancel(); 
         } 
        }); 
      dialog = quitDialogBuilder.create(); 
     break; 

     default: 
      dialog = null; 
    } 

    return dialog; 
} 

private final int QUIT_DIALOG = 0; 

} 

的遊戲活動:

package southgrove.game; 

import southgrove.droidgl.DroidGL; 
import southgrove.droidgl.core.Camera; 
import southgrove.droidgl.core.Node; 
import southgrove.droidgl.core.RootNode; 
import southgrove.game.R; 
import southgrove.game.board.BoardBase; 
import southgrove.game.board.core.*; 
import southgrove.game.cameras.StupidCamera; 
import southgrove.input.OnTouchFilter; 
import android.app.Activity; 
import android.app.AlertDialog; 
import android.app.Dialog; 
import android.content.Context; 
import android.content.DialogInterface; 
import android.os.Bundle; 
import android.os.Handler; 
import android.os.Message; 
import android.os.Vibrator; 
import android.widget.TextView; 

public class Game extends Activity 
{ 

@Override 
public void onCreate(Bundle savedInstanceState) 
{ 
    super.onCreate(savedInstanceState); 

    setContentView(R.layout.game); 

    // Get fpsTextView reference 
    fpsTextView = (TextView) findViewById(R.id.fpsTextView); 

    // Build meshes 
    TetrominoMesh.buildMeshes(); 

    // Setup the DroidGL surface 
    droidgl = (DroidGL) findViewById(R.id.droidGL); 
    droidgl.setLongClickable(true); 
    droidgl.setOnTouchListener(new GameSurfaceOnTouchFilter(false)); 

    // Create and add camera 
    final Camera camera = new StupidCamera(); 
    camera.move(0, 0, 14); 
    droidgl.registerCamera(camera); 
    DroidGL.setActiveCamera(camera); 

    // Create and add root node 
    final Node rootNode = new RootNode(); 
    droidgl.setRootNode(rootNode); 

    // Create and add game board 
    gameBoard = new GameBoard(droidgl, 32, 32, 8); 
    rootNode.addChild(gameBoard); 

    // start up updateHandler 
    updateHandler = new UpdateHandler(); 
    updateHandler.sleep(1); 

    // get vibrator service 
    vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); 
} 

@Override 
public void onBackPressed() 
{ 
    showDialog(QUIT_DIALOG); 
} 

@Override 
protected Dialog onCreateDialog(int id) 
{ 
    Dialog dialog; 

    switch (id) 
    { 
     case QUIT_DIALOG: 
      AlertDialog.Builder quitDialogBuilder = new AlertDialog.Builder(this); 
      quitDialogBuilder.setMessage("Really quit?") 
        .setCancelable(false) 
        .setPositiveButton("Yup!", new DialogInterface.OnClickListener() 
        { 
         public void onClick(DialogInterface dialog, int id) 
         { 
          Game.this.finish(); 
         } 
        }) 
        .setNegativeButton("Nope!", new DialogInterface.OnClickListener() 
        { 
         public void onClick(DialogInterface dialog, int id) 
         { 
          dialog.cancel(); 
         } 
        }); 
      dialog = quitDialogBuilder.create(); 
     break; 

     default: 
      dialog = null; 
    } 

    return dialog; 
} 

@Override 
protected void onPause() 
{ 
    super.onPause(); 
    droidgl.onPause(); 
} 

protected void onUpdate() 
{ 
    fpsTextView.setText("fps: " + String.valueOf(droidgl.getFps())); 
    updateHandler.sleep(500); 
} 

@Override 
protected void onResume() 
{ 
    super.onResume(); 
    droidgl.onResume(); 
} 

private DroidGL   droidgl; 
private GameBoard  gameBoard; 
private TextView  fpsTextView; 
private Vibrator  vibrator; 

private UpdateHandler updateHandler; 

private final int  QUIT_DIALOG = 0; 

private class UpdateHandler extends Handler 
{ 
    @Override 
    public void handleMessage(Message msg) 
    { 
     Game.this.onUpdate(); 
    } 

    public void sleep(long delayMillis) 
    { 
     this.removeMessages(0); 
     if (!Game.this.isFinishing()) 
      sendMessageDelayed(obtainMessage(0), delayMillis); 
    } 
} 

private class GameSurfaceOnTouchFilter extends OnTouchFilter 
{ 
    public GameSurfaceOnTouchFilter(Boolean consumeEvent) 
    { 
     super(consumeEvent); 
    } 

    private float flipDeltaX; 
    private float flipDeltaY; 

    protected void doubleTap(float x, float y) 
    { 
     super.doubleTap(x, y); 

     synchronized (gameBoard) 
     { 
      if (gameBoard.dropCursorTetromino()) 
      { 
       gameBoard.resetReactorTimer(); 
       gameBoard.setCursorTetromino((int) (Tetromino.NUM_TYPES * Math.random() - 1), 0); 
       if (gameBoard.removeConnectedTetrominoes(3)) 
       { 
        gameBoard.startShockWave(0, 0, 10f, 0.35f); 
        vibrator.vibrate(300); 
       } else 
       { 
        vibrator.vibrate(50); 
       } 
      } 
     } 
    } 

    protected void down(int pointer, float x, float y) 
    { 
     super.down(pointer, x, y); 

     if (pointer == 0) 
     { 
     } 

     if (pointer == 1) 
     { 
      flipDeltaX = 0; 
      flipDeltaY = 0; 
     } 
    } 

    protected void up(int pointer, float x, float y) 
    { 
     super.up(pointer, x, y); 

     synchronized (gameBoard) 
     { 
     } 
    } 

    protected void move(int pointer, float x, float y, float dx, float dy) 
    { 
     super.move(pointer, x, y, dx, dy); 

     synchronized (gameBoard) 
     { 
      if (pointer == 0) 
      { 
       gameBoard.addInertia(-dx * 0.0045f, dy * 0.0045f); 
      } 

      if (pointer == 1) 
      { 
       flipDeltaX -= dx; 
       flipDeltaY += dy; 

       if (Math.abs(flipDeltaX) > 45 || Math.abs(flipDeltaY) > 45) 
       { 
        vibrator.vibrate(50); 

        if (Math.abs(flipDeltaX) > Math.abs(flipDeltaY)) 
        { 
         if (flipDeltaX > 0) 
         { 
          gameBoard.rotateCursorTetromino(1); 
         } else 
         { 
          gameBoard.rotateCursorTetromino(-1); 
         } 
        } else 
        { 
         if (flipDeltaY > 0) 
         { 
          gameBoard.rotateCursorTetromino(1); 
         } else 
         { 
          gameBoard.rotateCursorTetromino(-1); 
         } 
        } 
        flipDeltaX = 0; 
        flipDeltaY = 0; 
       } 
      } 
     } 
    } 

} 

private class GameBoard extends BoardBase 
{ 

    public GameBoard(DroidGL droidGL, int width, int height, int depth) 
    { 
     super(droidGL, width, height, depth); 
    } 

    public void resetReactorTimer() 
    { 
     synchronized (this) 
     { 
      reactorTimer = 0; 
     } 
    } 

    public void startShockWave(int gridPosX, int gridPosY, float length, float magnitude) 
    { 
     synchronized (this) 
     { 
      for (int i = 0; i < tetrominoes.size(); i++) 
      { 
       tetrominoes.get(i).bounce(
         (float) (Math.random() * 0.12f), 
         (float) (Math.random() * magnitude) 
         ); 
      } 
     } 
    } 

    @Override 
    protected void onSetup() 
    { 
     synchronized (this) 
     { 
      setCursorTetromino((int) (Tetromino.NUM_TYPES * Math.random()), 0); 

      for (int i = 0; i < 1024; i++) 
      { 
       addTetromino(
         (int) (Math.random() * Tetromino.NUM_TYPES), 
         (int) (Math.random() * width/2) * 2, 
         (int) (Math.random() * height/2) * 2, 
         (int) (Math.random() * 2), 
         (int) (Math.random() * 4)); 
      } 

      removeConnectedTetrominoes(3); 
     } 
    } 

    @Override 
    protected void onLogic(float timeFactor) 
    { 
     synchronized (this) 
     { 
      if (shiftTimer > shiftTime || tetrominoes.size() < 15) 
      { 
       shiftTimer = 0; 
       shiftTime -= shiftTime/5; 

       shiftUp(); 

       for (int i = 0; i < 256; i++) 
       { 
        addTetromino(
          (int) (Math.random() * Tetromino.NUM_TYPES), 
          (int) (Math.random() * width/2) * 2, 
          (int) (Math.random() * height/2) * 2, 
          0,// (int) (Math.random() * (depth - 1)), 
          (int) (Math.random() * 4)); 
       } 
      } 

      if (reactorTimer > 1f) 
      { 
       reactorTimer = 0; 

       drop(); 
       removeConnectedTetrominoes(3); 
      } 

      reactorTimer += timeFactor; 
      shiftTimer += timeFactor; 
     } 
    } 

    private float shiftTime = 60 * 5; 
    private float reactorTimer; 
    private float shiftTimer; 
} 

} 

什麼可能導致這種情況?任何想法/推測都是受歡迎的。是的,我知道這是一段非常龐大的代碼,用於篩選。

+0

愚蠢的問題:你是否在調試模式下啓動,因爲我注意到,在Android中,它有時可能極其緩慢? – Sephy 2010-11-20 20:48:35

回答

7

「Key dispatch timed out」的一個常見原因是我經常經歷的,直到深入挖掘,在調試器中保留了UI線程(處理UI事件)超過很短的時間(詳情如下)。例如,如果你想在事件處理程序中調試代碼,這是潛在的問題。

例如,如果你的onTouchEvent設置斷點()的活動

class MyActivity extends Activity 
{ 
    public boolean onTouchEvent(MotionEvent me) 
    { 
     // ** Breakpoint ** 
     // Code you wish to debug 
    } 
} 

的......你堅持到這個線程(UI):

播放5秒後你會得到這樣的警告: 重點調度超時發送到com.hos/com.hos.MyActivity ...空到窗口...

超過20秒,你會得到: 重點調度超時發送到com.hos/COM。 hos.MyActivity ...空到窗口... 繼續等待關鍵出動

35秒後,您將獲得: 重點調度超時發送到com.hos/com.hos.MyActivity ...空到窗口... 超時過期的下一個密鑰&找到新的目標

在這一點上,不僅應用程序凍結,而且手機也是如此。 很多時候我需要等待ANR,有時候很難重啓手機。

所以一個簡單的答案就是不要拘泥於UI線程,無論是用調試器還是用時間昂貴的代碼。

=============

關於同步,這是一個非常類似的問題。在這個例子中,onTouchEvent()可能不得不等待非線程安全的共享資源。在這種情況下,如果觸摸事件期間人口正在發生,它可能會超時。

class MyActivity extends Activity 
{ 
    private static ArrayList<Object> m_alShared = new ArrayList<Object>(); 

    public boolean onTouchEvent(MotionEvent me) 
    { 
     synchronized(this) 
     { 
      // accessed shared resource. 
      m_alShared.get(?); 
     } 
    } 

    public void methodCalledByBackgroundThread() 
    { 
     synchronized(this) 
     { 
      // populate shared resource for more than 35 seconds 
      while (/* time < 35 seconds */) 
       m_alShared.add(?); 
     } 
    } 
} 

就我個人而言,我選擇不在UI線程上同步或使用任何「等待」函數。或者如果你需要,確保它很快。這是一個等待發生的競賽條件。特別是如果它不僅影響你的應用程序,還影響你的手機。

即我可能會選擇以下解決方案並同步每個添加。

public void methodCalledByBackgroundThread() 
    { 
     while (/* time < 35 seconds */) 
     { 
      synchronized(this) 
      { 
       // populate shared resource for more than 35 seconds 
       m_alShared.add(?); 
      } 
     } 
    }