【发布时间】:2016-03-22 05:31:27
【问题描述】:
在我的 Android 应用程序中,我使用 SurfaceView 来绘制东西。它在数千台设备上运行良好——除了现在用户开始在以下设备上报告 ANR:
- LG G4
- Android 5.1
- 3 GB 内存
- 5.5 英寸显示屏
- 2560 x 1440 像素分辨率
- 索尼 Xperia Z4
- Android 5.0
- 3 GB 内存
- 5,2" 显示屏
- 1920 x 1080 像素分辨率
- 华为Ascend Mate 7
- Android 5.1
- 3 GB 内存
- 6.0" 显示屏
- 1920 x 1080 像素分辨率
- 宏达 M9
- Android 5.1
- 3 GB 内存
- 5.0" 显示屏
- 1920 x 1080 像素分辨率
所以我得到了一台 LG G4 并且确实能够验证问题。它与SurfaceView直接相关。
现在猜猜经过数小时的调试后是什么解决了这个问题?它正在取代...
mSurfaceHolder.unlockCanvasAndPost(c);
...与...
mSurfaceHolder.unlockCanvasAndPost(c);
System.out.println("123"); // THIS IS THE FIX
这怎么可能?
以下代码是我的渲染线程,除了提到的设备外,它一直运行良好:
import android.graphics.Canvas;
import android.view.SurfaceHolder;
public class MyThread extends Thread {
private final SurfaceHolder mSurfaceHolder;
private final MySurfaceView mSurface;
private volatile boolean mRunning = false;
public MyThread(SurfaceHolder surfaceHolder, MySurfaceView surface) {
mSurfaceHolder = surfaceHolder;
mSurface = surface;
}
public void setRunning(boolean run) {
mRunning = run;
}
@Override
public void run() {
Canvas c;
while (mRunning) {
c = null;
try {
c = mSurfaceHolder.lockCanvas();
if (c != null) {
mSurface.doDraw(c);
}
}
finally { // when exception is thrown above we may not leave the surface in an inconsistent state
if (c != null) {
try {
mSurfaceHolder.unlockCanvasAndPost(c);
}
catch (Exception e) { }
}
}
}
}
}
代码部分来自 Android SDK 中的 LunarLander 示例,更具体地说是 LunarView.java。
更新代码以匹配 Android 6.0(API 级别 23)的改进示例会产生以下结果:
import android.graphics.Canvas;
import android.view.SurfaceHolder;
public class MyThread extends Thread {
/** Handle to the surface manager object that we interact with */
private final SurfaceHolder mSurfaceHolder;
private final MySurfaceView mSurface;
/** Used to signal the thread whether it should be running or not */
private boolean mRunning = false;
/** Lock for `mRunning` member */
private final Object mRunningLock = new Object();
public MyThread(SurfaceHolder surfaceHolder, MySurfaceView surface) {
mSurfaceHolder = surfaceHolder;
mSurface = surface;
}
/**
* Used to signal the thread whether it should be running or not
*
* @param running `true` to run or `false` to shut down
*/
public void setRunning(final boolean running) {
// do not allow modification while any canvas operations are still going on (see `run()`)
synchronized (mRunningLock) {
mRunning = running;
}
}
@Override
public void run() {
while (mRunning) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
// do not allow flag to be set to `false` until all canvas draw operations are complete
synchronized (mRunningLock) {
// stop canvas operations if flag has been set to `false`
if (mRunning) {
mSurface.doDraw(c);
}
}
}
}
// if an exception is thrown during the above, don't leave the view in an inconsistent state
finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
但是,这个类仍然不能在提到的设备上工作。我出现黑屏并且应用程序停止响应。
唯一(我发现的)解决问题的方法是添加System.out.println("123") 调用。并且在循环结束时添加一个短的睡眠时间结果是提供相同的结果:
try {
Thread.sleep(10);
}
catch (InterruptedException e) { }
但这些都不是真正的修复,是吗?这不奇怪吗?
(根据我对代码所做的更改,我还可以在错误日志中看到异常。有很多 developers with the same problem 但不幸的是没有为我的(特定于设备的)案例提供解决方案。
你能帮忙吗?
【问题讨论】:
-
从
surfaceCreated()创建线程应该可以正常工作。参见例如github.com/google/grafika/blob/master/src/com/android/grafika/… 。有关 SurfaceView 和 Activity 交互的一些额外说明,请参阅source.android.com/devices/graphics/architecture.html#activity -
谢谢!是的,我已经阅读了所有这些内容。这就是为什么我可以消除大部分可能的原因。一定是一些奇怪的竞争条件或线程锁定问题,在所有其他设备上都不会发生。我认为这可能是这些设备的高显示分辨率。但在平板电脑上,这些高分辨率已经很普遍了,而且它也适用于具有高分辨率的 Nexus 7 (2013) 等设备。但是,如果这有所作为,那么可能是更高的像素密度。也许是因为我在
xxhdpi或xxxhdpi中没有任何使用过的drawable。 -
我的渲染线程的
run()循环在后台连续执行,调用的doDraw()方法大约每40ms 运行一次。因此,似乎确实没有任何事情运行太久或让Canvas忙碌太久。尽管如此,主线程还是冻结了,屏幕是黑色的。
标签: java android surfaceview android-anr-dialog