В этом уроке рассмотрим несколько общих и универсальных советов по производительности.

 

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

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

 

 

notifyDataSetChanged

Когда мы у адаптера RecyclerView вызываем метод notifyDataSetChanged, то адаптер перерисует все элементы списка. Это не очень оптимальный вариант. У нас данные изменились только в одной строке, а мы перерисовываем все 10, 15 или 20 строк.

Чтобы избежать этого, необходимо вместо notifyDataSetChanged использовать notifyItemChanged, notifyItemInserted и прочие notifyItem* методы. Мы указываем им, какой именно элемент списка добавился, изменился или удалился, и адаптер перерисует минимальное количество элементов, чтобы отобразить эти изменения.

Конечно, мы не всегда знаем позиции измененных элементов, чтобы указать их адаптеру. Обычно у нас в адаптере есть текущие данные, из репозитория приходят новые, и нам надо разобраться, какие элементы поменялись, а какие нет. Для этого есть специальный механизм - DiffUtil. У меня на сайте есть подробный материал о нем. DiffUtil сравнит старые и новые данные и сам вызовет все необходимые notifyItem* методы в адаптере.

 

 

RecycledViewPool

Иногда layout представляет из себя много списков с одинаковыми элементами.

 

Например, Google Play Market

 


или Netflix

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

Пример из документации:

class OuterAdapter extends RecyclerView.Adapter<OuterAdapter.ViewHolder> {
    RecyclerView.RecycledViewPool mSharedPool = new RecyclerView.RecycledViewPool();

    ...

    @Override
    public void onCreateViewHolder(ViewGroup parent, int viewType) {
        // inflate inner item, find innerRecyclerView by ID…
        LinearLayoutManager innerLLM = new LinearLayoutManager(parent.getContext(),
                LinearLayoutManager.HORIZONTAL);
        innerRv.setLayoutManager(innerLLM);
        innerRv.setRecycledViewPool(mSharedPool);
        return new OuterAdapter.ViewHolder(innerRv);

    }
    ...
}

В адаптере вертикального RecyclerView создается общий пул mSharedPool и методом setRecycledViewPool передается в каждый горизонтальный RecyclerView.

 

 

ViewStub

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

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

Для такого "ленивого" создания View удобно использовать ViewStub.

Пример использования можно посмотреть в документации.

 

 

include, merge

Если у нас есть набор View, который повторяется в нескольких layout, мы обычно выносим эти View в отдельный layout файл и используем его в основных layout файлах с помощью тега include.

Внутри include файла можно использовать тег merge вместо корневого layout, чтобы не добавлять лишних View в иерархию.

 

 

Плоская иерархия View

Чем больше LinearLayout, FrameLayout и прочих ViewGroup находится на экране, тем ниже производительность. Поэтому рекомендуется использовать ConstraintLayout, который позволяет создавать экраны со сложной структурой, но без кучи вложенных друг в друга ViewGroup.

 

 

VectorDrawable

Для несложных картинок рекомендуется использовать векторную графику. Для отображения таких файлов есть классы VectorDrawable и AnimatedVectorDrawable.

Плюс в том, что вам не надо хранить несколько PNG или JPG mipmap-версий одной картинки. Векторная графика сама нарисует изображение под любое разрешение.

 

 

Nine-patch

Nine-patch позволяет задавать участки картинки, которые будут растягиваться, если картинка должна занять пространства больше, чем ее реальный размер. Часто используется для background изображений для кнопок или текстовых полей.

 

 

inSampleSize

При чтении с диска и отображении больших изображений используйте inSampleSize. Хотя современные библиотеки для работы с изображениями умеют это из коробки и нам уже не нужно ни о чем беспокоиться.

 

 

UI поток

Не выполняйте тяжелые операции в UI потоке, т.к. этот поток используется системой для отрисовки приложения и реагирования на действия пользователя. Если UI поток слишком занят, то приложение будет тормозить.

Чтение файлов, работа с БД, запросы в сеть - все это должно быть вынесено в отдельный поток. Android даже предоставляет инструмент StrictMode, чтобы все это контролировать. О нем я расскажу в отдельном уроке.

Для выполнения задач в фоновом потоке есть, например, следующие инструменты: AsyncTask, Handler, IntentService, Loader, ExecutorService, RxJava

 

 

Память

Про память мы еще очень подробно поговорим в следующих уроках. А пока простой совет - старайтесь создавать поменьше объектов, особенно в циклах, а также в методах типа onBindViewHolder и onDraw. Частое создание объектов ведет к тому, что память переполняется и системе приходится ее чистить. И т.к. процедура чистки может ненадолго приостанавливать работу приложения, то лучше бы выполнять ее как можно реже.

 

 

Tools, not rules

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

 

Курс Производительность

 


Присоединяйтесь к нам в Telegram:

- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование 

- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

- новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме 




Language

Социальные сети

 

Telegram канал



Android чат в Telegram



Группа ВКонтакте



Поддержка проекта

Яндекс
410011180491924

WebMoney
R248743991365
Z551306702056

Paypal