Двадцать лет назад использовать Android было невозможно. Десять лет назад было еще рано. Сегодня — уже поздно. Конечно, я утрирую, но, когда операционная система молода, многие ее недостатки можно списать на «детские болезни». Их можно понять, простить и не замечать, ожидая скорейших багфиксов от компании-производителя, но… Android уже дорос, пожалуй, до бальзаковского возраста, а мы все так же решаем проблемы, которые давным-давно должны быть исправлены. Сегодня мы рассмотрим те затруднения при разработке под Android, с которыми ты точно когда-нибудь столкнешься. И которые, кстати, дико бесят.
Рекомендуем: Инструменты Android-разработчика
- Жизненный цикл Activity может в любой момент перечеркнуть все твои планы
- Сбор ошибок, которого не было
- Теперь я стреляю у девушек телефон… чтобы отловить на нем баги
- Хотите меньше багов в приложении? Выкидывай фрагменты и асинктаски (лоадеры тоже не забудьте)
- Фрагментация ухудшает внешний вид
- Библиотекам поддержки самим нужна «помощь и поддержка»
- «Мы хотели помочь вам c разметкой и написали Constraint Layout, но он падает и тормозит»
- Просто записать файл на флешку не получится: спроси разрешения, а лучше городи контент-провайдер
- Все стандартные медиакомпоненты просто ужасны
- Любой желающий может декомпилировать ваше приложение и изменить его, как захочет, — оно просто создано для этого
Жизненный цикл Activity может в любой момент перечеркнуть все твои планы
Если вы программируете под Android, то наверняка встречали схему жизненного цикла для самой простой активити. А знал ли вы жизненный цикл окошка с кнопочками, которое создавал компилятор старого и доброго Delphi? Я думаю — нет, не знали, и знать вам это было не нужно. Интересно, как бы на вас посмотрели олдскульные дельфисты-паскалисты, если бы вы им сказали: «когда пользователь перевернет свой экран, нам надо пересоздать все формы и восстановить состояние всех компонентов». Скорее всего, они бы пальцем у виска покрутили. И не только потому, что переворачивать старые мониторы и системные блоки было не принято.
А меж тем Android именно так и устроен: повернул экран — начинай все сначала. Конечно, в настройках вы можете отключить поворот экрана, но это не всегда удобно, да и рядовые пользователи не знают и десяти процентов возможностей своего смартфона. Поэтому сейчас имеем мы то, что имеем, и единственное, что вы можете сделать при разработке, — это отключить в манифесте пересоздание Activity, добавив свойства
1 |
android:configChanges="keyboardHidden|orientation|screenSize" |
Конечно, «знающие люди» так делать не рекомендуют, но такой костыль используют даже серьезные социальные приложения. Если не верите — посмотрите сами манифесты из их APK.
Дело осложняют и сами производители устройств — каждый из них адаптирует ОС к своим устройствам, меняя в том числе ее поведение. Например, традиционно рекомендуется сохранять данные при событии onStop. Пользователь свернул приложение, и вызвался этот метод. Но есть одна проблема: на некоторых устройствах вызов этого метода не гарантирован. В таком случае нужно сохранять все, что можете, в методе onPause. Правда, он вызывается не только при выходе из активити, но и при показе диалогового окна, поэтому нужно предвидеть этот момент и предусматривать проверки на показ диалога.
Сбор ошибок, которого не было
Я с тоской вспоминаю старые времена виндовой разработки. Кому нужны системы для сбора ошибок? Да никому! Большинство ошибок лежало «на поверхности», программа или работает, или нет. Если она периодически вылетает — значит, «проклятая винда опять глючит». А если она вылетает по вине программиста, то и не страшно, ведь онлайн-сторы программного обеспечения еще не придумали и никто не засыплет вашу софтину минусами и гневными комментариями. Максимум — под пивко поплачет в жилетку своему дилеру пиратского ПО на Митинском радиорынке.
Сегодня вы лично, оперативно и максимально прозрачно отвечаете за каждый свой begin и end в Kotlin-коде. Поэтому собирать ошибки нужно сразу при старте приложения.
Систем сбора сейчас расплодилось превеликое множество, но лично я пользуюсь Crashlytics. В ее пользу говорит и то, что ее недавно купила сама Google. Включать сбор ошибок можно для каждой активити отдельно, но проще сразу включить его в классе Application:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@Override protected void onCreate() { super.onCreate(savedInstanceState); Fabric.with(this, new Crashlytics()); try { FirebaseApp.initializeApp(this); mFirebaseAnalytics = FirebaseAnalytics.getInstance(this); Crashlytics.log(Log.INFO, "App.onCreate", "Firebase initialized"); } catch (Exception e) { e.printStackTrace(); Crashlytics.log(Log.ERROR, "App.onCreate", "Error initialize Firebase with: " + e.getMessage()); } } |
Выполнение кода в этом месте может затянуть показ первого экрана приложения, но туда можно через тему поставить свой Splash и убить сразу двух зайцев — подгрузить инструменты и развлечь пользователя.
Как видно из приведенного выше кода, один инструмент Google — Crashlytics — следит за работой другого — Firebase. Несмотря на то что оба они могут выполнять одинаковые задачи — собирать события, Crashlytics действует так:
1 |
Crashlytics.log(Log.INFO, "App.onCreate", "Firebase initialized"); |
А Firebase — вот так:
1 2 3 4 5 6 7 8 |
public static void selectContent(String type, String id) { if (mFirebaseAnalytics != null) { Bundle bundle = new Bundle(); bundle.putString(FirebaseAnalytics.Param.ITEM_ID, id); bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, type); mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.SELECT_CONTENT, bundle); } } |
Теперь я стреляю у девушек телефон… чтобы отловить на нем баги
Про изменение прошивки производителем вы уже слышали. Но как быть, если неприятность уже случилась и ваше приложение, к примеру, стабильно падает у производителя X на устройстве Y с прошивкой Z?
Все бы ничего — подумаешь, одно падение у одного устройства. Но таких устройств было продано несколько миллионов штук, так что гневных отзывов и однозвездных комментариев вас ждет несколько десятков. Как быть? Использовать фермы для тестирования.
Из халявных инструментов я бы предпочел Firebase, который на бесплатном тарифе дает прогнать тесты по десяти реальным устройствам. Кроме того, не забывайте погуглить собственные веселые фермы от разработчиков смартфонов. И заводи контакты с людьми, которые работают в магазинах электроники или сервисных центрах (особенно неплохо это получается в маленьких городах). Тогда вы сможете делать, как я, — брать устройства для тестов у них. Главное, не забудьте вернуть гаджет в первоначальное состояние и скройте меню для разработчиков, если оно было заблокировано ранее.
Хотите меньше багов в приложении? Выкидывай фрагменты и асинктаски (лоадеры тоже не забудьте)
Говорят, что секрет 99,9% удачных пусков приложения — в отказе от фрагментов в любом виде. Дело в том, что у разных производителей они порой выкидывают такие замысловатые ошибки, что понять их можно только с помощью хрустального шара. Если шаром вы владеть не обучены, лучше вовсе отказаться от фрагментов. Тот же ViewPager можно сделать и без них.
Асинктаски можно использовать для разовых фоновых запросов, но, если нужно много работы делать в фоне, используйте другие инструменты (Rx, сервисы). Ну а лоадеры имеют старый баг, связанный с тем, что колбэки о завершении могут вообще не вызываться. Говорят, что в новых версиях это исправили, но проверять я бы не стал (они так каждые полгода говорят).
Рекомендую: Полезные советы разработчику на языке Kotlin
Фрагментация ухудшает внешний вид
Огромный парк разноформатных устройств — одновременно баг и фича мира Андроида. Из-за большой фрагментации приходится вымерять разметку под каждую плотность экрана. Редактор макетов экранов при этом крайне неудобный. Из-за этого особо отчаянные (Telegram) вообще его не используют, а генерируют экраны динамически, из кода.
А как там с редизайном стандартных меню и вкладок? Легко! Два дня вдумчивого гугления, ящик пива, блок сигарет, пробы, ошибки, несколько костылей — и пожалуйста, приложение выглядит почти как нарисовал дизайнер. Уверен, что «отличные» компоненты DrawerLayout и ViewPager отлично научат вас пользоваться поиском при любом нестандартном поведении или когда просто захотите сменить шрифты в меню.
Библиотекам поддержки самим нужна «помощь и поддержка»
Серьезно, каждый новый релиз — новые костыли! Почти все компоненты из библиотеки поддержки несут неизлечимые баги, которые не исправляются годами. У меня было несколько ошибок, которые умерли вместе с устройствами, но так и не были исправлены. К примеру, отличный класс для уведомлений NotificationCompat меняется каждую версию Android, и вы меняете свой код вслед за ней.
Библиотеки поддержки очень большие и сами тянут в проект библиотеки для поддержки. Программисты даже шутят: «Каждый класс в этих библиотеках будет иметь суффикс CompatCompat». Кстати, чтобы увидеть, что тянет та или иная библиотека за собой, можно воспользоваться инструментом gradle — dependencies.
Для удаления хотя бы части ненужного кода можно прописать правило для gradle:
1 2 3 4 5 6 |
configurations.all { exclude group : 'com.android.support', module :'transition' exclude group : 'com.android.support', module :'customtabs' exclude group : 'com.android.support', module :'support-media-compat' exclude group : 'com.android.support', module :'support-fragment' } |
После отработки правил исключения не забудьте еще раз все протестировать.
«Мы хотели помочь вам c разметкой и написали Constraint Layout, но он падает и тормозит»
Новинка, которая должна была упростить нам жизнь, — Constraint Layout, — сложна в использовании, и приложение из-за нее становится тяжелее и медленнее. Чтобы работать с этим инструментом эффективнее, советую прочесть статью. А в преддверии версии Constraint Layout 2.0 можно изучить это выступление. По слухам, его представят на Google IO 2018.
Просто записать файл на флешку не получится: спроси разрешения, а лучше городи контент-провайдер
Сама идея ограничивать доступ программ к файловой системе — благая и по сравнению с «виндовой вольницей», где каждый делает, что хочет, конечно, секьюрная. Но какой же это геморрой, коллеги!
Перекинуть данные через простой файл не разрешает сама ОС. Хотите обмена? Пишите контент-провайдер. Точно нужно записать что-нибудь на диск? Реально нужно? Извольте спросить у пользователя кучу разрешений и заранее продумать, что делать, если он их вам не даст.
Рекомендуем: Что должен знать начинающий Андроид-разработчик
Все стандартные медиакомпоненты просто ужасны
Максимум, на что способны классы VideoView, MediaPlayer и SurfaceView, — заставить вас чесать репу и просиживать долгие часы за компьютером. Если вам нужен результат, то ищите сторонние библиотеки. Пусть чужой опыт сэкономит вам время и нервы. Вот хороший плеер.
Для работы с камерой изучите «Побеждаем Android Camera2 API с помощью RxJava2».
Как лучше проигрывать музыку? Подсмотрите у Timber и Phonograph.
Любой желающий может декомпилировать ваше приложение и изменить его, как захочет, — оно просто создано для этого
Исторически сложилось так (и это уже точно не исправить), что код для Android работает в виртуальной машине. Кому пришло в голову поднимать на низкопроизводительных ARM-процессорах виртуалки? Вряд ли теперь мы это узнаем. Apple сделала все, как надо, и завоевала приз наших зрительских симпатий, а вот «корпорация добра» решила извратиться, и теперь нас напрягает (спасибо современным процессорам) даже не производительность, а тот факт, что исходный код приложения можно восстановить почти что до запятой.
Пишите свой код качественнее, и его будут читать не только китайские хакеры, но и программисты из развитых стран — считайте это успехом. 😉 А стоит вам пренебречь обфускацией, и любой школьник сможет модифицировать вашу программу и сделать на ее основе свой шедевр.