尽管在SurfaceDestroyed()中停止线程,但在SurfaceView Thread中为null Canvas - 仅在Android 4 / ICS上

时间:2012-04-28 14:23:29

标签: android surfaceview

我有一个SurfaceView扩展,其中的骨架实现如Lunar Lander示例中所示。也就是说,图run()的{​​{1}}方法基本上是:

Thread

当表面被破坏时public void run() { while (mRun) { Canvas c; try { c = mSurfaceHolder.lockCanvas(); synchronized (mSurfaceHolder) { doDraw(c); // Main drawing method - not included in this code snippet } } finally { // do this in a finally so that if an exception is thrown // during the above, we don't leave the Surface in an // inconsistent state if (c != null) { mSurfaceHolder.unlockCanvasAndPost(c); } } } } } 被正确停止:

Thread

在我迄今为止经常测试的设备上(HTC Desire,Desire HD和Archos 101,如果我没记错的话,它们之间有OS 2.2和2.3.3)以上从未出现过问题。也就是说,当表面被破坏是因为用户退出public void surfaceDestroyed(SurfaceHolder holder) { // we have to tell thread to shut down & wait for it to finish, or else // it might touch the Surface after we return and explode boolean retry = true; thread.setRunning(false); while (retry) { try { thread.join(); retry = false; } catch (InterruptedException e) { } } } 或另一个Activity时,Activity内的代码始终确保surfaceDestroyed()永远不会被要求它返回mSurfaceHolder.lockCanvas()

我在运行Android 4 / ICS的新HTC One X上发现的差异是,在调用方法null期间(即该方法中的代码仍在执行)我的绘图{ {1}}会从surfaceDestroyed()获得Thread画布。这当然会导致应用程序崩溃。在我的One X上,这将发生每次表面被摧毁 - 无论是由于旋转手机,还是退出null等等。

我对此感到困惑,因为我认为mSurfaceHolder.lockCanvas()应该返回非Activity mSurfaceHolder.lockCanvas(),直到null实际退出。实际上,这就是Javadoc所说的:

Canvas

我现在的解决方案是检查surfaceDestroyed()。这很好用:

This is called immediately before a surface is being destroyed. After returning from this call, you should no longer try to access this surface. If you have a rendering thread that directly accesses the surface, you must ensure that thread is no longer touching the Surface before returning from this function.

但是,为什么我突然不得不为Android 4 / ICS做这个?

2 个答案:

答案 0 :(得分:5)

为了详细说明我的评论,似乎有一个针对此行为更改的错误提单,您可以在此处找到:http://code.google.com/p/android/issues/detail?id=38658。如果它影响了你,它可能值得盯着它,只是因为它提升了它的重要性!

就个人而言,我自己也看过这个,只是使用了最新的Android SDK附带的月球着陆器示例。我把它放在我的HTC Sensation XE(4.0.3)上并且在方向改变时,我会在表面被破坏之前返回一些空画布。

所以我使用的解决方法是在将画布传递给我的更新和渲染方法之前仔细检查画布是否为空。

安迪

答案 1 :(得分:0)

我做了一个小实验。我在surfaceDestroyed()方法中添加了一些通知标志,如下所示:

public void surfaceDestroyed(SurfaceHolder holder) {
Log.i("i","i have started");
...[the code]...
Log.i("i","i have finished");
}

我发现的是nullPionterExceptionOccurs发生在第一个标志之后但在第二个标志之前。对于你的问题,最可能的anwser是你可以在旧视图可达时锁定画布,你在它上画,但同时屏幕会改变。因此,当解锁画布时,没有地方可以显示结果 - 导致错误。

P.S。 从示例安卓码开始玩Lunar Lander并尝试在游戏运行时旋转屏幕。当发生错误时,只需查看背景位图即将绘制的方式。您会发现,屏幕方向已更改,但程序尝试绘制位图,就像没有任何事情发生一样。