1

我正在 2.3 上开发一个 android 游戏,并已开始在更多设备上进行测试,以便我可以发布它。

在某些(并非所有)4.0 设备上,我的游戏在最小化时会崩溃。您可以锁定手机并解锁它,游戏恢复和暂停都很好。当您尝试返回主屏幕时会发生崩溃。

错误日志如下所示:

07-18 14:33:44.839 E/AndroidRuntime(15542)FATAL EXCEPTION: Thread-662
07-18 14:33:44.839 E/AndroidRuntime(15542)java.lang.NullPointerException
07-18 14:33:44.839 E/AndroidRuntime(15542)at com.petronicarts.stormthecastle.MainThread.run(MainThread.java:55)
07-18 14:33:44.846 W/IInputConnectionWrapper(15542)showStatusIcon on inactive InputConnection
07-18 14:33:45.081 I/ActivityManager(178)No longer want com.android.packageinstaller (pid 15351): hidden #16
07-18 14:33:45.143 W/InputDispatcher(178)channel '41bb4d28 com.android.packageinstaller/com.android.packageinstaller.InstallAppProgress (server)' ~ Consumer closed input channel or an error occurred.  events=0x8
07-18 14:33:45.143 E/InputDispatcher(178)channel '41bb4d28 com.android.packageinstaller/com.android.packageinstaller.InstallAppProgress (server)' ~ Channel is unrecoverably broken and will be disposed!
07-18 14:33:45.190 W/InputDispatcher(178)Attempted to unregister already unregistered input channel '41bb4d28 com.android.packageinstaller/com.android.packageinstaller.InstallAppProgress (server)'
07-18 14:33:45.190 I/WindowManager(178)WIN DEATH: Window{41bb4d28 com.android.packageinstaller/com.android.packageinstaller.InstallAppProgress paused=false}
07-18 14:33:45.198 I/WindowManager(178)WINDOW DIED Window{41bb4d28 com.android.packageinstaller/com.android.packageinstaller.InstallAppProgress paused=false}
07-18 14:33:47.268 D/dalvikvm(178)GC_EXPLICIT freed 338K, 20% free 22816K/28487K, paused 6ms+5ms
07-18 14:34:15.464 I/power   (178)*** set_screen_state 0
07-18 14:34:15.471 D/SurfaceFlinger(115)About to give-up screen, flinger = 0x1822918
07-18 14:34:15.596 D/NfcService(403)NFC-C OFF, disconnect

我相信重要的一行是:

com.petronicarts.stormthecastle.MainThread.run(MainThread.java:55)

查看第 55 行,它是这样的:

canvas.setMatrix(matrix);

在我的运行线程中:

    @Override
public void run() 
{
    boolean ScaleGame = true;
    //boolean SkipFrame = false;
    Bitmap screen = Bitmap.createBitmap(960, 540, Config.RGB_565);
    Canvas canvas;
    Canvas canvas2 = new Canvas(screen);
    Paint paint = new Paint();
    Matrix matrix = new Matrix();
    matrix.preScale(gamePanel.getScaleX(), gamePanel.getScaleY());
    if (gamePanel.getScaleX() == 1 && gamePanel.getScaleY() == 1)
        ScaleGame = false;
    long startTime, elapsedTime;
    startTime = System.currentTimeMillis();
    elapsedTime = System.currentTimeMillis() - startTime;

    this.gamePanel.setScreenBitmap(screen);

    while (running) {
        if(!pleaseWait) {
            canvas = null;
            // try locking the canvas for exclusive pixel editing on the surface
            try {
                canvas = this.surfaceHolder.lockCanvas();
                if (ScaleGame)
                    canvas.setMatrix(matrix);
                synchronized (surfaceHolder) {
                    startTime = System.currentTimeMillis();
                    this.gamePanel.update((float)elapsedTime);

                    canvas.drawBitmap(screen, 0, 0, paint);
                    elapsedTime = System.currentTimeMillis() - startTime;

                }
            } finally {
                if (canvas != null) {
                    surfaceHolder.unlockCanvasAndPost(canvas);
                }
            }            
        }
        else {
            synchronized (this) {
                try {
                    wait();
                } catch (Exception e) { }
            }
        }
    }
}

我不知道为什么我无法设置矩阵。看起来代码应该可以正常工作。我认为这意味着我正在以仅适用于 2.3 的方式处理某些事情。

我的暂停、恢复和销毁事件如下所示:

public void pause() {
    justPause = true;
    pauseGame = true;


    SharedPreferences fileStore = this.getContext().getSharedPreferences("userData", 0);
    SharedPreferences.Editor editor = fileStore.edit();
    editor.putInt("highscore", highscore);
    editor.commit();

    AudioService.StopMusic();

}

public void resume(Context context) {
    if (gameState == 1)
        AudioService.StartMusic();
    //gold += 1000;
}

public void destroy() {
    thread.setRunning(false);


    if (thread != null)
    {
        Thread killThread = thread;
        thread = null;
        killThread.interrupt();
    }   
}

如果您有任何想法,他们将非常欢迎。谢谢。

4

2 回答 2

2

我看到的一个问题是在调用“canvas = this.surfaceHolder.lockCanvas();”之后 您只在 finally 块中检查 null 。我会将其更改为如下所示:

if(!pleaseWait) {
    canvas = this.surfaceHolder.lockCanvas();

    if (canvas != null) {
        // try locking the canvas for exclusive pixel editing on the surface
        try {
            if (ScaleGame)
                canvas.setMatrix(matrix);
            synchronized (surfaceHolder) {
                startTime = System.currentTimeMillis();
                this.gamePanel.update((float)elapsedTime);
                canvas.drawBitmap(screen, 0, 0, paint);
                elapsedTime = System.currentTimeMillis() - startTime;
        }
        finally {
            surfaceHolder.unlockCanvasAndPost(canvas);
        }            
    }
}

如果未设置画布矩阵(当 ScaleGame 为 false 时),我还会询问是否应该运行同步块。我对问题或 Canvas 没有足够的洞察力来回答这个问题。如果答案是否定的,那么同步块应该包含在 if 语句中。

于 2012-07-19T14:18:06.140 回答
1

您永远不应该锁定 UI 线程,因为它会阻塞 UI 线程并阻止用户与您的应用顺利交互。文档lockCanvas建议改为与单独的绘图线程同步:

If null is not returned, lockCanvas() internally holds a lock until the corresponding unlockCanvasAndPost(Canvas) call, preventing SurfaceView from creating, destroying, or modifying the surface while it is being drawn. This can be more convenient than accessing the Surface directly, as you do not need to do special synchronization with a drawing thread in Callback.surfaceDestroyed.

Android 3.0+ introduced StrictMode checks to ensure developers didn't abuse the UI thread, so it is no surprise that your app works on Android 2.3, but not on Android 4.0.

于 2012-07-19T14:54:05.590 回答