Приложение может делать слишком много работы в своем основном потоке

379

Я новичок в среде Android SDK / API. Это первое, что я пытаюсь нарисовать график / график. Я пробовал запускать различные виды примеров кода на эмуляторе с использованием 3 разных бесплатных библиотек, на экране макета ничего не отображается. LogCat повторяет следующее сообщение:

 W / Trace (1378): неожиданное значение от nativeGetEnabledTags: 0
 Я / Хореограф (1378): пропущено 55 кадров! Приложение может выполнять слишком много работы в своем основном потоке.

Проблема не исчезла, и диаграмма работала, когда я запустил образец кода, относящийся к пробной копии лицензионной библиотеки.

user2038135
источник
2
Вы рисуете свои диаграммы в отдельной теме?
Арекс
Спасибо за ваш комментарий, я отредактировал вопрос, чтобы сделать его более понятным. Действие при выполнении показывает, что я выполняю действие, которое не имеет дизайна, его макет => показывает белый экран.
user2038135
1
@ Арекс Нет, я не использую отдельную ветку.
user2038135
1
Я думаю, что вам вообще не рекомендуется выполнять длинные операции с основным потоком, потому что это останавливает все приложение, вы можете прочитать, как использовать потоки, здесь: stackoverflow.com/questions/3391272/… Игнорировать код HTTP-запрос "и просто выполните ваши потенциально длинные операции там.
Арекс
1
Почему бы вам не попробовать поискать, вы найдете информацию о хореографе. Я рекомендую вам прочитать этот ответ: stackoverflow.com/questions/11266535/…
Габриэль Эстебан

Ответы:

479

взято из: Android UI: исправление пропущенных кадров

Любой, кто начинает разработку приложения для Android, видит это сообщение на logcat «Хореограф (abc): пропущенные xx кадры! Приложение может выполнять слишком много работы над своим основным потоком ». Так что же это на самом деле означает, почему вы должны быть обеспокоены и как это решить.

Это означает, что вашему коду требуется много времени для обработки, а фреймы пропускаются из-за него. Возможно, из-за какой-то тяжелой обработки, которую вы выполняете в основе вашего приложения или доступа к БД, или любой другой вещи, которая заставляет поток остановитесь на некоторое время.

Вот более подробное объяснение:

Choreographer позволяет приложениям подключаться к vsync и правильно рассчитывать время для повышения производительности.

Android-анимация для внутреннего просмотра использует Choreographer для той же цели: для правильного определения времени анимации и, возможно, для повышения производительности.

Так как Choreographer рассказывается обо всех событиях vsync, я могу сказать, не завершается ли один из Runnables, переданных с помощью Choreographer.post * apis, за один кадр, что приводит к пропуску кадров.

В моем понимании хореограф может только обнаружить пропуск кадра. Невозможно объяснить, почему это происходит.

Сообщение «Приложение может выполнять слишком много работы в своем основном потоке». может вводить в заблуждение.

источник: смысл сообщений хореографа в Logcat

Почему вы должны быть обеспокоены

Когда это сообщение появляется на эмуляторе Android, а количество пропущенных кадров довольно мало (<100), вы можете сделать безопасную ставку на медленный эмулятор - что происходит почти все время. Но если количество пропущенных и больших кадров составляет порядка 300+, то с вашим кодом могут возникнуть серьезные проблемы. Устройства Android поставляются с огромным количеством аппаратного обеспечения, в отличие от устройств на базе iOS и Windows. Оперативная память и процессор различаются, и если вы хотите разумную производительность и удобство работы на всех устройствах, вам нужно исправить это. Когда кадры пропускаются, пользовательский интерфейс работает медленно и медленно, что нежелательно для пользователя.

Как это исправить

Исправление этого требует идентификации узлов, где есть или может произойти большая продолжительность обработки. Лучший способ - выполнять всю обработку независимо от того, насколько она мала или велика в отдельном от основного потока пользовательского интерфейса. Так что будь то доступ к данным из базы данных SQLite или выполнение какой-то жесткой математики или просто сортировка массива - сделайте это в другом потоке

