当前位置:  开发笔记 > 编程语言 > 正文

仅在特定设备上的SurfaceView中的ANR - 唯一的解决方案是短暂的睡眠时间

如何解决《仅在特定设备上的SurfaceView中的ANR-唯一的解决方案是短暂的睡眠时间》经验,为你挑选了1个好方法。

在我的Android应用程序中,我用a SurfaceView来绘制东西.它在数千台设备上运行良好 - 除了现在用户开始在以下设备上报告ANR:

LG G4

Android 5.1

3 GB RAM

5.5"显示

2560 x 1440 px分辨率

索尼Xperia Z4

Android 5.0

3 GB RAM

5,2"显示

1920 x 1080 px分辨率

华为Ascend Mate 7

Android 5.1

3 GB RAM

6.0"显示

1920 x 1080 px分辨率

HTC M9

Android 5.1

3 GB RAM

5.0"显示

1920 x 1080 px分辨率

所以我得到了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) { }
                }
            }
        }
    }

}

代码部分来自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);
                }
            }
        }
    }

}

但是,这个类仍不适用于上述设备.我得到一个黑屏,应用程序停止响应.

唯一(我发现)解决问题的方法是添加System.out.println("123")调用.并且在循环结束时添加一个短暂的睡眠时间,结果是提供相同的结果:

try {
    Thread.sleep(10);
}
catch (InterruptedException e) { }

但这些都不是真正的修复,是吗?这不奇怪吗?

(根据什么改变我对代码做,我也能看到一个异常错误日志.有许多开发商 用 了 同样的 问题,但遗憾的是没有确实提供了我的(设备特定的)情况下的解决方案.

你能帮我吗?



1> caw..:

目前正在为我工​​作的是什么,虽然没有真正解决问题的原因但是表面上对抗症状:

1.删​​除Canvas操作

我的渲染线程调用子类doDraw(Canvas canvas)上的自定义方法SurfaceView.

在该方法中,如果我删除所有来电Canvas.drawBitmap(...),Canvas.drawRect(...)对等操作Canvas,应用程序不会再冻结.

单个调用Canvas.drawColor(int color)可能会留在方法中.即使是昂贵的操作,比如BitmapFactory.decodeResource(Resources res, int id, Options opts)读取/写入我的内部Bitmap缓存也是如此.没有冻结.

显然,没有任何绘图,这SurfaceView并不是真的有用.

2.在渲染线程的运行循环中休眠10ms

我的渲染线程执行的方法:

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

简单地在循环中添加一个短暂的睡眠时间(例如在结尾处)可以修复LG G4上的所有冻结:

while (mRunning) {
    ...

    try { Thread.sleep(10); } catch (Exception e) { }
}

但是谁知道为什么这样可行,如果这确实解决了问题(在所有设备上).

3.打印东西 System.out

Thread.sleep(...)上述相同的事情也可以使用System.out.println("123"),奇怪的是.

4.延迟渲染线程的开始10ms

这是我从以下内容开始渲染线程的方式SurfaceView:

@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
    mRenderThread = new MyThread(getHolder(), this);
    mRenderThread.setRunning(true);
    mRenderThread.start();
}

在以下延迟执行中包含这三行时,应用程序不再冻结:

new Handler().postDelayed(new Runnable() {

    @Override
    public void run() {
        ...
    }

}, 10);

这似乎是因为在开始时只有一个可能的死锁.如果清除(延迟执行),则没有其他死锁.该应用程序运行后就好了.

但是当离开时Activity,应用程序再次冻结.

5.只需使用其他设备

除了LG G4,索尼Xperia Z4,华为Ascend Mate 7,HTC M9(以及其他一些设备)外,该应用程序在数千台设备上运行良好.

这可能是特定于设备的故障吗?人们肯定会听说过这个......


所有这些"解决方案"都很苛刻.我希望有更好的解决方案 - 我打赌有!

推荐阅读
云聪京初瑞子_617
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有