【问题标题】:App Crashes on Android 4.0, but not 2.3 -- Strange Exception应用程序在 Android 4.0 上崩溃,但不是 2.3 - 奇怪的异常
【发布时间】:2012-07-19 22:54:05
【问题描述】:

我正在开发一款 2.3 版的安卓游戏,并已开始在更多设备上进行测试,以便发布它。

在某些(并非所有)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();
    }   
}

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

【问题讨论】:

  • 显然,this.surfaceHolder.lockCanvas() 必须返回 null 前导 canvas.setMatrix(matrix) 以导致空指针异常。我会尝试谷歌surfaceHolder.lockCanvas 看看我是否能找到这个方法调用在 2.3 和 4.0 之间的任何差异。

标签: java android android-4.0-ice-cream-sandwich


【解决方案1】:

我看到的一个问题是在调用“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 语句中。

【讨论】:

    【解决方案2】:

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

    如果未返回 null,lockCanvas() 会在内部保持锁定,直到相应的 unlockCanvasAndPost(Canvas) 调用,防止 SurfaceView 在绘制表面时创建、销毁或修改表面。这比直接访问Surface 更方便,因为您不需要与Callback.surfaceDestroyed 中的绘图线程进行特殊同步。

    Android 3.0+ 引入了 StrictMode 检查以确保开发人员不会滥用 UI 线程,因此您的应用可以在 Android 2.3 上运行但在 Android 4.0 上运行也就不足为奇了。

    【讨论】:

    • 如何更改我的代码?我使用的逻辑是我找到的教程的直接变体。您能建议一种方法来修改上述线程并使其工作吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-17
    • 2011-04-20
    • 2011-08-23
    • 1970-01-01
    相关资源
    最近更新 更多