Программирование Android Как сделать Android-приложение быстрым и отзывчивым Thu, November 21 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.


Как сделать Android-приложение быстрым и отзывчивым Печать
Добавил(а) microsin   

Android-ANR-dialogНаверное можно написать код, который будет бить все рекорды по быстродействию, но он все равно будет выглядеть вялым, зависать на значительный период времени, и думать слишком долго, чтобы обработать процесс ввода. И самое плохое, что может случиться в плане отзывчивости программы - это появление окна диалога "Приложение не отвечает" (экран ANR, "Application Not Responding").

В Android имеется защита от приложений, которые не отвечают должным образом по времени, и в этом случае система отображает соответствующее окно диалога ANR, которое предлагает остановить неработающее приложение, как это показано на скриншоте. В этот момент Ваше предложение не отвечает в течение установленного периода времени, и система предлагает пользователю опцию выбора - закрыть приложение или еще подождать. При разработке приложения является критичным требование отзывчивости приложения для системы, чтобы ANR-диалог никогда не появлялся для пользователя.

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

[Когда срабатывает ANR?]

Обычно система отображает ANR, если приложение не может ответить на ввод пользователя. Например, если приложение блокирует операции ввода-вывода I/O (часто случается при доступе к сети) на потоке обработки интерфейса UI, то система не может обработать события ввода от пользователя. Или возможно приложение тратит слишком много времени, создавая в памяти тщательно продуманную структуру данных в памяти, или вычисляя следующее перемещение в игре на потоке UI. Всегда важно делать эти вычисления эффективными, однако все равно даже самый эффективный код требует некоторого времени для выполнения.

В любых ситуациях, в которых Ваше приложение может производить потенциально длительную операцию, Вы не должны выполнять работу в потоке UI. Вместо этого нужно создать рабочий поток, и выполнять длительные действия и циклы в этом потоке. Это сохранит работоспособность потока UI (который поддерживает цикл обработки событий интерфейса пользователя UI), и предотвратит от того, что система будет считать Ваш код зависшим. Поскольку такая потоковая обработка выполняется на уровне класса, Вы можете думать о скорости отклика как о проблеме класса (сравните с быстродействием традиционного кода, где скорость отклика основывается на уровне быстродействия методов).

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

• Нет ответа на событие ввода (такое как нажатие на кнопку или касание экрана) в течение 5 секунд.
BroadcastReceiver не завершил выполнение в течение 10 секунд.

[Как избежать появления диалога ANR]

Приложения Android обычно работают полностью на одном потоке по умолчанию, который называется "UI thread" (поток интерфейса пользователя) или "main thread" (главный поток). Это означает, что если Ваше приложение делает что-то в потоке UI слишком долго, то может сработать ANR, потому что Ваша программа не дает самой себе шанса обработать событие ввода или внутренние широковещательные сообщения (intent broadcasts).

Таким образом, любой метод, который работает в потоке UI, должен делать в этом потоке как можно меньше работы, насколько это возможно. В частности, активности (см. [2]) должны выполнять как можно действий в ключевых обработчиках своего жизненного цикла, таких как onCreate() и onResume(). Потенциально долгие и непрогнозируемые по длительности операции, такие как работа с сетью или операции с базами данных, или дорогие в плане вычислительных ресурсов расчеты типа изменения размера растрового изображения должны выполняться в отдельном рабочем потоке (или в случае операций с базами данных, через асинхронный запрос).

Наиболее эффективный метод создания рабочего потока для долгих операций - применение класса AsyncTask [4]. Просто расширите (extend) класс AsyncTask, и реализуйте (implement) метод doInBackground(), чтобы выполнить нужную Вам работу. Чтобы передать изменения в процессе работы пользователю (в поток UI), Вы можете вызвать publishProgress(), который запустит метод обратного вызова onProgressUpdate(), так называемый callback method. Из Вашей реализации onProgressUpdate() (которая работает в потоке UI) Вы можете оповещать пользователя о результатах работы потока. Пример:

private class DownloadFilesTask extends AsyncTask < URL, Integer, Long >
{
    // Здесь делайте всю длительную по времени работу.
    protected Long doInBackground(URL... urls)
    {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++)
        {
            totalSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) ((i / (float) count) * 100));
            // Ранний выход, если был вызван cancel().
            if (isCancelled())
               break;
        }
        return totalSize;
    }
// Этот метод будет вызван каждый раз, когда в потоке // выполнится publishProgress(). protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); }
// Этот метод будет вызван, когда doInBackground() завершится. protected void onPostExecute(Long result) { showNotification("Downloaded " + result + " bytes"); } }

Чтобы запустить этот рабочий поток, просто создайте экземпляр класса DownloadFilesTask и вызовите метод execute():

new DownloadFilesTask().execute(url1, url2, url3);

Вы могли бы также захотеть создать собственный класс Thread или HandlerThread, несмотря на то, что это несколько сложнее, чем использование AsyncTask. Если Вы решились на это, то должны установить приоритет потока (thread priority) в значение "background" путем вызова Process.setThreadPriority() и передачи ему константы THREAD_PRIORITY_BACKGROUND. Если Вы не установите приоритет потока в пониженное значение (как здесь рекомендуется), то Вы все еще можете замедлить Ваше приложение, потому что рабочий поток по умолчанию будет работать с тем же приоритетом, что и поток интерфейса UI thread.

Если Вы реализовали Thread или HandlerThread для рабочего потока, то убедитесь, что Ваш UI thread не блокируется, когда он ждет результата рабочего потока, т. е. не вызывайте методов Thread.wait() или Thread.sleep(). Вместо блокирования на ожидании завершения рабочего потока, ваш главный поток UI (main thread) должен предоставить Handler (обработчик) для других потоков, чтобы отправить туда сообщение при их завершении. Разработанное по такому методу приложение позволит потоку UI быть всегда готовым к вводу и это предотвратит появление диалогов ANR, когда истечет таймаут 5 секунд на обработку событий ввода.

Определенное ограничение на время выполнения BroadcastReceiver подчеркивает тот факт, что подразумевается для действий в приемниках широковещательных сообщений: малые по объему дискретные действия в фоновом режиме, такие как установка или регистрация оповещений (registering Notification). Таким образом, как и с другими методами, вызываемыми в потоке интерфейса UI, приложения должны избегать потенциально длительных операций или вычислений в broadcast receiver. Однако вместо того, чтобы выполнять интенсивные вычислительные задачи через рабочие потоки, Ваше приложение должно запустить IntentService, если потенциально долгие действия должны быть предприняты в ответ на широковещательную передачу (intent broadcast).

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

[Как уменьшить время отклика]

Обычно длительность отклика 100 .. 200 мс является порогом, после которого пользователи будут чувствовать замедление приложения. Есть еще некоторые дополнительные подсказки, как избежать ANR и сделать Ваше приложение реагирующим быстрее с точки зрения пользователя:

• Если Ваше приложение делает какую-то работу в фоновом режиме в ответ на ввод пользователя, показывайте индикатор прогресса - показывающий, что что-то делается и/или сколько уже сделано (можно использовать в UI такой виджет как ProgressBar).
• Для игр важно выполнять вычисления для движений в рабочем потоке (не в главном потоке, обрабатывающем интерфейс UI).
• Если Ваше приложение имеет долгую начальную фазу запуска и инициализации, рассмотрите возможность показа splash screen, или выполните отрисовку главного вида окна как можно быстрее, показывая при этом, что загрузка идет, и при этом асинхронно заполните всю нужную информацию. В любом случае показывайте, что работа по выполнению некого действия все же идет, идет успешно и скоро должна закончиться, чтобы пользователь понимал, что происходит, и не чувствовал, что приложение зависло.
• Используйте инструменты оптимизации (performance tools), такие как Systrace и Traceview, чтобы определить узкие места в отзывчивости Вашего приложения.

[Ссылки]

1. Keeping Your App Responsive site:developer.android.com.
2. Класс Activity.
3. Android Beginner: Creating Splash Screen site:onlymobilepro.com.
4Работа с потоками через AsyncTask

 

Добавить комментарий


Защитный код
Обновить

Top of Page