Androide; Msgstr "Se ha agotado el tiempo de envío de claves …"

Tengo una actividad de menú y una actividad de juego que se inicia desde el menú. Algunas (la mayoría) de las veces que lanzo la actividad del juego; toda la entrada cuelga por unos pocos (hasta 10 ish) segundos y luego se reproduce en hyperspeed mientras que obtener esto en 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}}} 

La actividad del menú:

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

La actividad del juego:

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

¿Qué podría estar causando esto? Cualquier ideas / especulaciones son bienvenidas. Y sí, sé que es un muro bastante grande de código para filtrar.

Una causa común de la "Tiempo de envío de clave expirado", que experimenté con bastante frecuencia hasta excavar un poco más profundo, se mantiene en el subproceso de interfaz de usuario – que controla eventos de interfaz de usuario – en el depurador durante más de un corto período de tiempo (detalles a continuación) . Por ejemplo, si desea depurar código en su controlador de eventos, esto es un problema potencial.

Por ejemplo, si establece un punto de interrupción en onTouchEvent () de una actividad

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

… y se aferran a este hilo (UI):

Después de 5 segundos obtendrá esta advertencia: El envío de la clave se ha agotado enviando a com.hos / com.hos.MyActivity … null to Window …

Después de 20 segundos obtendrás: Despacho de teclas expirado enviando a com.hos / com.hos.MyActivity … null a Window … Continuando esperando que la clave sea despachada

Después de 35 segundos obtendrás: Despacho de teclas expirado enviando a com.hos / com.hos.MyActivity … null a Window … expiró el proceso caducado siguiente Key & find new target

En este punto, no sólo es la aplicación congelada, pero también lo es el teléfono. Muy a menudo tengo que esperar a la ANR ya veces duro reiniciar el teléfono.

Así que una respuesta simple es no mantener el hilo de la interfaz de usuario, ya sea con el depurador o con código de tiempo caro.

Unesdoc.unesco.org unesdoc.unesco.org

En cuanto a la sincronización, este es un problema muy similar. En este ejemplo, un onTouchEvent () puede tener que esperar a la población de un recurso compartido no seguro de subprocesos. En este caso, puede demorar si la población está ocurriendo durante el evento táctil.

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

Personalmente, elijo no sincronizar o utilizar cualquier función de "espera" en el subproceso de interfaz de usuario. O si es necesario, asegúrese de que sea rápido. Es una condición de carrera esperando a suceder. Especialmente si afecta no solo a tu aplicación, sino también a tu teléfono.

es decir, podría optar por la siguiente solución y sincronizar cada agregar.

  public void methodCalledByBackgroundThread() { while (/* time < 35 seconds */) { synchronized(this) { // populate shared resource for more than 35 seconds m_alShared.add(?); } } } 
  • Cómo obtener el tiempo de computación en NDK
  • Glide image loading timeout increase
  • Notificación de Android Push (lento)
  • Acelerar el tiempo del emulador de Android
  • ¿Es posible ver variables en tiempo de ejecución?
  • Android: ¿Activar / desactivar un botón en Runtime?
  • ¿Cómo obtener la hora actual de Google para Android?
  • Problema extraño con los tiempos de espera de conexión y Google Volley
  • No se puede cargar Android Device Monitor
  • ¿Cómo se puede comprobar el permiso en tiempo de ejecución sin lanzar SecurityException?
  • ¿Cómo puedo obtener el tiempo de "red" (desde el ajuste "Automático" llamado "Usar valores proporcionados por la red"), NO el tiempo en el teléfono?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.