Fandroid.info — Уроки по разработке андроид-приложений
Продвинутый курс по созданию android-приложения Radio App на языке Kotlin с Jetpack Compose
15 4 430
В этом курсе вы научитесь, как создать андроид-приложение для прослушивания лучших мировых интернет-радиостанций. В процессе прохождения нашего курса вы создадите свое мобильное приложение и сможете опубликовать его! Не
АКЦИЯ! Два Продвинутых курса по цене одного
16 11 347
Подписывайтесь на любой Продвинутый курс и получите еще один в подарок! UPD: Акция продлена! Подпишитесь на любой Продвинутый курс по разработке андроид-приложений и игр и получите еще
Урок 19. Как создать андроид-приложение с вкладками – TabLayout с ViewPager2 на Kotlin
0 9 917
На прошлом уроке мы познакомились с ViewPager2 и создали андроид-приложение, в котором можно листать экраны свайпом вправо или влево.
Урок 18. Как создать слайдер экранов с использованием ViewPager2 на Kotlin
0 9 522
На прошлом уроке мы познакомились с нижней панелью навигации BottomNavigationView. В этом уроке реализуем андроид-приложение, в котором можно будет листать экраны свайпом вправо или влево. Для реализации слайдера
Обновление до Android 12, MAD Skills WorkManager, AndroidX, и другие новости в выпуске Now in Android 37
0 1 586
Добро пожаловать в Now in Android, ваше текущее руководство по новинкам и важным событиям в мире разработки Android. В этом эпизоде Chet Haase охватывает обновление на Android 12,
Урок 17. Android Navigation. Знакомство с BottomNavigationView. Как добавить фрагменты в панель Bottom Navigation.
0 18 905
На прошлом уроке мы работали с анимацией переходов между экранами. На этом уроке познакомимся с нижней панелью навигации BottomNavigationView, которая позволяет переходить между экранами – пунктами назначения навигации,
Урок 16. Android Navigation. Анимация переходов между пунктами назначения. Transition Framework & Animation Framework
0 6 197
На прошлом уроке мы обменивались данными между экранами-пунктами назначения. В этом уроке реализуем анимацию переходов между экранами. Transition Framework & Animation Framework В первой части этого урока мы
Урок 15. Передача данных между экранами — пунктами назначения. Android Navigation. Bundle vs Safe Args
2 11 031
Продолжаем серию уроков по разработке android-приложений в Android Studio на языке Kotlin. На прошлом уроке мы выполняли навигацию по условию, авторизован пользователь или нет. На этом уроке На
Урок 14. Навигация по условию в андроид приложении. Android Conditional Navigation & Firebase Authentication
0 5 724
Продолжаем серию уроков по разработке android-приложений в Android Studio на языке Kotlin. Пришло время познакомиться с Android Conditional Navigation и Firebase Authentication. На прошлом уроке мы научились работать
Hilt: кратчайшее руководство по фреймворку DI для Android
0 9 116
В поисках надежной, но простой структуры внедрения зависимостей (DI), не так давно я пришел к выводу, что Koin — это структура DI, которая отвечает моим требованиям. Также, на
VPN-расширение для браузера – почему важно использовать и какие в нем преимущества
0 739
Сегодня большинство Интернет-пользователей задаются вопросом о том, зачем им нужно применять VPN-расширение для браузера. Первый и главный мотив – это обезопасить свой трафик в сети и предотвратить слежку.
Урок 13. Навигация в Android приложении. Интеграция в новый проект, добавление пунктов назначения (destinations) и переходов между ними (actions)
0 8 925
Продолжаем серию уроков по разработке android-приложений в Android Studio на языке Kotlin. На прошлом уроке мы познакомились с библиотекой Navigation Architecture Component, которая позволяет пользователям перемещаться между различными
Бесплатный курс Python
1 2 338
Python – один из самых популярных языков программирования. Программы, написанные на нем, могут работать практически на всех известных операционных системах, для которых написаны интерпретаторы языка. Благодаря лаконичному синтаксису,
Сейчас в Android # 12
0 1 872
Перевод выпуска Now in Android #12 от официалов. Теперь в Android видео + подкаст, релизы AndroidX, Dynamic Feature Modules в Jetpack Navigation, статьи о встроенных классах Kotlin и Android
Улучшение времени запуска приложений на Android: уроки Facebook
Улучшение времени запуска приложения — нетривиальная задача и требует глубокого понимания вещей, которые на нее влияют. В этом году команда Google Android и команда приложения Facebook совместно работали над метриками и обменивались подходами для улучшения запуска приложений. В общедоступной документации Google Android содержится много информации об оптимизации запуска приложений. В дополнение к этому мы хотим поделиться тем, как это применялось к приложению Facebook и что помогло им улучшить запуск приложения.
Сейчас Facebook ежемесячно используют более 2.9 миллиарда человек. Facebook помогает людям создавать сообщества и сближает мир. Это место, где люди могут делиться жизненными моментами, узнавать и обсуждать происходящее, налаживать и развивать отношения, а также вместе работать над созданием экономических возможностей.
Разработчики приложений Facebook стремятся к тому, чтобы у людей был лучший опыт и чтобы приложение безупречно работало на всех устройствах в любой стране и в различных сетевых условиях. Работая вместе, команда Google Android и команда Facebook согласовали определение метрик запуска приложений и лучшие практики, и поделились ими в этой статье.
С чего начать
Начните с измерения времени запуска. Это позволит вам узнать, насколько хорош ваш пользовательский опыт при запуске, отслеживать любые изменения, а также понять, сколько стоит вложить в улучшения. В конце концов, время запуска должно быть привязано к удовлетворенности пользователей, их вовлеченности или росту пользовательской базы, чтобы расставить приоритеты для ваших инвестиций.
Android определяет две метрики для измерения времени запуска приложения: время до полного отображения (Time-To-Full-Display, TTFD) и время до начала отображения (Time-To-Initial-Display, TTID). Хотя вы можете дополнительно разделить его на время холодного/теплого старта, этот пост не делает однозначного различия между ними — подход Facebook состоит в том, чтобы измерить и оптимизировать время запуска, которое переживают все пользователи, взаимодействующие с приложением (некоторые из запусков будут холодными, некоторые теплыми).
Время до полного отображения, Time-To-Full-Display
TTFD фиксирует время, когда ваше приложение завершило рендеринг и готово к взаимодействию с пользователем и работе, возможно, включая загрузку контента из хранилища или сети. Это может занять некоторое время в медленных сетях и может зависеть от того, на какой экран попадают ваши пользователи. Таким образом, может быть полезно показать что-то сразу и позволить пользователям увидеть, что прогресс идет, что подводит нас к TTID…
Время до начального отображения, Time-To-Initial-Display
TTID фиксирует время, в течение которого ваше приложение отрисовывает фон, навигацию, любой быстро загружаемый локальный контент, заполнители для более медленного локального контента или контента, поступающего из сети. TTID показывает, когда пользователи могут перемещаться и добираться туда, куда они хотят.
Не меняйте слишком много: одна вещь, на которую следует обратить внимание — это визуальное переключение контента вашего приложения между TTID и TTFD. Например, можно показывать кэшированное содержимое, а затем отключать его, когда поступает сетевой контент. Это может раздражать и расстраивать пользователей, поэтому убедитесь, что в TTID есть достаточно значимого контента, чтобы показать пользователям как можно больше того, чего стоит ожидать от TTFD.
Сосредоточьтесь на успехе пользователей
Пользователи обращаются к вашему приложению за контентом, загрузка которого может занять некоторое время, и вы хотите доставить им этот контент как можно быстрее.
Разработчики приложений Facebook сосредотачиваются на метрике, основанной на времени до полного отображения (TTFD), в которой есть весь контент и изображения, потому что это и есть полный опыт того, ради чего пользователи пришли в приложение. Если сетевой запрос контента или изображений занимает много времени или терпит неудачу, разработчики хотят знать об этом, чтобы они могли улучшить в процессе запуска.
Какова хорошая цель для TTID и TTFD?
Метрика запуска Facebook — это процент запусков приложения, которые они считают «плохими», то есть любой запуск, для которого TTFD превышает 2. 5 секунды, ИЛИ любая часть запуска, которая не удалась (например, не удается загрузить изображение или приложение дает сбой). Facebook фокусируется на снижении этого процента неудачных запусков либо путем улучшения успешных запусков, которые занимают более 2.5 секунд, либо путем устранения проблем, вызывающих неудачные запуски. Метрика в 2.5 секунды была выбрана на основе исследования, которое показало, что это имеет значение для пользователей Facebook (это также соответствует метрике Largest Contentful Paint (LCP) в рекомендациях Web Vitals для веб-сайтов).
Включение полного опыта в TTFD, особенно любых сетевых вызовов для получения недавнего контента, может сделать ваш показатель запуска очень медленными по сравнению с TTID. На самом деле это хорошо! Он представляет реальный опыт пользователей вашего приложения. Усовершенствования, которые вы вносите в это приложение, могут способствовать более активному использованию и восприятию пользователями производительности вашего приложения, как это было в Facebook.
Измерение TTFD может быть сложной задачей в зависимости от вашего приложения. Если это слишком сложно, можно начать с Time To Initial Display. Это может привести к потере данных о производительности загрузки части вашего контента, если у вас есть заполнители или изображения, но хорошо начать с чего-то, даже если это всего лишь часть того, что ваши пользователи видят при взаимодействии с вашим приложением каждый день.
Инструментарий TTID
В Android 4.4 (уровень API 19) и выше logcat предоставляет значение Displayed, фиксирующее время, прошедшее между запуском процесса и завершением отрисовки первого кадра соответствующей Activity на экране.
Строка лога выглядит примерно так:
ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms
Инструментарий TTFD
Чтобы использовать TTFD, вызовите reportFullyDrawn() в Activity после того, как весь ваш контент появится на экране. Не забудьте включить любой контент, который заменяет заполнители, а также любые изображения, которые вы визуализируете (обязательно учитывайте, когда отображается само изображение, а не только его плейсхолдер). Как только вы используете reportFullyDrawn(), вы можете увидеть это в logcat:
ActivityManager: Fully drawn {package}/.MainActivity: +1s54ms
Рекомендации разработчиков Facebook
Разработчики приложений Facebook уже много лет оптимизируют приложение для миллиардов пользователей на множестве устройств, платформ и стран. В этом разделе рассказывается о некоторых ключевых уроках, которые разработчики приложений Facebook применили для оптимизации запуска своих приложений.
- Сначала поймите, потом оптимизируйте. После того, как вы определили хорошую метрику для запуска, ее использование в приложении позволит вам понять и расставить приоритеты в улучшении эффективности запуска, чтобы обеспечить лучший опыт для пользователей. Начав с измерения, вы сможете понять, что есть возможность, вы можете определить, на чем сосредоточить свои усилия, и вы увидите, насколько вы улучшили ситуацию, когда начнете оптимизацию.
- Сначала исправляйте сбои. После того, как вы измерили запуск, убедитесь, что ваше приложение запускается надежно. Сбои во время запуска — самый неприятный и самый быстрый способ заставить пользователей покинуть ваше приложение. Измерьте и устраните их в первую очередь.
- Не забывайте о функциональной надежности — ваше приложение показывает какой-то контент быстро, но не загружает весь контент или он загружается много времени? Ваше приложение может запускаться быстро, но не работать так, как хочет клиент (например, если нажатие кнопки не работает), это ухудшает пользовательский опыт.
- Стремитесь к согласованности — непостоянная производительность больше разочаровывает, чем медленная, но постоянная. Взгляните на “длинный хвост” своих запусков и посмотрите, есть ли какие-либо исправления или способы смягчить эти медленные запуски. Не забывайте следить за запусками без сети в офлайн-режиме и за запусками в сетях с потерями.
- Распараллеливание работы
- Будь «ленивы» — как только у вас будет надежный и стабильный запуск, просмотрите все, что вы делаете, чтобы отобразить свой первый видимый экран с контентом — есть ли там какая-то работа, в которой нет необходимости? Удалите, отложите или переместите в фоновый режим любую работу, которая не связана напрямую с запуском приложения, до тех пор, пока приложение не будет запущено (но будьте осторожны и следите за отзывчивостью вашего приложения в качестве контрметрики). Постарайтесь сделать onCreate() вашего приложения максимально легким. Вы также можете воспользоваться библиотекой Jetpack App Startup для инициализации компонентов при запуске приложения. При этом убедитесь, что загружены все необходимые модули для старта Activity и нет изменений там, где становятся доступными lazily-loaded модули.
- Демонстрируйте прогресс, но не меняйте интерфейс слишком сильно. Старайтесь не слишком сильно менять то, что предлагается пользователям во время запуска. Прискорбно пытаться нажимать на что-то только для того, чтобы это потом изменилось. Это похоже на концепцию Cumulative Layout Shift (CLS) из Web Vitals. Для сетевых загрузок с неопределенной продолжительностью закройте сплеш-скрин и покажите заполнители для асинхронной загрузки. Рассмотрите возможность применения анимации к области содержимого, отражающей состояние загрузки. Убедитесь, что структура загруженного содержимого максимально соответствует скелетной структуре, чтобы обеспечить плавный переход к ней после загрузки содержимого.
- Кэшируйте — когда пользователь впервые открывает ваше приложение, вы можете отобразить индикаторы загрузки для некоторых элементов пользовательского интерфейса. В следующий раз, когда пользователь зайдет в ваше приложение, вы можете показать кэшированное содержимое при загрузке более свежего контента. Вы когда-нибудь видели обновление вашего фида FB после загрузки вашего приложения, когда мы получаем обновленный контент из сети? Сокращение времени сетевых запросов в вашем старте — отличный способ ускорить процесс и обеспечить более стабильную производительность при запуске. Однако показ кэшированного содержимого не всегда может быть лучшим подходом, как описано в следующем пункте, и поэтому важно определить, что лучше работает для клиента.
- Работайте быстро и медленно — немного более медленный, свежий и релевантный контент может быть лучше, чем быстро устаревший контент. Показ свежего контента вашим пользователям может быть более ценным, чем сверхбыстрый запуск только для обновления контента вскоре после запуска. Оцените, лучше ли оптимизировать показ свежего контента как можно быстрее с тайм-аутом для показа устаревшего контента, если сеть медленная, или просто сразу показать то, что доступно, если сеть отключена.
- Последовательность для начала сеанса — может оказаться полезным сбросить пользователей до основного контента после того, как ваше приложение долгое время находится в фоновом режиме. Устройства могут сохранять ваше приложение в памяти в течение длительного времени.
- Посмотрите на внутреннюю работу. Отследите и посмотрите, что выполняется во время запуска, или подключите отладчик — вы можете быть удивлены тем, что обнаружите! Как только вы получите хорошее представление о старте, вы сможете эффективно оптимизировать производительность своего приложения. Так вы сможете инвестировать свои усилия в свои самые большие возможности, потому что вы будете знать, где они.
- Упростите выполнение правильных действий. Иногда разработчики используют плохие шаблоны и архитектуру, потому что существует слишком много способов сделать что-то. Не бойтесь объединить шаблоны, используемые в вашем приложении, и оптимизировать их, чтобы было легко выбрать, как выполнить задачу, и чтобы эта задача была эффективной. Хорошим примером этого могут быть шаблоны “нетерпеливого” выполнения кода. Если вы на старте запускаете код для контента, который появляется только после первой полноэкранной отрисовки, вы по определению снижаете производительность. Ленивое выполнение кода — хороший шаблон. Активно запускайте код только тогда, когда он критически блокирует ваш запуск.
Рекомендации от команды Google Android
Рекомендации команды Google Android по измерению и оптимизации запуска приложений доступны в общедоступных документах: App startup time. В этом разделе обобщены некоторые ключевые моменты, которые связаны с приведенными выше рекомендациями Facebook, которые следует учитывать всем разработчикам приложений для Android.
- TTID и TTFD — важные метрики для запуска приложения. Google Android показывает TTID в Play Console. TTFD — это расширенная метрика TTID, поэтому любые улучшения TTID должны применяться к обоим показателям.
- Вызовите reportFullyDrawn(), чтобы получить TTFD и сообщить системе, что рендеринг вашей Activity завершен. Чтобы улучшить запуск приложения, система Android настраивает оптимизацию, чтобы расставить приоритеты для задач, которая выполняются до вызова reportFullyDrawn(). Вызов этого метода, когда ваше приложение находится в полностью пригодном для использования состоянии, сократит время запуска вашего приложения. Каждое приложение должно использовать этот API! И не забудьте измерять этот показатель.
- Мониторинг технических характеристик вашего приложения с помощью Android Vitals поможет вам улучшить запуск вашего приложения. Используя Play Console, вы можете просматривать данные, которые помогут вам понять и сократить время запуска вашего приложения и многое другое.
- Мы знаем, что исправление ошибки в рабочей среде намного дороже, чем исправление во время разработки. То же самое и с производительностью. Настройте свое приложение для измерения запуска приложений на раннем этапе с помощью локальных тестов производительности с помощью Jetpack Macrobenchmark: Startup.
- Как мы уже говорили выше, измерения — это ключ к пониманию и оптимизации старта. Android предлагает system tracing, который может помочь глубже изучить и диагностировать проблемы с запуском приложений.
- Библиотека Jetpack App startup обеспечивает простой и эффективный способ инициализации компонентов при запуске приложения. И разработчики библиотек, и разработчики приложений могут использовать эту библиотеку для оптимизации последовательностей запуска и явного задания порядка инициализации. Вы можете использовать эту библиотеку, чтобы указать, какие компоненты в какие моменты запуска загружаются.
- Типичная проблема, которая влияет на запуск приложения, — это слишком много действий во время инициализации. Например, создание больших или сложных макетов, блокировка отрисовки экрана, загрузка и декодирование растровых изображений, сборка мусора и т.д.
Резюме
В этой статье описаны некоторые ключевые показатели запуска и передовые практики по улучшению опыта, которые помогают улучшить вовлеченность пользователей приложения Facebook для Android. В ней также представляются метрики, библиотеки и инструменты, рекомендованные командой Google Android. Любое приложение для Android выиграет от применения некоторых из стратегий, описанных в статье. Измерьте и сделайте запуск вашего приложения приятным и быстрым для ваших пользователей!
Источник
Если вы нашли опечатку — выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor. ru.
Уроки приложений Google, часть 2
Определите и измерьте эти транзакции, чтобы понять:
- Сколько времени это займет и является ли это ожидаемым и необходимым?
- Является ли основной поток спящим/бездействующим/заблокированным в это время? Если да, это может быть узким местом производительности.
- Можно ли отложить это?
- Разумно используйте синтаксический анализ XML и Json: Приложение Gboard оптимизировало синтаксический анализ списка файлов, используя код Java вместо синтаксического анализа XML, поскольку они анализировали их в памяти как объекты Java во время выполнения. Итак, GBoard проделал работу по преобразованию всего XML в код Java во время компиляции, затем задержка стала временем загрузки класса, что намного быстрее, почти на ⅕ предыдущего. Сравнительный анализ синтаксического анализа XML и Json для вашего приложения, чтобы определить подходящую библиотеку для использования.
- Проанализируйте и устраните серьезные конфликты при чтении диска: Чтобы зафиксировать это, используйте StrictMode в среде разработки.
- Обнаруживает случайный доступ к диску или сети в основном потоке приложения, где принимаются операции пользовательского интерфейса и выполняются анимации.
- Может автоматически завершать работу приложения (или регистрировать его в logcat), когда произошло нарушение, путем добавления различных наказаний.
Оптимизировать размер приложения
Пользователи часто избегают загрузки приложений, которые кажутся слишком большими, особенно на развивающихся рынках, где устройства подключаются к нестабильным сетям 2G и 3G с низкой пропускной способностью или работают по тарифным планам с побайтной оплатой.
- Удаление ненужных макетов: Команды Gboard и Camera из Google проверили макеты, которые не использовались или могли быть объединены с небольшими изменениями пользовательского интерфейса, и удалили ненужные макеты, уменьшив общий размер кода приложения.
- При необходимости перейти на динамические макеты/представления: Приложения углубляются и находят макеты и представления, которые могут быть динамически отображены. Они использовали слияние и viewstub для дальнейшей оптимизации своих представлений и макетов.
- Переоцените функции с низким DAU. Попробуйте отключить функции, которые занимают больше памяти и снижают производительность приложения: Команда дополнительно проанализировала свои приложения, специально оптимизировав их для Android Go, и отключила функции на устройствах Go, которые мало использовались, но занимали много памяти. Они удалили сложные анимации, большие GIF-файлы и т. д., чтобы освободить место для частей приложения.
- Попробуйте объединить собственные двоичные файлы с общими зависимостями в один: Если приложение имеет разные реализации JNI с большим количеством общих базовых зависимостей, все разные двоичные файлы составляют размер APK с избыточными компонентами. Приложение Camera от Google выиграло от объединения нескольких двоичных файлов JNI в один двоичный файл JNI, сохраняя при этом отдельные файлы Java и JNI. Это помогло приложению уменьшить размер apk на несколько Мб .
- Попробуйте уменьшить размер кода dalvik: Проверьте код, который никогда не используется во время выполнения, например большие классы и автоматически сгенерированный код.
- Оптимизаторы кода, такие как ProGuard(), могут помочь оптимизировать и уменьшить размер кода, но они не могут работать с кодами, защищенными константами времени выполнения. Замена проверки/флагов константами времени компиляции, чтобы максимально использовать инструменты оптимизации.
- Уменьшить размер переводимых строк:
а. Не переводите строки пользовательского интерфейса только для внутреннего использования. Пометить их как переводимые = «ложь».
б. Удалить неиспользуемые альтернативные ресурсы : вы можете использовать свойство resConfigs плагина Android Gradle для удаления альтернативных файлов ресурсов, которые не нужны вашему приложению. если вы используете библиотеку, включающую языковые ресурсы (например, AppCompat или Google Play Services), то ваше приложение включает все переведенные языковые строки для сообщений в этих библиотеках, независимо от того, переведена ли остальная часть вашего приложения на те же языки или нет. Если вы хотите оставить только те языки, которые официально поддерживаются вашим приложением, вы можете указать эти языки с помощью свойства resConfig. Любые ресурсы для неуказанных языков удаляются.
Следующий фрагмент показывает, как ограничить ваши языковые ресурсы только на английском и французском
26
26. «en», «fr»
}
}
Подробнее читайте здесь.
в. Не переводите то, что не нуждается в переводе : Если строка не участвует в пользовательском интерфейсе, ее не следует переводить. Строки для целей отладки, сообщений об исключениях, URL-адресов и т. д. должны быть строковыми литералами в коде, а не ресурсами.
и. Все равно не переводите то, что не показывается пользователю
Можно иметь строковый ресурс, который практически никогда не показывается пользователю, но на который все равно часто ссылаются. Один пример находится в вашем
, если у вас есть набор android:label и ссылка на ресурс String, но метка Activity никогда не отображается на самом деле (например, это не Activity Launcher и у него нет панели приложений, которая показывает свою собственную этикетка).
д. Не переводите URL -адреса: Рассмотрим этот пример:
Вы можете распознать < и > — это escape-символы для «<» и «>». Здесь они необходимы, потому что если вы поместите тег внутри тега, то компилятор ресурсов Android просто их удалит (как это происходит со всеми тегами, которые он не распознает). Однако это означает, что вы переводите теги HTML и URL-адрес на 78 языков . Совершенно ненужный.
Вместо этого удалите часть с помощью HTML:
Это устройство не поддерживает Android Auto.
Обратите внимание, что мы не определяем отдельную строку для «Подробнее», потому что это обычная строка. Чтобы создать фрагмент HTML для ссылки, мы определяем « Но ресурсы так не работают: если у вас есть идентичная строка под другим именем, то, если обе они не будут переведены одинаково на 78 языков (маловероятно), вы получите дубликаты.
Не повторять строки!
г. Не используйте отдельные строки для ВСЕХ ЗАГЛАВНЫХ букв или регистра заголовков: Android имеет встроенную поддержку изменения регистра.
Используйте android:capitalize (начиная с уровня API 1). Если установлено, указывает, что этот TextView имеет текстовый метод ввода и должен автоматически использовать заглавные буквы при вводе пользователем. По умолчанию «нет».
- Уменьшение размера ресурсов: Помните о различных форм-факторах целевых устройств, поддерживаемых вашим приложением, и соответствующим образом корректируйте ресурсы.
- Загрузите свое приложение с помощью пакетов приложений Android: Самый простой способ получить немедленную экономию размера приложения при публикации в Google Play – это загрузить свое приложение в виде набора приложений Android. Это новый формат загрузки, который включает все ваши приложения. скомпилированный код и ресурсы, но откладывает создание и подписание APK в Google Play. Подробнее здесь.
- Используйте функцию динамической доставки, если применимо: Play Feature Delivery использует расширенные возможности наборов приложений, позволяя доставлять определенные функции вашего приложения условно или загружать по запросу. Вы можете использовать функциональные модули для пользовательской доставки. Уникальным преимуществом функциональных модулей является возможность настроить способ и время загрузки различных функций вашего приложения на устройства под управлением Android 5.0 (уровень API 21) или выше. Узнайте больше здесь.
Резюме
В этой части блога собраны некоторые передовые практики, рекомендации и выводы из приложений Google, которые помогут оптимизировать размер вашего приложения, уменьшить задержку при запуске и улучшить взаимодействие с приложением Go, что поможет привлечь пользователей и принять ваше приложение для Android. В части 3 вы познакомитесь с инструментами, которые помогли приложениям Google выявлять и устранять такие проблемы с производительностью в своем приложении!
Автор: Нихарика Арора, инженер по связям с разработчиками
При сборке для Android Go особое внимание уделяется оптимизации производительности и использованию ресурсов.
В части 1 этого блога мы обсудили, почему разработчикам следует подумать о сборке для Android Go, несколько советов по оптимизации памяти приложения и определили стандартный подход, которого следует придерживаться при устранении проблем с производительностью. В этом блоге мы поговорим о других важных моментах, на которые следует обратить внимание при создании приложений для Android Go.
Оптимизируйте свои приложения для Android Go
Уменьшить задержку запуска
Улучшение времени запуска приложения требует глубокого понимания того, что на него влияет.
Если есть задачи, которые занимают больше времени и блокируют основной поток, попробуйте переместить их в фоновый поток или предпочтите использовать WorkManager.
- Проверка инициализации сторонних библиотек
Ленивая загрузка сторонних библиотек. Многие библиотеки имеют инициализацию по запросу или отключают параметры автоматической инициализации.
- Проверить время рендеринга изображений webp / png
- Предпочитать изображения webp jpg/png
- Предпочитать svg для маленьких иконок.
- Проверить, не видны ли какие-либо макеты, но все равно тратить время на загрузку изображений.
- Исследование с использованием изображения с низким разрешением в соответствии с возможностями памяти устройства.
- Удаление ненужного фона/альфа-канала из представлений.
- Избегайте синхронных IPC в потоке пользовательского интерфейса: Проверка транзакций связывателя, удерживающих основной поток занятым: часто в приложении происходит несколько межпроцессных взаимодействий. Это может быть что угодно, например, загрузка изображений/активов, загрузка сторонних библиотек, тяжелая работа над основным потоком приложения, например, доступ к диску или сети и т. д. StrictMode — полезный инструмент разработчика для обнаружения такого случайного использования.
Определите и измерьте эти транзакции, чтобы понять:
- Сколько времени это занимает и является ли это ожидаемым и необходимым?
- Является ли основной поток спящим/бездействующим/заблокированным в это время? Если да, это может быть узким местом производительности.
- Можно ли отложить это?
- Разумно используйте синтаксический анализ XML и Json: Приложение Gboard оптимизировало синтаксический анализ списка файлов, используя код Java вместо синтаксического анализа XML, поскольку они анализировали их в памяти как объекты Java во время выполнения. Итак, GBoard проделал работу по преобразованию всего XML в код Java во время компиляции, затем задержка стала временем загрузки класса, что намного быстрее, почти на ⅕ предыдущего. Сравнительный анализ синтаксического анализа XML и Json для вашего приложения, чтобы определить подходящую библиотеку для использования.
- Проанализируйте и устраните серьезные конфликты при чтении диска: Чтобы зафиксировать это, используйте StrictMode в среде разработки.
- Обнаруживает случайный доступ к диску или сети в основном потоке приложения, где принимаются операции пользовательского интерфейса и выполняются анимации.
- Может автоматически завершать работу приложения (или регистрировать его в logcat), когда произошло нарушение, путем добавления различных наказаний.
Оптимизировать размер приложения
Пользователи часто избегают загрузки приложений, которые кажутся слишком большими, особенно на развивающихся рынках, где устройства подключаются к нестабильным сетям 2G и 3G с низкой пропускной способностью или работают по тарифным планам с побайтной оплатой.
- Удаление ненужных макетов: Команды Gboard и Camera из Google проверили макеты, которые не использовались или могли быть объединены с небольшими изменениями пользовательского интерфейса, и удалили ненужные макеты, уменьшив общий размер кода приложения.
- При необходимости перейти на динамические макеты/представления: Приложения углубляются и находят макеты и представления, которые могут быть динамически отображены. Они использовали слияние и viewstub для дальнейшей оптимизации своих представлений и макетов.
- Переоцените функции с низким DAU. Попробуйте отключить функции, которые занимают больше памяти и снижают производительность приложения: Команда дополнительно проанализировала свои приложения, специально оптимизировав их для Android Go, и отключила функции на устройствах Go, которые мало использовались, но занимали много памяти. Они удалили сложные анимации, большие GIF-файлы и т. д., чтобы освободить место для частей приложения.
- Попробуйте объединить собственные двоичные файлы с общими зависимостями в один: Если приложение имеет разные реализации JNI с большим количеством общих базовых зависимостей, все разные двоичные файлы составляют размер APK с избыточными компонентами. Приложение Camera от Google выиграло от объединения нескольких двоичных файлов JNI в один двоичный файл JNI, сохраняя при этом отдельные файлы Java и JNI. Это помогло приложению уменьшить размер apk на несколько Мб .
- Попробуйте уменьшить размер кода dalvik: Проверьте код, который никогда не используется во время выполнения, например большие классы и автоматически сгенерированный код.
- Оптимизаторы кода, такие как ProGuard(), могут помочь оптимизировать и уменьшить размер кода, но они не могут работать с кодами, защищенными константами времени выполнения. Замена проверки/флагов константами времени компиляции, чтобы максимально использовать инструменты оптимизации.
- Уменьшить размер переводимых строк:
а. Не переводите строки пользовательского интерфейса только для внутреннего использования. Пометить их как переводимые = «ложь».
б. Удалить неиспользуемые альтернативные ресурсы : вы можете использовать свойство resConfigs плагина Android Gradle для удаления альтернативных файлов ресурсов, которые не нужны вашему приложению. если вы используете библиотеку, включающую языковые ресурсы (например, AppCompat или Google Play Services), то ваше приложение включает все переведенные языковые строки для сообщений в этих библиотеках, независимо от того, переведена ли остальная часть вашего приложения на те же языки или нет. Если вы хотите оставить только те языки, которые официально поддерживаются вашим приложением, вы можете указать эти языки с помощью свойства resConfig. Любые ресурсы для неуказанных языков удаляются.
Следующий фрагмент показывает, как ограничить ваши языковые ресурсы только на английском и французском
26
26. «en», «fr»
}
}
Подробнее читайте здесь.
в. Не переводите то, что не нуждается в переводе : Если строка не участвует в пользовательском интерфейсе, ее не следует переводить. Строки для целей отладки, сообщений об исключениях, URL-адресов и т. д. должны быть строковыми литералами в коде, а не ресурсами.
и. Все равно не переводите то, что не показывается пользователю
Можно иметь строковый ресурс, который практически никогда не показывается пользователю, но на который все равно часто ссылаются. Один пример находится в вашем
, если у вас есть набор android:label и ссылка на ресурс String, но метка Activity никогда не отображается на самом деле (например, это не Activity Launcher и у него нет панели приложений, которая показывает свою собственную этикетка).
д. Не переводите URL -адреса: Рассмотрим этот пример:
Вы можете распознать < и > — это escape-символы для «<» и «>». Здесь они необходимы, потому что если вы поместите тег внутри тега, то компилятор ресурсов Android просто их удалит (как это происходит со всеми тегами, которые он не распознает). Однако это означает, что вы переводите теги HTML и URL-адрес на 78 языков . Совершенно ненужный.
Вместо этого удалите часть с помощью HTML:
Это устройство не поддерживает Android Auto.
Обратите внимание, что мы не определяем отдельную строку для «Подробнее», потому что это обычная строка. Чтобы создать фрагмент HTML для ссылки, мы определяем « Но ресурсы так не работают: если у вас есть идентичная строка под другим именем, то, если обе они не будут переведены одинаково на 78 языков (маловероятно), вы получите дубликаты.
Не повторять строки!
г. Не используйте отдельные строки для ВСЕХ ЗАГЛАВНЫХ букв или регистра заголовков: Android имеет встроенную поддержку изменения регистра.
Используйте android:capitalize (начиная с уровня API 1). Если установлено, указывает, что этот TextView имеет текстовый метод ввода и должен автоматически использовать заглавные буквы при вводе пользователем. По умолчанию «нет».
- Уменьшение размера ресурсов: Помните о различных форм-факторах целевых устройств, поддерживаемых вашим приложением, и соответствующим образом корректируйте ресурсы.
- Загрузите свое приложение с помощью пакетов приложений Android: Самый простой способ получить немедленную экономию размера приложения при публикации в Google Play – это загрузить свое приложение в виде набора приложений Android. Это новый формат загрузки, который включает все ваши приложения. скомпилированный код и ресурсы, но откладывает создание и подписание APK в Google Play. Подробнее здесь.
- Используйте функцию динамической доставки, если применимо: Play Feature Delivery использует расширенные возможности наборов приложений, позволяя доставлять определенные функции вашего приложения условно или загружать по запросу. Вы можете использовать функциональные модули для пользовательской доставки. Уникальным преимуществом функциональных модулей является возможность настроить способ и время загрузки различных функций вашего приложения на устройства под управлением Android 5.0 (уровень API 21) или выше. Узнайте больше здесь.
Резюме
В этой части блога собраны некоторые передовые практики, рекомендации и выводы из приложений Google, которые помогут оптимизировать размер вашего приложения, уменьшить задержку при запуске и улучшить взаимодействие с приложением Go, что поможет привлечь пользователей и принять ваше приложение для Android. В части 3 вы познакомитесь с инструментами, которые помогли приложениям Google выявлять и устранять такие проблемы с производительностью в своем приложении!
Уроки создания виджетов для Android — для мобильных устройств (2022)
Мэтт Боуэн, Джеймс Локхарт, Сесилия Хунка и Карлос Перейра
Когда было объявлено о новом виджете для iOS 14, наша команда iOS приступила к разработке интерфейса для использования новой платформы. Однако виджеты не новы для Android и существуют уже более десяти лет. Shopify глубоко заботится о своем мобильном опыте, и пока у нас есть мобильное приложение Shopify, наши команды Android и iOS поставляют каждую функцию индивидуально в унисон. Теперь, когда в центре внимания iOS 14, это был идеальный момент, чтобы пересмотреть наше предложение для Android.
Поскольку наш вклад был одинаковым для обеих платформ, как и наши аналоги для iOS в то время, мы знали, что продавцы используют наши виджеты, но им нужно было больше.
Содержание
- Почему виджеты важны для Shopify
- Почему мы не использовали React Native
- Создание виджетов
- Получение данных
- Создание пользовательского интерфейса
- Сбор аналитики
- Доставка новых виджетов
- Что дальше
Наши виджеты в основном ориентированы на аналитику, которая помогает продавцам понять, как у них дела, и получить информацию для быстрого принятия более эффективных решений в отношении своего бизнеса. Мониторинг метрик — это ежедневная деятельность многих наших продавцов, и на мобильных устройствах у нас есть возможность предоставить продавцам более быстрый доступ к этим данным с помощью виджетов. Они предоставляют продавцам уникальную возможность быстро получить информацию о своих магазинах, которая недоступна в Интернете.
Добавление виджета InsightsПосле сбора отзывов и постоянного поиска возможностей для расширения возможностей нашего виджета мы находимся на третьей итерации, и мы поделимся с вами проблемами, с которыми мы столкнулись, и тем, как мы их решили.
Пару лет назад Shopify решил полностью перейти на React Native. Новая разработка должна выполняться в React Native, и мы также переносим некоторые приложения на эту технологию. Это включает в себя флагманское приложение администратора, которое является сопутствующим приложением для виджетов.
Тогда почему бы не написать виджеты на React Native?
Проведя предварительное расследование, мы быстро обнаружили некоторые препятствия (например, тот факт, что RemoteViews — единственный способ создавать виджеты). В настоящее время в сообществе React Native нет официальной поддержки RemoteViews, необходимого для поддержки виджетов. Это было очень похоже на квадратный колышек в круглом отверстии. Наше приложение для iOS также столкнулось с проблемами, связанными с поддержкой React Native, и мы шли по тому же пути. Shopify верит в использование правильного инструмента для работы, мы считаем, что нативная разработка была правильным выбором в этом случае.
Создавая архитектуру для виджетов, мы хотели обеспечить единообразие работы как на Android, так и на iOS, сохраняя при этом идиомы платформ там, где это имело смысл. В разделах ниже мы хотим дать вам представление о нашем опыте создания виджетов. Указав на некоторые из наиболее сложных проблем, с которыми мы столкнулись. Наша цель — пролить свет на эти редко используемые поверхности, вдохновить их и сэкономить время, когда дело доходит до реализации виджетов в ваших приложениях.
Получение данных
Некоторые типы виджетов содержат данные, которые меняются реже (например, напоминания), а некоторые можно прогнозировать на весь день (например, календарь и погода). В нашем случае продавцам нужны актуальные показатели их бизнеса, поэтому нам нужно показывать как можно более свежие данные. Задержки в данных могут привести к путанице или, что еще хуже, к задержке информации, которая может изменить действие. Допустим, вы следите за фондовым рынком и ожидаете, что биржевое приложение и данные виджета будут как можно более актуальными. Если данные устарели на несколько часов, возможно, вы пропустили что-то важное! Чтобы наши виджеты были ценными, нам нужна свежая информация с учетом использования сети.
Получение данных в приложении
Виджеты можно обновлять с помощью актуальной и своевременной информации, используя данные, доступные локально, или получая их с сервера. Выборка сервера может быть инициирована самим виджетом или хост-приложением. В нашем случае, поскольку приложению не нужна та же информация, что нужна виджету, мы решили, что будет разумнее получить ее из виджета.
Одним из преимуществ управления виджетами в экосистеме Android по сравнению с iOS является гибкость. На iOS связь между приложением и виджетом ограничена, тогда как на Android таких ограничений нет. Это становится ясно, когда мы думаем о том, как мы настраиваем виджет. Экран конфигурации виджета имеет доступ ко всем библиотекам и классам, которые есть в нашем основном приложении. Он ничем не отличается от любого другого экрана в приложении. Это в основном верно и для виджета. Мы можем получить доступ к ресурсам, содержащимся в нашем основном приложении, поэтому нам не нужно дублировать какой-либо код. Единственные ограничения в виджете накладываются на виды зданий, которые мы рассмотрим позже.
Когда мы сохраняем нашу конфигурацию, мы используем общие настройки для сохранения данных между экраном конфигурации и виджетом. Когда запускается обновление виджета, данные общих настроек для данного виджета используются для создания нашего запроса, а результаты отображаются в виджете. Мы можем читать эти данные из любого места в нашем приложении, что позволяет нам повторно использовать эти данные в других частях нашего приложения, если это необходимо.
Обеспечение антихрупкости виджетов
Архитектура виджетов построена таким образом, что обновления учитывают использование батареи, когда обновления контролируются системой. Точно так же наши виджеты также должны помнить об экономии полосы пропускания при получении данных по сети. При разработке нашей второй итерации мы столкнулись со своеобразной проблемой, которая усугублялась нашим конкретным вариантом использования. Поскольку нам нужны свежие данные, мы всегда извлекаем новые данные из нашего бэкэнда при каждом обновлении. Каждое обновление происходит примерно через 15 минут, чтобы наши виджеты не переставали обновляться. Мы обнаружили, что виджеты вызывают свой метод обновления 9.0320 onUpdate() , более одного раза в цикле обновления. В таких виджетах, как календарь, эти дополнительные вызовы не требуют особых дополнительных затрат, поскольку данные хранятся локально. Однако в нашем приложении это вызывало от двух до пяти дополнительных сетевых вызовов для одного и того же виджета с теми же данными в быстрой последовательности.
Чтобы исправить ненужные обращения туда и обратно, мы создали простой кратковременный кеш:
- Система запрашивает у нашего виджета новые данные из Reportify (служба данных Shopify)
- Сначала мы просматриваем локальный кэш, используя идентификатор widgetID, предоставленный системой.
- Если есть данные, и эти данные были установлены менее минуты назад, мы возвращаем их и не делаем сетевой запрос. Мы также учитываем такие настройки, как локаль, чтобы не избежать принудительного обновления после смены языка.
- В противном случае мы извлекаем данные как обычно и сохраняем их в кэше с отметкой времени.
С помощью этого решения мы сократили неиспользуемые сетевые вызовы и нагрузку на систему, а также избежали сбора некорректной аналитики.
Реализация стратегии декодера с динамическим выбором
Мы используем тот же подход, что и для iOS. Мы создаем динамический набор запросов на основе того, что настроил продавец.
Для каждой метрики у нас есть соответствующая реализация определения. Этот подход позволяет каждой метрике иметь полную гибкость в отношении того, какие данные ей нужны, и как она декодирует данные из ответа.
Когда Android просит нас обновить наши виджеты, мы извлекаем выбор продавцов из нашего объекта конфигурации. Поскольку у каждого идентификатора метрики есть определение, мы сопоставляем их, чтобы создать динамический набор запросов.
Мы включаем расширение в наш объект ответа, которое связывает определения с декодером. Наш сервис отправляет обратно массив данных ответа, соответствующих сделанным запросам. Мы сопоставляем исходные определения, декодируя каждый фрагмент в соответствии с ожидаемым типом возвращаемого значения.
Создание пользовательского интерфейса
Как и в iOS, мы поддерживаем три размера виджета для версий до Android 12 и придерживаемся тех же правил для макета ячейки, за исключением маленького виджета. Небольшой виджет на Android поддерживает одну метрику (по сравнению с двумя на iOS), а наименьший размер виджета на Android — это сетка 2×1. Мы быстро обнаружили, что в это пространство поместится только одна метрика, поэтому этот дизайн немного отличается для разных платформ.
В отличие от iOS с быстрым предварительным просмотром, мы были ограничены предварительным просмотром XML и запуском виджета на эмуляторе или устройстве. Мы также создаем виджеты динамически, поэтому даже предварительный просмотр XML был относительно бесполезен, если мы хотели увидеть предварительный просмотр всего виджета. Виджеты в настоящее время находятся в дорожной карте Jetpack Compose на 2022 год, поэтому это, вероятно, скоро изменится с предварительными сборками Jetpack.
С добавлением динамических макетов в Android 12 мы создали пять дополнительных размеров для поддержки каждого размера между исходными тремя. Эти новые размеры уникальны для Android. Это также привело к использованию размеров сетки как части нашего соглашения об именах, как вы можете видеть в нашем перечислении WidgetLayout ниже.
Для структуры нашего виджета мы использовали перечисление, которое действует как план для сопоставления соответствующего файла макета с областью нашего виджета. Это особенно полезно, когда мы хотим добавить новый виджет, потому что нам просто нужно добавить новую конфигурацию перечисления.
Для динамического создания виджетов мы считываем нашу конфигурацию из общих настроек и передаем эту информацию API RemoteViews.
Если вы знакомы с RemoteViews API, вы можете заметить updateView()
, который не является методом RemoteViews по умолчанию. Мы создали этот метод расширения в результате проблемы, с которой мы столкнулись при построении нашего макета виджета таким динамическим образом. Когда виджет обновляется, новые удаленные представления добавляются к существующим. Как вы, наверное, догадались, виджет выглядел не так уж и хорошо. Хуже того, при каждом последующем обновлении добавляется больше удаленных представлений. Мы обнаружили, что объединение двух методов API RemoteViews removeAllViews()
и addView()
решил эту проблему.
После создания удаленных представлений мы передаем родительское удаленное представление методу AppWidgetProvider updateAppWidget()
для отображения желаемого макета.
Стоит отметить, что мы пытались использовать partialUpdateAppWidget()
, чтобы наши удаленные представления не присоединялись друг к другу, но столкнулись с той же проблемой.
Использование динамических дат
Одной из важных частей информации о нашем виджете является последняя обновленная метка времени. Это помогает устранить путаницу, позволяя продавцам быстро узнать, насколько актуальны данные, которые они просматривают. Если данные сильно устарели (скажем, вы поехали на выходные на дачу и пропустили несколько обновлений) и не отображалась временная метка, можно предположить, что данные, которые вы просматриваете, имеют точность до секунды. Это может вызвать ненужную путаницу у наших продавцов. Решение здесь состояло в том, чтобы обеспечить связь с нашим продавцом, когда было сделано последнее обновление.
В нашем предыдущем дизайне у нас были только маленькие виджеты, и они могли отображать только одну метрику. Эта информация приводила к длинному фрагменту текста, который на небольших устройствах иногда переносился и отображался на двух строках. Это было нормально, когда в нашем старом дизайне было много места, но не в наших новых проектах с большим объемом данных. Мы исследовали, как лучше всего работать с временными метками в виджетах, и самым многообещающим решением было использование относительного времени . Вместо статического значения, такого как «по состоянию на 15:30», как в нашей предыдущей итерации. У нас была бы динамическая дата, которая выглядела бы так: «1 минута, 3 секунды назад».
Следует помнить, что даже если виджет виден, у нас есть ограниченное количество обновлений, которые мы можем активировать. В противном случае это будет потреблять много ненужных ресурсов на устройстве. Мы знали, что не можем запускать обновления виджета так часто, как нам хотелось бы. У Android есть стратегия решения этой проблемы с помощью TextClock. Однако нет поддержки относительного времени, что было бы бесполезно в нашем случае использования. Мы также изучали использование будильников, но потенциально обновление каждую минуту потребляло бы слишком много заряда батареи.
Один большой вывод, который мы сделали из этих исследований, заключался в том, чтобы всегда тестировать свои виджеты в разных условиях. Особенно низкий заряд батареи или плохая сеть. Эти поверхности имеют гораздо более строгие ограничения, чем обычные приложения, и ОС с большей вероятностью будет игнорировать обновления.
В конце концов мы решили, что не будем использовать относительное время, и оставили время обновления виджета в качестве метки времени. Таким образом, у нас есть полный контроль над такими вещами, как форматирование даты и стиль.
Добавление конфигурации
Наши новые виджеты имеют множество вариантов конфигурации, что позволяет нашим продавцам выбирать именно то, что им нужно. Для каждого размера виджета продавец может выбрать магазин, определенное количество метрик и диапазон дат. Это единственная часть виджета, которая не использует RemoteViews, поэтому нет никаких ограничений на тип View, который вы можете использовать. Мы обмениваемся информацией между конфигурацией и виджетом через общие настройки.
Конфигурация виджета InsightsРабота с диаграммами и изображениями
Виджеты Android ограничены RemoteViews в качестве своих строительных блоков и очень ограничены с точки зрения поддерживаемых типов представлений. Если вам нужно поддерживать что-то помимо основного текста и изображений, вам нужно проявить немного творчества.
Наши виджеты поддерживают как спарклайны, так и гистограммы искр, созданные с использованием библиотеки MPAndroidCharts. Эти диаграммы уже настроены и оформлены в нашем основном приложении, поэтому повторное использование здесь было идеальным; за исключением того, что мы не можем использовать какие-либо пользовательские представления в наших виджетах. К счастью, эта библиотека создает диаграммы путем рисования на холсте, и мы просто экспортируем диаграммы в виде растрового изображения в представление изображения.
Как только мы смогли измерить виджет, мы построили диаграмму необходимого размера, создали растровое изображение и установили его в RemoteView.ImageView. При таком подходе следует помнить одну небольшую вещь: если вы хотите иметь прозрачный фон, вам придется использовать ARGB_8888 в качестве конфигурации растрового изображения. Этот простой подход с растровым изображением к ImageView позволил нам обрабатывать любой пользовательский рисунок, который нам нужно было сделать.
Устранение мерцания
Одна небольшая, но досадная проблема, с которой мы столкнулись на протяжении всего проекта, — это то, что мы любим называть «мерцанием виджета». Мерцание — это побочный эффект обновления виджетом своих данных. Между обновлениями Android использует initialLayout
из конфигурации виджета в качестве заполнителя, пока виджет извлекает свои данные и создает свои новые RemoteViews. Мы обнаружили, что устранить такое поведение невозможно, поэтому мы реализовали несколько стратегий, чтобы уменьшить частоту и продолжительность мерцания.
Первая стратегия используется, когда продавец впервые размещает виджет на главном экране. Здесь мы можем уменьшить частоту мерцания. В предыдущем разделе «Как сделать виджеты нехрупкими» мы поделились нашим недолговечным кешем. Кэш вступает в игру, потому что ОС запускает несколько обновлений для виджета, как только он помещается на главный экран. Иногда мы видели быстрые три или четыре мерцания, вызванные обновлениями виджета. После того, как виджет получает свои данные в первый раз, мы предотвращаем любые дополнительные обновления в течение первых 60 секунд, уменьшая частоту мерцания.
Вторая стратегия уменьшает продолжительность мерцания (или продолжительность отображения initialLayout
). Мы сохраняем конфигурацию виджетов как часть общих настроек при каждом ее обновлении. У нас всегда есть моментальный снимок того, какая информация виджета отображается в данный момент. Когда вызывается метод onUpdate()
, мы вызываем метод renderEarlyFromCache()
как можно раньше. Цель этого метода — создать виджет через общие настройки. Мы предоставляем этот кешированный виджет в качестве заполнителя, пока не поступят новые данные.
Сбор аналитики
Крупнейший виджет в облегченном режимеПосле разработки наших первых виджетов мы добавили стратегическую аналитику в ключевые области, чтобы понять, как продавцы используют эту функциональность. Это позволило нам извлечь уроки из использования, чтобы мы могли улучшить их. Команда данных создала информационные панели, отображающие подробные сведения о количестве установленных виджетов, наиболее популярных показателях и размерах.
Большая часть данных, используемых для создания панелей мониторинга, поступает из событий аналитики, запускаемых через виджеты и приложение Shopify.
Для этих новых виджетов мы хотели лучше понять их принятие и сохранение, поэтому нам нужно было зафиксировать, как пользователи настраивают свои виджеты с течением времени и какие из них добавляются или удаляются.
Обнаружение добавления и удаления виджетов
В отличие от iOS, сбор этих данных в Android выполняется очень просто. Чтобы зафиксировать, когда продавец добавляет виджет, мы отправляем наше аналитическое событие при сохранении конфигурации. При удалении виджета встроенные виджеты 9Метод 0793 onDeleted дает нам идентификатор удаленного виджета. Затем мы можем просмотреть информацию о виджете в общих настройках и отправить событие до окончательного удаления информации о виджете с устройства.
Когда мы начали разработку наших новых виджетов, наше приложение было нацелено на Android 11. Мы знали, что в конечном итоге нацелимся на Android 12, но мы не хотели, чтобы обновление заблокировало выпуск. Мы решили внедрить специальные функции Android 12 после того, как наше приложение ориентировалось на более новую версию, что привело к непредвиденной проблеме в процессе обновления с выбором виджета.
Наш подход к выбору виджетов в предыдущих версиях заключался в отображении каждого доступного размера в качестве опции. С введением адаптивных макетов нам больше не нужно было отображать каждый размер как отдельный вариант. Теперь продавцы могут выбрать один виджет и изменить его размер в соответствии с желаемым макетом. В предыдущих версиях продавцы могли выбрать маленький, средний и большой виджет. В версиях 12 и выше продавцы могут выбрать только один виджет, размер которого можно изменить до тех же макетов, что и у малого, среднего, большого и нескольких других макетов, которые находятся между ними. Этот шаблон следует тому, что Google делает с их большим виджетом погоды, включенным в устройства, а также примером в их документации. Мы отключили средние и маленькие виджеты в Android 12, добавив флаг в наш AndroidManifest
и установка этого значения в attrs.xml
для каждой версии:
Приведенный выше подход ведет себя так, как ожидалось, средние и маленькие виджеты теперь недоступны в средстве выбора. Однако, если продавец уже использовал Android 12 и добавил виджет среднего или большого размера до нашего обновления виджета, эти виджеты были удалены с их главного экрана. Это можно легко расценить как ошибку и снизить доверие к этой функции. Оглядываясь назад, мы могли бы создать прототип того, как обновление выглядело бы для продавца, который уже использовал Android 12.
Разрешить доступность только большому виджету было принято на основе данных. Отслеживая использование виджетов при запуске, мы увидели, что большой виджет был самым популярным, а удаление двух других окажет наименьшее влияние на текущих продавцов виджетов.
Создание новых макетов
Мы обнаружили ошибку при создании новых макетов, которые помещаются между исходными малыми, средними и большими виджетами.
После изучения ошибки мы обнаружили превышение буфера транзакций Binder. Однако размер буфера составляет 1 МБ, а ошибка отображала 0,66 МБ, что не превышало задокументированный размер буфера. Эта ошибка поставила в тупик многих разработчиков. Поэкспериментировав со способами уменьшения размера, мы обнаружили, что можем либо отказаться от пары целых макетов, либо убрать поддержку четвертой и пятой строк малой метрики. Мы выбрали последнее, поэтому наш виджет 2×3 имеет только три строки данных, когда в нем есть место для пяти.
Переосмысление экрана конфигурации
Теперь, когда у нас есть один виджет на выбор, нам пришлось переосмыслить, как наш экран конфигурации будет выглядеть для продавца. Без наших трех фиксированных размеров мы больше не могли отображать фиксированное количество метрик в нашей конфигурации.
Наш единственный выбор состоял в том, чтобы отобразить максимальное количество метрик, доступных для наибольшего размера (которого на момент написания этой статьи было семь). Мало того, что это имело для нас наибольший смысл с точки зрения UX, мы также должны были сделать это таким образом из-за того, как работают новые адаптивные макеты. Android должен заранее знать все возможные макеты. Даже если пользователь уменьшит свой виджет до размера, который отображает одну метрику, Android должен знать, что представляют собой остальные шесть, чтобы его можно было изменить до нашего самого большого макета без каких-либо сбоев.
Мы также обновили описание, которое отображается в верхней части экрана конфигурации и объясняет это поведение.
Сбор дополнительной аналитики
В iOS мы собираем аналитические данные, когда продавец перенастраивает виджет, чтобы получить представление о шаблонах использования. Реконфигурация для Android была возможна только в версии 12, и из-за ограничений метода AppWidgetProvider onAppWidgetOptionsChanged()
мы не смогли получить эти данные на Android.
Я поделюсь дополнительной информацией о создании наших макетов, чтобы придать контекст нашей проблеме. Установка точек останова для новых динамических макетов очень хорошо работает на всех устройствах. Google рекомендует создать сопоставление ваших точек останова с желаемым макетом удаленного просмотра. Чтобы развить предыдущий пример, в котором мы показали метод buildWidgetRemoteView()
, мы снова использовали этот метод как часть сопоставления точек останова. Этот подход позволяет нам надежно сопоставлять наши точки останова с желаемым макетом виджета.
При изменении конфигурации или размера виджета вызывается метод onAppWidgetOptionsChanged()
. Здесь мы хотели бы зафиксировать наши аналитические данные о том, что изменилось. К сожалению, такого сопоставления представлений здесь нет. У нас есть доступ к значениям ширины и высоты, которые измеряются в dp, что изначально казалось полезным. Сначала мы чувствовали, что можем различить эти измерения во что-то значимое и сопоставить эти значения с размерами макета. Протестировав на паре устройств, мы поняли, что подход ненадежен и приведет к большому объему неверных аналитических данных. Не зная с уверенностью, к какому размеру мы пришли или к какому приближаемся, мы решили пропустить это конкретное аналитическое событие из Android. Мы надеемся довести это до сведения Google и включить его в будущий выпуск.
Уже имея пару существующих виджетов, мы должны были решить, как перейти на новые виджеты, поскольку они заменят существующую реализацию.
Мы не нашли много документации по переносу виджетов. Документы предоставили только способ улучшить ваш виджет, что означает добавление новых функций Android 12 к тому, что у вас уже было. Это было неприменимо к нам, поскольку наши существующие виджеты сильно отличались от тех, которые мы создавали.
Основная проблема, которую мы не могли обойти, была связана со стратегиями изменения размеров наших существующих и новых виджетов. Существующие виджеты использовали фиксированную ширину и высоту, поэтому они всегда были квадратными. Наши новые виджеты занимают все доступное место. Не было способа гарантировать, что новый виджет поместится на доступном пространстве, которое занимал существующий виджет. Если бы существующий виджет был того же размера, что и новый, его стоило бы изучить дальше.
Первоначальный план, на который мы надеялись, заключался в том, чтобы один из наших виджетов трансформировался в новый виджет, а другой удалялся. Учитывая вышесказанное, эта стратегия не сработает.
Компромисс, к которому мы пришли, чтобы не удалять полностью все виджеты продавца в одночасье, заключался в том, чтобы отказаться от поддержки старых виджетов одновременно с выпуском нового. Чтобы отказаться от поддержки, мы обновили пользовательский интерфейс нашего старого виджета, чтобы отобразить сообщение о том, что виджет больше не поддерживается и продавец должен добавить новые.
Сообщение об устаревании виджетаНевозможно программно добавить новый виджет или вывести продавца в окно выбора виджета, нажав на старый виджет. Мы добавили некоторую информацию, чтобы облегчить переход, обновив документы нашего справочного центра, в том числе информацию о том, как использовать виджеты, указав нашим старым виджетам открывать документы справочного центра, и просто потратив много времени, прежде чем удалить сообщение об устаревании. В конце концов, это была не самая идеальная ситуация, и мы узнали о подводных камнях в двух экосистемах.
По мере того, как мы продолжаем узнавать о том, как продавцы используют наше новое поколение виджетов и функции Android 12, мы продолжим оттачивать лучший опыт на обеих наших платформах. Это также дает возможность другим командам Shopify развивать то, что мы начали, и создавать больше виджетов, чтобы помочь продавцам.
Говоря о мобильных платформах, это подводит нас к ускорению работы с WearOS. Наше приложение для WatchOS скоро получит обновление с добавлением Widgetkit; Похоже, пришло время, наконец, предоставить поддержку часов Android Merchants!
Мэтт Боуэн — разработчик мобильных приложений в группе Core Admin Experience. Расположен в Вест-Уорике, Род-Айленд. Личное хобби включает в себя тренировки и просмотр матчей Boston Celtics и New England Patriots.
Джеймс Локхарт — штатный разработчик из Оттавы, Канада. Опыт мобильной разработки за последние 10 с лишним лет: от Phonegap до нативной iOS/Android и теперь нативной React. Он заядлый пекарь и готовит, когда не участвует в административном приложении Shopify.
Сесилия Хунка — разработчик в группе Core Admin Experience в Shopify. Живет в Торонто, Канада, любит живую музыку и головоломки.
Карлос Перейра — старший разработчик из Монреаля, Канада, с более чем десятилетним опытом создания нативных iOS-приложений на Objective-C, Swift, а теперь и на React Native. В настоящее время участвует в административном приложении Shopify.
Где бы вы ни были, ваше следующее путешествие начинается здесь! Если вас интересует создание систем с нуля для решения реальных проблем, в нашем инженерном блоге есть истории о других проблемах, с которыми мы столкнулись.