Теперь здесь есть одна загвоздка: вы создадите новый поток для выполнения этих операций, и при запуске приложения произойдет сбой, сказав, что «только исходный поток, создавший иерархию представлений, может касаться его представлений». Вы должны знать, что пользовательский интерфейс в Android может быть изменен только основным потоком или потоком пользовательского интерфейса. Любой другой поток, который пытается это сделать, завершается сбоем и вылетает с этой ошибкой. Вам нужно создать новый Runnable внутри runOnUiThread, и внутри этого runnable вы должны выполнять все операции, связанные с пользовательским интерфейсом. Найдите пример здесь .

Итак, у нас есть Thread и Runnable для обработки данных из основного потока, что еще? В Android есть AsyncTask, который позволяет выполнять длительные процессы в потоке пользовательского интерфейса. Это наиболее полезно, когда ваши приложения управляются данными или веб-API или используют сложные пользовательские интерфейсы, подобные тем, которые создаются с использованием Canvas. Сила AsyncTask заключается в том, что она позволяет делать вещи в фоновом режиме, и как только вы закончите обработку, вы можете просто выполнить необходимые действия в пользовательском интерфейсе, не вызывая какого-либо запаздывающего эффекта. Это возможно, потому что AsyncTask происходит из потока пользовательского интерфейса Activity - все операции, которые вы выполняете в пользовательском интерфейсе через AsyncTask, выполняются в другом потоке, отличном от основного потока пользовательского интерфейса. Никаких помех для взаимодействия с пользователем.

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

Jorgesys
источник
41
У меня просто есть приложение, в котором, если я нажимаю кнопку, фоновое изображение кнопки меняется, и кнопка становится неактивной. Как я делаю слишком много работы :(
Remian8985
1
@ Remian8985 - Изменение фонового изображения для кнопки (при условии, что вы загружаете это изображение) должно быть выполнено в AsyncTask - то есть, чтобы выполнить эту фоновую операцию загрузки и опубликовать результат в потоке пользовательского интерфейса (предоставьте изображение обратно). См. Ссылку на Android- ссылку
BenJaminSila
11
@BenJaminSila меняет фон в AsyncTask? В самом деле?
user25
11
@ user25 «при условии, что вы скачиваете это изображение»
forresthopkinsa
«Когда это сообщение появляется на эмуляторе Android, а количество пропущенных кадров довольно мало (<100), вы можете сделать безопасную ставку на медленный эмулятор». Это все еще применимо сегодня? Эмуляторы становятся довольно быстрыми, верно?
Робин Диджхоф
243

Как и другие отвечали выше, "пропустили 55 кадров!" означает, что некоторая тяжелая обработка находится в вашем приложении.

В моем случае нет тяжелого процесса в моем приложении. Я дважды и трижды проверил все и удалил те процессы, которые мне показались немного тяжелыми.

Я удалил фрагменты, действия, библиотеки, пока не остался только скелет. Но все же проблема не ушла. Я решил проверить ресурсы и обнаружил, что некоторые иконки и фон, которые я использую, довольно большие, потому что я забыл проверить размер этих ресурсов.

Итак, я предлагаю, если ни один из приведенных выше ответов не поможет, вы также можете проверить размер файлов ресурсов.

Ситху
источник
1
У меня тоже сработало. У меня было приложение, которое выполняло очень мало работы, но было медленным и медленным. Я продолжал получать пропущенные кадры журналов. Как только я удалил фон из своей деятельности, все было хорошо. Спасибо!
Акраби
Отличный ответ, я считаю, что это была именно моя проблема. Я попробовал кучу других (довольно сложных) решений, и приложение было таким же медленным. Я удалил все веб-сервисы и попытался оптимизировать мой код до мозга костей. Не сработало, тогда я это увидел. Как только я удалил свое фоновое изображение (самое большое изображение, которое у меня есть), приложение работает примерно так же быстро, как вы можете щелкнуть мышью, даже со старым «медленным» кодом.
М Барбоза
вы сделали мой день!
Николас Мастромарино
:) Вы абсолютный гений.
Метин Ильхан
@batsheva не обязательно быть 1 КБ. Это зависит от ваших потребностей, скажем, вам нужно более четкое изображение, вы можете использовать более высокое разрешение, но убедитесь, что вы разбиты на разные размеры на разные папки ресурсов.
Sithu
61

