Производительность — один из ключевых параметров мобильного приложения. Твое детище может быть сколь угодно функциональным, красивым и полезным, но если оно тормозит — провал практически гарантирован. К счастью, многих проблем можно избежать, следуя простым правилам и пользуясь подходящими инструментами.
 

Как появляются лаги

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

  • слишком долгая загрузка приложения и отдельных экранов;
  • фризы, когда приложение просто зависает, а через некоторое время появляется сообщение с предложением его убить;
  • проседания FPS, когда вместо плавной прокрутки и анимации пользователь видит слайд-шоу.

Причины всех этих проблем просты: слишком долгое выполнение каких-либо операций, вычислительных или операций ввода-вывода, а вот способы оптимизации разные.

 

Холодный старт

Запуск приложения состоит из нескольких стадий: это инициализация нового процесса, подготовка окна для вывода интерфейса приложения, показ окна на экране и передача управления коду приложения. Далее приложение должно сформировать интерфейс на основе описания в XML-файле, подгрузить с «диска» или из интернета необходимые для корректного отображения интерфейса элементы (битмапы, данные для списков, графиков и прочее), инициализировать дополнительные элементы интерфейса, такие как выдвижное меню (Drawer), повесить на элементы интерфейса колбэки.

Очевидно, что это огромный объем работы и следует приложить все усилия для того, чтобы выполнить ее как можно быстрее. Два главных инструмента в этом деле:

  • отложенная инициализация;
  • параллельный запуск задач.

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

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

Пример: у тебя есть приложение, которое должно показывать на главном экране сводку данных, полученных из интернета. Самый очевидный способ сделать это — получить данные с сервера и после этого отобразить интерфейс. И хотя Android по умолчанию не позволяет выполнять сетевые запросы в основном потоке приложения, вынуждая создавать отдельный поток для получения данных с сервера, большинство разработчиков все равно постараются сделать код последовательным.

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

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

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

Еще одно узкое место: формирование интерфейса из описания лайотов в XML-файле. Когда ты вызываешь метод setContentView() или inflate() объекта LayoutInflater (в коде фрагмента), Android находит нужный лайот в бинарном XML-файле (для эффективности Android Studio упаковывает XML в бинарный формат), читает его, проводит парсинг и на основе полученных данных формирует интерфейс, измеряя и подгоняя элементы интерфейса друг к другу.

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

Другой вариант — перейти на язык Kotlin и использовать библиотеку Anko. Она позволяет описывать интерфейс прямо в коде, благодаря чему скорость отображения интерфейса возрастает в четыре раза, а ты получаешь большую гибкость в управлении логикой формирования интерфейса.

 

Фризы и проседания FPS

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

Продолжение статьи доступно только подписчикам

Cтатьи из последних выпусков журнала можно покупать отдельно только через два месяца после публикации. Чтобы читать эту статью, необходимо купить подписку.

Подпишись на журнал «Хакер» по выгодной цене!

Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта, включая эту статью. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке

3 комментария

Подпишитесь на ][, чтобы участвовать в обсуждении

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

Check Also

Безопасность превыше всего. 9 простых трюков, которые сделают жизнь линуксоида секьюрнее

Жизнь обычных людей складывается из мелочей. Жизнь линуксоида складывается из множества ма…