9

我有一个 SurfaceView 扩展,它的基本原理是在Lunar Lander示例中实现的。也就是run()画图的方法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当表面被破坏时正确停止:

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) {
        }
    }
}

在迄今为止我通常测试过的设备上(HTC Desire、Desire HD 和 Archos 101,如果我没记错的话,它们之间有 OS 2.2 和 2.3.3)上面从来没有出现过问题。也就是说,当由于用户退出而破坏了表面时,Activity或者Activity在顶部调用了另一个,其中的代码surfaceDestroyed()始终确保mSurfaceHolder.lockCanvas()永远不会调用它来返回null

然而,我在运行 Android 4 / ICS 的新 HTC One X 上发现的不同之处在于,在调用方法期间surfaceDestroyed()(即该方法中的代码仍在执行),我的绘图Thread将获得一个null来自mSurfaceHolder.lockCanvas(). 这当然会导致应用程序崩溃。在我的 One X 上,每次表面被破坏时都会发生这种情况——无论是由于旋转手机、退出手机Activity等等。

我对此感到困惑,因为我的印象是mSurfaceHolder.lockCanvas()应该返回一个非null Canvas直到surfaceDestroyed()实际退出。确实,这就是 Javadoc 所说的:

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.

我现在的解决方案是只检查null. 这工作正常:

if(c != null){
    doDraw(c); // Main drawing method - not included in this code snippet
}

但是,任何想法为什么我突然不得不为 Android 4 / ICS 这样做?

4

2 回答 2

5

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

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

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

安迪

于 2013-02-12T09:57:39.157 回答
0

我做了一个小实验。我在这样的方法中放置了一些通知标志surfaceDestroyed()

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

我发现的是,nullPionterExceptionOccurs发生在第一个标志之后,但在第二个标志之前。对于您的问题,最可能的答案是您在可以访问旧视图时锁定画布,您在其上进行绘制,但与此同时屏幕会发生变化。因此,当解锁画布时,没有地方显示结果 - 导致错误。

PS 从示例 android 代码中玩一下 Lunar Lander,并在游戏运行时尝试旋转屏幕。当错误发生时,只需看看背景位图是如何绘制的。您会发现,屏幕方向发生了变化,但程序试图绘制位图,就好像什么都没发生一样。

于 2013-03-06T12:44:39.520 回答