API级别15的SurfaceView-Thread中出现意外的NullPointerException

时间:2012-05-12 12:02:47

标签: android multithreading surfaceview

我编写了一个应用程序,我想在更高的API级别上测试以检查兼容性。对于API 10(2.3.3)没有问题,但是当我在API 15(4.0.3)上运行我的应用程序时,当我退出Activity时,我的一个SurfaceViews中出现了NullPointerException。 我不得不说我解决了这个问题,但我无法弄清楚为什么实际发生了异常。所以也许你可以告诉我。

以下是在API 10上为我工作的代码: 这是run() - 方法的通用结构。

public void run() {
    while (mThreadActive) {
        c = null;
        try {
            c = mSurfaceHolder.lockCanvas(null);
            synchronized (mSurfaceHolder) {
                if(mState == 1) {
                    updateValues();
                    updateAnimation();
                }
                doDraw(c);
            }
        } finally {
            if (c != null) {
                mSurfaceHolder.unlockCanvasAndPost(c);
            }
        }
    }
}

退出活动时在API 15上: 当doDraw() - 方法尝试在“c”上写入时,异常就会出现。我检查了c并发现它是null,所以毫不奇怪我得到了一个例外。我还检查了mThreadActive并发现尽管我将其设置为false,但while - 循环仍会触发。 以下是代码示例:

public void run() {
    while (mThreadActive) {
        c = null;
        try {
            c = mSurfaceHolder.lockCanvas(null);
            synchronized (mSurfaceHolder) {
                if(mState == 1) {
                    updateValues();
                    updateAnimation();
                }

                if(!mThreadActive)    // so it really is!
                    Log.d("Thread", "mThreadActive is false!");

                if(c == null)   // so it is too!
                    Log.d("Thread", "c is null!");

                doDraw(c);   // error
            }
        } finally {
            if (c != null) {
                mSurfaceHolder.unlockCanvasAndPost(c);
            }
        }
    }
}

我可以想象mThreadActive在被while - 语句检查后变为假的原因,但我无法弄清楚为什么“{”在mSurfaceHolder.lockCanvas(null)之后为空。似乎代码没有顺序运行。

嗯,解决方法是在绘制之前检查c != null

public void run() {
    while (mThreadActive) {
        c = null;
        try {
            c = mSurfaceHolder.lockCanvas(null);
            synchronized (mSurfaceHolder) {
                if(mState == 1) {
                    updateValues();
                    updateAnimation();
                }
                if(c != null) // prevent drawing on c if c doesnt exist.
                    doDraw(c);
            }
        } finally {
            if (c != null) {
                mSurfaceHolder.unlockCanvasAndPost(c);
            }
        }
    }
}

那么为什么我会在API 15上获得异常,而它在API 10上运行良好呢? 另一个有趣的是,我有其他具有相同结构的SurfaceViews,但与此相比,它们工作得很好! 为什么代码没有顺序运行?是因为我在模拟器上测试(这是非常迟钝的)?

谢谢。

2 个答案:

答案 0 :(得分:2)

您提到尽管while()为假,但您的mThreadActive循环似乎仍在继续。 mThreadActive是否已标记volatile?它可能需要。

此外,lockCanvas(null)闻起来。由于我们无法看到您的其余代码,因此您不清楚自己要做什么。 API如何将null传递给lockCanvas? (我们是在讨论SurfaceHolder或子类的原始实例吗?)

请注意,SurfaceHolder.lockCanvas()的API规范表明它可以返回null

  

如果尚未创建曲面或无法编辑曲面,则返回null。您通常需要实现Callback.surfaceCreated以找出Surface何时可用。

通过阅读API,看起来您应该实现SurfaceHolder.Callback界面并回复surfaceCreated()事件,这实际上是一个事件,告诉您已准备好写入画布。

答案 1 :(得分:0)

调用时我也遇到了类似的问题:

public void surfaceDestroyed(SurfaceHolder holder)  {
    isAttached = false;
    this.drawthread = null;
}

当退出我的应用程序以停止绘制线程时 - 我使用isAttached的while布尔值为false,但是while循环中的绘图代码仍然执行 - 在退出时给出了一个nullexception。不是一个显示塞子,但我真的想要修复。正在考虑if (canvas != null)种解决方案,但我认为必须有更好的方法。

现在这里出现了这个错误的奇怪之处 - 它只在某些时候发生 - 并且在更快的设备上发生的情况更少。我看到的问题是holder.lockCanvas()返回null - 意味着在boolean设置为false之前,holder被销毁,而while有机会停止执行Thread。线程竞赛问题?

在使while boolean null之前找到了一个使用Thread.join()的可能解决方案,但是holder.lockCanvas()返回null,在绘制完成之前,持有者被销毁,所以它有点无意义并且仍然导致一个nullexception。

我能想到的只有其他解决方案是覆盖后退按钮方法并在销毁surfaceview之前将while bool强制为null(如果可能的话),但我认为if (canvas != null)可能更清晰。那里的任何其他想法都渴望听到!

编辑:尚未在其他地方测试,但我使用API​​ 17

收到错误