Поддержание отклика приложения

Рисунок 1. Диалоговое ПНО отображаться для пользователя.

Можно написать код, который выиграет все тесты производительности в мире, но все еще остается вялым, зависает или притормаживает в течение значительных периодов, или слишком долго обрабатывает ввод. Самое худшее, что может случиться с отзывчивость вашего приложения это "Приложение не отвечает" (ПНО) диалог.

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

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

Что вызывает ПНО?

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

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

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

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

Как избежать ПНО

Приложения Android обычно работают исключительно на одном потоке по умолчанию называемом "поток пользовательского интерфейса" или "основной поток". Это означает, что всё что угодно в вашем приложении, выполняемое в потоке пользовательского интерфейса, что занимает много времени для завершения, может вызвать диалоговое окно ПНО поскольку приложение не дает себе шанса для обработки событий ввода или рассылки намерений.

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

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

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    // Do the long-running work in here
    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));
            // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }

    // This is called each time you call publishProgress()
    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }

    // This is called when doInBackground() is finished
    protected void onPostExecute(Long result) {
        showNotification("Downloaded " + result + " bytes");
    }
}

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

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

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

При реализации Thread или HandlerThread, убедитесь, что ваш поток пользовательского интерфейса не блокируется, ожидая завершения рабочего потока — не вызывайте Thread.wait() или Thread.sleep(). Вместо блокировки в ожидании завершения рабочего потока, основной поток должен предоставить Handler для других потоков для обратной передачи данных после завершения. Проектирование приложения таким образом позволит потоку пользовательского интерфейса вашего приложения продолжать реагировать на ввод и таким образом избежать ПНО диалогов, вызванных ожиданием обработки входного события в течении 5 секунд .

Специальное ограничение на BroadcastReceiver (на время выполнения) подчеркивает, что вещательные приемники предназначены для выполнения: маленьких, дискретных объемов работ в фоновом режиме, таких как сохранение настроек меню и регистрации Notification. Поэтому с другими методами вызываемыми в потоке пользовательского интерфейса, приложения должны избегать потенциально длительных операций или вычислений в обработчиках вещательных событий. Но вместо того, чтобы делать ресурсоемкие задачи с помощью рабочих потоков, приложение должно запустить IntentService если потенциально длительные действия должны быть выполнены в ответ на трансляцию намерения.

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

Укрепление быстроты ответов

Как правило, от 100 до 200мс это порог, за которым пользователи будут воспринимать замедление в приложении. Таким образом, вот некоторые дополнительные советы кроме того что нужно сделать, чтобы избежать ПНО, и сделать ваше приложение с хорошим откликом в глазах пользователя:

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