Android "Dispatching timeout ..."

I have a menu action and game activity that starts from the menu. Some (most) times when I start the game; all inputs freeze for a few (up to 10-ish) seconds and then play out in hyperspeed until I get this in logcat:

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} lb=android.os.BinderProxy@484e8a58 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} lb=android.os.BinderProxy@485487b0 fin=false gfw=true ed=true tts=0 wf=false fp=false mcf=Window{4833d500 southgrove.game/southgrove.game.Game paused=false}}} 

Menu Activity:

 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; } 

Game action:

 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; } } 

What could be the reason for this? Any ideas / specs are welcome. And yes, I know that a rather massive wall of code is sifted through.

+3
source share
1 answer

One of the common reasons for a β€œkey-sending timeout,” which I often experienced before digging a little deeper, is keeping the UI thread that handles UI events in the debugger for more than a short time (details below). For example, if you want to debug code in an event handler, this is a potential problem.

For example, if you set a breakpoint in onTouchEvent () for Activity

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

... and you hold onto this thread (UI):

After 5 seconds, you will receive the following warning: Sending a key send by email to com.hos / com.hos.MyActivity ... null for the window ...

After 20 seconds you will receive: Sending the key send by e-mail com.hos / com.hos.MyActivity ... null for the window ... Continuing to wait for the key to be sent

After 35 seconds you will receive: Sending a key send by e-mail com.hos / com.hos.MyActivity ... null for the window ... the timeout has expired for the key and search for a new target

At this moment, not only the application is frozen, but also the phone. Quite often, I need to wait for ANR, and sometimes restart the phone.

So, one simple answer is that this does not apply to the user interface thread, whether with a debugger or with an expensive time code.

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

Regarding synchronization, this is a very similar problem. In this example, onTouchEvent () might have to wait for the aggregate of an insecure share. In this case, it can wait time if the population occurs during the touch event.

 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(?); } } } 

Personally, I prefer not to sync or use any wait function in the user interface thread. Or, if you need to, make sure it is fast. This is a race condition awaiting its appearance. Especially if this affects not only your application, but also your phone.

i.e. I could choose the next solution and synchronize each add.

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

All Articles