3

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

这是在 API 10 上为我工作的代码:它是run()-method 的通用结构。

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()-method 尝试在“c”上写入时发生异常。我检查了 c 并发现它是空的,所以我得到一个异常也就不足为奇了。我还检查mThreadActive并发现虽然我将其设置为 false,但while-loop 仍然会触发。这是代码示例:

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

我可以想象为什么在被-statementmThreadActive检查后会变为 false ,但我无法弄清楚为什么 "c" 在. 似乎代码没有按顺序运行。whilemSurfaceHolder.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 上运行良好?另一个有趣的事情是,我有其他结构相同的 SurfaceView,但与这个相比,它们工作得很好!为什么代码不按顺序运行?是因为我正在模拟器上进行测试(这非常滞后)吗?

谢谢你。

4

2 回答 2

2

您提到尽管是错误的,您的while()循环似乎仍在继续。mThreadActivemThreadActive标记volatile吗?它可能需要。

还有,lockCanvas(null)气味。由于我们看不到您的其余代码,因此尚不清楚您要做什么。API 对传递 a 有什么null看法lockCanvas?(我们是在谈论原始实例SurfaceHolder还是子类?)

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

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

从阅读 API 来看,您应该正在实现SurfaceHolder.Callback接口并响应surfaceCreated()事件,这实际上是告诉您已准备好写入画布的事件。

于 2012-05-12T12:10:52.020 回答
0

打电话时我也遇到了一个非常相似的问题:

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

当退出我的应用程序以停止绘图线程时 - 我使用的 while 布尔isAttached值为 false,但该 while 循环中的绘图代码仍然执行 - 在退出时给出 null 异常。不是表演停止者,而是我真正想解决的问题。也在考虑一种if (canvas != null)解决方案,但我认为必须有更好的方法。

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

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

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

编辑:尚未在其他地方测试,但我使用 API 17 得到错误

于 2013-02-08T07:11:11.243 回答