В моем приложении для 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) { }
Но это не настоящие исправления, не так ли? Разве это не странно?
(В зависимости от того, какие изменения я внес в код, я также могу увидеть исключение в журнале ошибок. Существует много разработчики с то же самое проблема, но, к сожалению, ни один из них не дает решения для моего (зависящего от устройства) случая.
Вы можете помочь?
surfaceCreated()
должно работать нормально. См., например. github.com/google/grafika/blob/ master/src/com/android/grafika/ . Некоторые дополнительные примечания о взаимодействии SurfaceView и Activity см. в разделе source.android.com/devices. /graphics/architecture.html#activity - person fadden   schedule 17.12.2015xxhdpi
илиxxxhdpi
. - person caw   schedule 17.12.2015run()
моего потока рендеринга постоянно выполняется в фоновом режиме, а вызываемый методdoDraw()
запускается примерно каждые 40 мс. Так что на самом деле не похоже, чтобы что-то работало слишком долго или слишком долго занималоCanvas
. Все равно основной поток зависает и экран черный. - person caw   schedule 17.12.2015