У меня тоже была такая же проблема.
Мой был случай, когда я использовал фоновое изображение, которое было в drawable. Это конкретное изображение было около 130 КБ и использовалось во время заставки и домашней страницы в моем приложении для Android.

Решение - я просто переместил это конкретное изображение в папку drawables-xxx из drawables и смог освободить много памяти, занятой фоном, и пропускаемые кадры больше не пропускались.

Обновление Используйте папку ресурсов для рисования «nodp» для хранения фоновых файлов.
Будет ли приоритетной для рисования папка, соответствующая плотности, или drawable-nodpi?

Prakhar1001
источник
7
Я переместил свое большое фоновое изображение из нарисованного в mimap-xxxhdpi, и это сработало!
bgplaya
Вы очень помогли. Спасибо
N.Droid
2
Это решение делает свое дело. drawable-xxxhdpiВместо этого я использую папку, drawableчто значительно уменьшает используемую память (на 70% меньше). Также полезно знать, что экраны одного и того же размера различаются по размеру DPI. Соотношение в пикселях между ними ldpi = 1:0.75, mdpi = 1:1, hdpi = 1:1.5, xhdpi = 1:2, xxhdpi = 1:3, xxxhdpi = 1:4. Используя drawable-xxxhdpiпапку, вы позволяете уменьшать изображения до размеров экрана вашего устройства, что снижает потребление памяти и ЦП.
Тимо
2
Перемещение изображений из drawableв drawable-nodpiпредотвращает получение приложения Out of Memory Error.
Шрути
Боже мой ... спасибо! У меня было изображение в папке drawable, и это сделало мое приложение медленным до чертиков (хотя изображение было только 100kb !!!). После генерации файлов drawable-xxx (я использовал Android Drawable Importer) мое приложение чертовски быстро. Большое спасибо!
error1337
20

Другой распространенной причиной задержек в потоке пользовательского интерфейса является доступ к SharedPreferences. Когда вы вызываете a PreferenceManager.getSharedPreferencesи другие подобные методы в первый раз, связанный XML-файл немедленно загружается и анализируется в том же потоке. .

Одним из хороших способов борьбы с этой проблемой является запуск первой загрузки SharedPreference из фонового потока, запускаемой как можно раньше (например, из onCreate вашего класса Application). Таким образом, объект предпочтения может быть уже создан к тому времени, когда вы захотите его использовать.

К сожалению, иногда чтение файлов предпочтений необходимо на ранних этапах запуска (например, в начальном действии или даже в самом приложении). В таких случаях все еще возможно избежать остановки пользовательского интерфейса с помощью MessageQueue.IdleHandler. Сделайте все остальное, что вам нужно выполнить в главном потоке, затем установите IdleHandler для выполнения кода после того, как ваша активность будет полностью прорисована. В этом Runnable вы должны иметь доступ к SharedPreferences, не откладывая слишком много операций рисования и не делая Choreographer несчастным.

user1643723
источник
1
В этом случае вы должны предпочесть метод apply () вместо commit (). Метод apply () не может блокировать пользовательский интерфейс. Вы можете посмотреть здесь developer.android.com/training/data-storage/shared-preferences
Эмре Гюрсес
16

Попробуйте использовать следующие стратегии для повышения производительности вашего приложения:

  • Если возможно, используйте многопоточное программирование. Выигрыш в производительности огромен, даже если ваш смартфон имеет одно ядро ​​(потоки могут работать на разных ядрах, если процессор имеет два или более). Полезно отделить логику вашего приложения от пользовательского интерфейса. Используйте потоки Java, AsyncTask или IntentService. Проверьте это .
  • Прочитайте и следуйте советам по разной производительности сайта разработки Android. Проверьте здесь .
MigDus
источник
3
Ваша первая ссылка требует, чтобы у вас "... была подтвержденная учетная запись ..." для доступа к ней.
перерыв
9

У меня такая же проблема. Эмулятор Android отлично работал на Android <6.0. Когда я использовал эмулятор Nexus 5 (Android 6.0), приложение работало очень медленно I/Choreographer: Skipped framesв журналах.

Итак, я решил эту проблему, изменив hardwareAcceleratedопцию в файле манифеста trueследующим образом:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapplication">

    <application android:hardwareAccelerated="true">
        ...
    </application>
