Intereting Posts
Получить текущий объект фрагмента Использование архитектуры Android? Действительно ли фрагментам нужен пустой конструктор? Аутентификация FirebaseUI с помощью Facebook не работает Дополнительные разрешения, чтобы приложение отображалось на всех устройствах и включало дополнительные функции на некоторых устройствах? Как проверить, является ли вид фрагмента видимым для пользователя? Сжатие изображения камеры перед загрузкой Сообщение Firebase NoSuchMethodError.zzUr exception Сохранить Android SQLite Android Studio 2.0 – плагин слишком стар, обновите его до более поздней версии или установите переменную окружения ANDROID_DAILY_OVERRIDE в IntelliJ IDEA – ничего не может построить, всегда получается, что «пакет R не существует» Как отключить режим сдвига BottomNavigationView? Android Studio с сервисами Google Play Как я могу использовать новый Xtend-язык Eclipse в своем проекте Android? Создание хэш-карты / карты из XML-ресурсов

ANR в SurfaceView только для определенных устройств – Единственное исправление – короткое время сна

В моем приложении для Android я использую SurfaceView для рисования вещей. Он отлично работает на тысячах устройств, за исключением того, что теперь пользователи начали сообщать ANR на следующих устройствах:

  • LG G4
    • Android 5.1
    • 3 ГБ оперативной памяти
    • 5.5 "дисплей
    • Разрешение 2560 x 1440 px
  • 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) { } 

Но это не настоящие исправления, не так ли? Разве это не странно?

(В зависимости от того, какие изменения я вношу в код, я также могу видеть исключение в журнале ошибок. Есть много разработчиков с одной и той же проблемой, но, к сожалению, ни один из них не предоставляет решение для моего (конкретного устройства) случая.

Вы можете помочь?

Solutions Collecting From Web of "ANR в SurfaceView только для определенных устройств – Единственное исправление – короткое время сна"

То, что в настоящее время работает для меня, хотя на самом деле не фиксирует причину проблемы, но поверхностно нарушает симптомы:

1. Удаление операций с Canvas

Мой поток render вызывает настраиваемый метод doDraw(Canvas canvas) в подклассе SurfaceView .

В этом методе, если я удалю все вызовы Canvas.drawBitmap(...) , Canvas.drawRect(...) и других операций на Canvas , приложение больше не замерзает.

В Canvas.drawColor(int color) может остаться один вызов Canvas.drawColor(int color) . И даже дорогостоящие операции, такие как BitmapFactory.decodeResource(Resources res, int id, Options opts) и чтение / запись в мой внутренний Bitmap кеш прекрасно. Без зависаний.

Очевидно, что без рисования SurfaceView не очень помогает.

2. Спящий 10 мс в цикле выполнения рендеринга потока

Метод, выполняемый моим потоком рендеринга:

 @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. Откладывание начала рендеринга на 10 мс

Вот как я запускаю поток рендеринга из 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, Sony Xperia Z4, Huawei Ascend Mate 7, HTC M9 (и, вероятно, нескольких других устройств), приложение отлично работает на тысячах устройств.

Может ли это быть конкретным аппаратным сбоем? Можно было бы услышать об этом …


Все эти «решения» хаки. Хотелось бы, чтобы было лучшее решение – и я уверен, что есть!

Посмотрите на след ANR. Где он, кажется, застревает? ANR означают, что основной поток пользовательского интерфейса не отвечает, поэтому то, что вы делаете в потоке рендеринга, не имеет значения, если они не сражаются за блокировку.

Симптомы, о которых вы сообщаете, звучат как гонка. Если ваш основной поток пользовательского интерфейса застрял, скажем, mRunningLock , возможно, что поток вашего рендеринга оставит его незаблокированным для очень короткого окна. Добавление сообщения журнала или вызов сна дает основному потоку возможность проснуться и выполнить работу до того, как поток рендеринга снова захватит его.

(Это на самом деле не имеет смысла для меня – ваш код выглядит так, что он должен быть остановлен, ожидая lockCanvas() , ожидая обновления дисплея, поэтому вам нужно посмотреть на трассировку потока в ANR.)

FWIW, вам не нужно синхронизировать на mSurfaceHolder . Ранний пример сделал это, и каждый пример с тех пор клонировал его.

Как только вы это выясните, вы можете прочитать о игровых циклах .