caw*_*caw 18 java android surfaceview android-anr-dialog
在我的Android应用程序中,我用a SurfaceView来绘制东西.它在数千台设备上运行良好 - 除了现在用户开始在以下设备上报告ANR:
所以我得到了LG G4,确实能够验证问题.它与...直接相关SurfaceView.
现在猜猜经过几个小时的调试后修复了什么问题?它正在取代......
mSurfaceHolder.unlockCanvasAndPost(c);
Run Code Online (Sandbox Code Playgroud)
...... ......
mSurfaceHolder.unlockCanvasAndPost(c);
System.out.println("123"); // THIS IS THE FIX
Run Code Online (Sandbox Code Playgroud)
怎么会这样?
以下代码是我的渲染线程,除了提到的设备之外一直工作正常:
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) { }
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
代码部分来自LunarLanderAndroid SDK中的示例,更具体地说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);
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
但是,这个类仍不适用于上述设备.我得到一个黑屏,应用程序停止响应.
唯一(我发现)解决问题的方法是添加System.out.println("123")调用.并且在循环结束时添加一个短暂的睡眠时间,结果是提供相同的结果:
try {
Thread.sleep(10);
}
catch (InterruptedException e) { }
Run Code Online (Sandbox Code Playgroud)
但这些都不是真正的修复,是吗?这不奇怪吗?
(根据什么改变我对代码做,我也能看到一个异常错误日志.有许多开发商 用 了 同样的 问题,但遗憾的是没有确实提供了我的(设备特定的)情况下的解决方案.
你能帮我吗?
目前正在为我工作的是什么,虽然没有真正解决问题的原因但是表面上对抗症状:
Canvas操作我的渲染线程调用子类doDraw(Canvas canvas)上的自定义方法SurfaceView.
在该方法中,如果我删除所有来电Canvas.drawBitmap(...),Canvas.drawRect(...)对等操作Canvas,应用程序不会再冻结.
单个调用Canvas.drawColor(int color)可能会留在方法中.即使是昂贵的操作,比如BitmapFactory.decodeResource(Resources res, int id, Options opts)读取/写入我的内部Bitmap缓存也是如此.没有冻结.
显然,没有任何绘图,这SurfaceView并不是真的有用.
我的渲染线程执行的方法:
@Override
public void run() {
Canvas c;
while (mRunning) {
c = null;
try {
c = mSurfaceHolder.lockCanvas();
if (c != null) {
mSurface.doDraw(c);
}
}
finally {
if (c != null) {
try {
mSurfaceHolder.unlockCanvasAndPost(c);
}
catch (Exception e) { }
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
简单地在循环中添加一个短暂的睡眠时间(例如在结尾处)可以修复LG G4上的所有冻结:
while (mRunning) {
...
try { Thread.sleep(10); } catch (Exception e) { }
}
Run Code Online (Sandbox Code Playgroud)
但是谁知道为什么这样可行,如果这确实解决了问题(在所有设备上).
System.out与Thread.sleep(...)上述相同的事情也可以使用System.out.println("123"),奇怪的是.
这是我从以下内容开始渲染线程的方式SurfaceView:
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
mRenderThread = new MyThread(getHolder(), this);
mRenderThread.setRunning(true);
mRenderThread.start();
}
Run Code Online (Sandbox Code Playgroud)
在以下延迟执行中包含这三行时,应用程序不再冻结:
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
...
}
}, 10);
Run Code Online (Sandbox Code Playgroud)
这似乎是因为在开始时只有一个可能的死锁.如果清除(延迟执行),则没有其他死锁.该应用程序运行后就好了.
但是当离开时Activity,应用程序再次冻结.
除了LG G4,索尼Xperia Z4,华为Ascend Mate 7,HTC M9(以及其他一些设备)外,该应用程序在数千台设备上运行良好.
这可能是特定于设备的故障吗?人们肯定会听说过这个......
所有这些"解决方案"都很苛刻.我希望有更好的解决方案 - 我打赌有!
| 归档时间: |
|
| 查看次数: |
796 次 |
| 最近记录: |