应用程序在Android 4.0上崩溃,但不是2.3 – 奇怪的exception

我正在开发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(); } } 

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

我看到的一个问题是在调用“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); } } } 

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

您永远不应该对UI线程进行锁定,因为它可以阻止UI线程并阻止用户与您的应用程序平滑交互。 lockCanvas的文档建议与单独的绘图线程同步:

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

Android 3.0+引入了StrictMode检查以确保开发人员不会滥用UI线程,因此您的应用程序在Android 2.3上运行并且在Android 4.0上运行并不奇怪。