В моем приложении для Android я использую SurfaceView
для рисования вещей. Он отлично работает на тысячах устройств, за исключением того, что теперь пользователи начали сообщать о ANR на следующих устройствах:
- LG G4
- Android 5.1
- Оперативная память 3 ГБ
- 5.5 "
- Разрешение 2560 x 1440 пикселей
- Sony Xperia Z4
- Android 5.0
- Оперативная память 3 ГБ
- 5,2 "
- Разрешение 1920 x 1080 пикселей
- Huawei Ascend Mate 7
- Android 5.1
- Оперативная память 3 ГБ
- 6.0 "
- Разрешение 1920 x 1080 пикселей
- HTC M9
- Android 5.1
- Оперативная память 3 ГБ
- 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) { }
}
}
}
}
}
Код, по частям, из примера LunarLander
в Android 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) { }
Но это не настоящие исправления, не так ли? Разве это не странно?
(В зависимости от изменений, которые я вношу в код, я также могу видеть исключение в журнале ошибок. Существует много developers с та же проблема, но, к сожалению, никто не предоставляет решение для моего (конкретного устройства) случая.
Вы можете помочь?