我有菜单活动和从菜单启动的游戏活动。 我发起游戏活动的一些(大多数时间);所有输入都挂起几个(最多10个小时)秒然后在超速速度下播放,而我在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}}}
菜单活动:
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 :(得分:7)
“密钥调度超时”的一个常见原因是我经常在深入挖掘之前经历过,在调试器中持有UI线程(处理UI事件)的时间超过了很短的时间(详情如下)。例如,如果要在事件处理程序中调试代码,这可能是一个问题。
例如,如果在Activity的onTouchEvent()中设置断点
class MyActivity extends Activity
{
public boolean onTouchEvent(MotionEvent me)
{
// ** Breakpoint **
// Code you wish to debug
}
}
...你坚持这个主题(UI):
5秒后你会收到这个警告: 密钥发送超时发送到com.hos / com.hos.MyActivity ... null到Window ...
20秒后你会得到: 密钥发送超时发送到com.hos / com.hos.MyActivity ... null到Window ... 继续等待发送密钥
35秒后你会得到: 密钥发送超时发送到com.hos / com.hos.MyActivity ... null到Window ... 超时过期的过程下一个Key&amp;找到新目标
此时,不仅应用程序被冻结,手机也被冻结。 我经常需要等待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(?);
}
}
}