</manifest>
phen0menon
источник
8

Я не эксперт, но я получил это сообщение отладки, когда я хотел отправить данные из моего приложения для Android на веб-сервер. Хотя я использовал класс AsyncTask и выполнял передачу данных в фоновом режиме, для получения данных результата с сервера я использовал метод get () класса AsyncTask, который делает интерфейс синхронным, что означает, что ваш интерфейс будет ожидать слишком долго. Поэтому я советую вашему приложению выполнять все сетевые задачи в отдельном потоке.

саба
источник
6

Оптимизируйте ваши изображения ... Не используйте изображения размером более 100 КБ ... Загрузка изображений занимает слишком много ресурсов процессора и приводит к зависанию вашего приложения.

HarshitG
источник
4
Уменьшите размер изображения либо с помощью кода Java, либо используйте Photoshop для обрезки изображений ... а также сожмите изображения с помощью compressor.io
HarshitG
5

У меня такая же проблема. В моем случае у меня было 2 вложенных реляционных макета. RelativeLayout всегда должен делать два прохода измерения. Если вы вложите RelativeLayouts, вы получите алгоритм экспоненциального измерения.

Радослав
источник
4

это обычно происходит, когда вы выполняете огромные процессы в главном потоке. можно пропустить кадры менее 200. но если у вас более 200 пропущенных кадров, это может замедлить поток пользовательского интерфейса приложения. что вы можете сделать, это сделать эти процессы в новом потоке, называемом рабочим потоком, и после этого, когда вы захотите получить доступ и выполнить что-либо с потоком пользовательского интерфейса (например: сделать что-то с представлениями, findView и т. д.), вы можете использовать обработчик или runOnUiThread (Мне это нравится больше), чтобы отобразить результаты обработки. это абсолютно решает проблему. Использование рабочих потоков очень полезно или даже должно использоваться, когда дело доходит до этого случая.

Хоссейн Карами
источник
1

У меня такая же проблема. Когда я запускал код на другом компьютере, он работал нормально. На моем, однако, он отображал «Приложение может выполнять слишком много работы в своем основном потоке».

Я решил свою проблему, перезапустив Android-студию [Файл -> Недействительные кеши / Перезапуск -> нажмите «Отменить и перезагрузить»].

sonida
источник
Я не знаю, почему ваше решение сработало. В любом случае спасибо.
Бхуванеш Б.С.
1

В моем случае это было потому, что я случайно установил точку останова для метода. Как только я очистил его, сообщение исчезло, и производительность значительно улучшилась.

FractalBob
источник
0

У моего приложения была такая же проблема. Но это было не что иное, как отображение списка карточек и текста на нем. Ничего не работает в фоновом режиме. Но затем после некоторого расследования обнаружил, что причиной этого было изображение, установленное для фона карты, хотя оно было маленьким (350 КБ). Затем я преобразовал изображение в 9-образное изображение, используя http://romannurik.github.io/AndroidAssetStudio/index.html .
Это сработало для меня.

naamadheya
источник
0

Проведя много исследований и разработок по этому вопросу, я получил решение,

В моем случае я использую Сервис, который будет запускаться каждые 2 секунды и с runonUIThread, мне было интересно, что проблема была там, но не совсем. Следующая проблема, которую я обнаружил, состоит в том, что я использую большое изображение в майском приложении, и в этом проблема.

Я удалил Изображения и установил новые Изображения.

Вывод: - Посмотрите в своем коде есть ли какой-либо необработанный файл, который вы используете, имеет большой размер.

Гектор Моррис
источник
0

Сначала прочитайте предупреждение. Это говорит о большей нагрузке на основной поток. Так что вам нужно просто запустить функции с большим количеством работы в потоке.

de_billa_
источник
-1

У меня возникла та же проблема при разработке приложения, которое использует много разрисовываемых png-файлов в сетке. Я также попытался оптимизировать мой код, насколько это возможно ... но у меня не получилось ... Потом я попытался уменьшить размер этих png ... и думаю, что он работает абсолютно нормально ... Поэтому я предлагаю уменьшить размер доступных ресурсов, если таковые имеются.

Шубхам Ранакоти
источник