安卓; “密钥发送超时……”

2024-02-15

我有一个菜单活动和一个从菜单启动的游戏活动。 有些(大多数)时间我启动游戏活动;所有输入都会挂起几秒(最多 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;
}

}

可能是什么原因造成的?欢迎任何想法/猜测。是的,我知道这是一堵相当大的代码墙需要筛选。


“按键调度超时”的一个常见原因是在调试器中占用 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 的密钥调度超时 ... 窗口为空 ...

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(?);
            }
        }
    }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

安卓; “密钥发送超时……” 的相关文章

随机推荐