Пост 11. С чего начать новичку. Краткий план обучения: основы
Собрал свои мысли в кучку и написал примерный план обучения для новичков (старичкам тоже полезно повторять иногда гайды - скоро поговорим об этом тоже). Сложность в том, что у всех разный опыт и универсальный план подобрать довольно сложно. Будем исходить из того, что человек совсем ничего не знает о платформе или знает совсем немного.
И так, поехали.
1. Архитектура Android-приложения. Роль jvm, как устроено приложение, процессы в Android.
2. Компоненты в Android. Стоит понимать какие существуют основные компоненты и какой у них жизненный цикл.
3. AndroidManifest.xml - основной конфиг приложения. Как объявлять активити, интент фильтры, сервисы, пермишены и т.д.
4. Activity - фундамент любого приложения. Жизненный цикл и основные методы.
5. Fragment - строительные кирпичики приложения. Жизненный цикл и основные методы.
6. Сейчас чаще всего архитектура приложения строится на базе "паттерна" Single Activity (одна Activity и множество Fragment) и крайне полезно понимать как взаимодействуют Acitivty и Fragment и как пересекаются их жизненные циклы. Тут же: FragmentTransaction, различия методов add() и replace(), commit() / commitNow(), FragmentManager. По сути, многие роутеры (тот же Cicherone) за кулисами работают именно с этими классами.
7. Tasks и Back Stack, навигация между экранами, флаги влияющие на поведение в стеке
8. Context - основной класс для доступа к ресурсам (и основной источник утечек памяти), Intent, IntentFilter, Bundle, запуск активити и других приложений. Здесь же полезно разобрать как не надо работать с Context: context leaks
9. Основные типы ресурсов и папки для них (res/values, res/layout, res/drawable, assets и т.д.)
10. Service. Жизненный цикл и основные методы, типы сервисов
11. Views: основные виджеты в Android (TextView, EditText, ImageView, View и т.д.)
12. Базовые ViewGroups: базовые контейнеры вьюшек (LinearLayout, RelativeLayout, FrameLayout)
13. RecyclerView: списки, сетки (гриды) и прочее - это все про него. Штука мощная, поэтому стоит отдельного внимания. Сюда же можно добавить RecyclerView Adapters, ViewHolders, Layout Managers.
14. ConstraintLayout. Самый мощный ViewGroup с помощью которого можно сверстать почти что угодно. Также выделен отдельно, ибо невероятно мощный, с кучей возможностей. Говорят, что пришел к нам с iOS.
15. Кастомные Views. Как создать, жизненный цикл, методы onMeasure, onLayout, парсинг атрибутов (attrs.xml) - помнить про метод recycle()
16. Animations. Виды анимаций, метод view.animate(), векторная анимация, ObjectAnimator / ValueAnimator, android:animateLayoutChanges, а в качестве бонуса можно рассмотреть библиотеку Lottie, которая часто используется для сложной векторной анимации (сплеш-скрины, красивые разные штуки)
17. Стили и темы: кастомизируем внешний вид приложения
18. Toolbar, Menus, BottomNavigationView, ViewPager / ViewPager2
19. Контейнеры для прокрутки: ScrollView, HorizontalScrollView, NestedScrollView, полезно знать возможные проблемы с NestedScrollView
20. Dialogs: Dialog, DialogFragment
21. BottomSheets, BottomSheetBehavior, CoordinatorLayout, BottomSheetDialog, BottomSheetDialogFragment. Приложения без BottomSheets сейчас сложно встретить
22. Permissions: типы, правила прозрачности и минимального количества пермишенов
23. SharedPreferences, DataStore (опционально)
24. BroadcastReceiver - встроенный механизм "event bus" в Android: ограничения, типы, класс LocalBroadcastManager.
25. Handler, Looper, HandlerThread
26. На закуску: ContentProvider - CRUD-интерфейс для работы со встроенными БД в Android (контакты, календарь и прочее)
Пошаговый гайд по созданию вашего первого приложения:
https://developer.android.com/training/basics/firstapp
Другие гайды:
https://developer.android.com/guide
У Гугла есть еще CodeLabs: https://developer.android.com/courses/fundamentals-training/toc-v2 - тоже полезная штука. Можно скачать код, поиграться с ним и все это в пошаговом туториале.
Собрал свои мысли в кучку и написал примерный план обучения для новичков (старичкам тоже полезно повторять иногда гайды - скоро поговорим об этом тоже). Сложность в том, что у всех разный опыт и универсальный план подобрать довольно сложно. Будем исходить из того, что человек совсем ничего не знает о платформе или знает совсем немного.
И так, поехали.
1. Архитектура Android-приложения. Роль jvm, как устроено приложение, процессы в Android.
2. Компоненты в Android. Стоит понимать какие существуют основные компоненты и какой у них жизненный цикл.
3. AndroidManifest.xml - основной конфиг приложения. Как объявлять активити, интент фильтры, сервисы, пермишены и т.д.
4. Activity - фундамент любого приложения. Жизненный цикл и основные методы.
5. Fragment - строительные кирпичики приложения. Жизненный цикл и основные методы.
6. Сейчас чаще всего архитектура приложения строится на базе "паттерна" Single Activity (одна Activity и множество Fragment) и крайне полезно понимать как взаимодействуют Acitivty и Fragment и как пересекаются их жизненные циклы. Тут же: FragmentTransaction, различия методов add() и replace(), commit() / commitNow(), FragmentManager. По сути, многие роутеры (тот же Cicherone) за кулисами работают именно с этими классами.
7. Tasks и Back Stack, навигация между экранами, флаги влияющие на поведение в стеке
8. Context - основной класс для доступа к ресурсам (и основной источник утечек памяти), Intent, IntentFilter, Bundle, запуск активити и других приложений. Здесь же полезно разобрать как не надо работать с Context: context leaks
9. Основные типы ресурсов и папки для них (res/values, res/layout, res/drawable, assets и т.д.)
10. Service. Жизненный цикл и основные методы, типы сервисов
11. Views: основные виджеты в Android (TextView, EditText, ImageView, View и т.д.)
12. Базовые ViewGroups: базовые контейнеры вьюшек (LinearLayout, RelativeLayout, FrameLayout)
13. RecyclerView: списки, сетки (гриды) и прочее - это все про него. Штука мощная, поэтому стоит отдельного внимания. Сюда же можно добавить RecyclerView Adapters, ViewHolders, Layout Managers.
14. ConstraintLayout. Самый мощный ViewGroup с помощью которого можно сверстать почти что угодно. Также выделен отдельно, ибо невероятно мощный, с кучей возможностей. Говорят, что пришел к нам с iOS.
15. Кастомные Views. Как создать, жизненный цикл, методы onMeasure, onLayout, парсинг атрибутов (attrs.xml) - помнить про метод recycle()
16. Animations. Виды анимаций, метод view.animate(), векторная анимация, ObjectAnimator / ValueAnimator, android:animateLayoutChanges, а в качестве бонуса можно рассмотреть библиотеку Lottie, которая часто используется для сложной векторной анимации (сплеш-скрины, красивые разные штуки)
17. Стили и темы: кастомизируем внешний вид приложения
18. Toolbar, Menus, BottomNavigationView, ViewPager / ViewPager2
19. Контейнеры для прокрутки: ScrollView, HorizontalScrollView, NestedScrollView, полезно знать возможные проблемы с NestedScrollView
20. Dialogs: Dialog, DialogFragment
21. BottomSheets, BottomSheetBehavior, CoordinatorLayout, BottomSheetDialog, BottomSheetDialogFragment. Приложения без BottomSheets сейчас сложно встретить
22. Permissions: типы, правила прозрачности и минимального количества пермишенов
23. SharedPreferences, DataStore (опционально)
24. BroadcastReceiver - встроенный механизм "event bus" в Android: ограничения, типы, класс LocalBroadcastManager.
25. Handler, Looper, HandlerThread
26. На закуску: ContentProvider - CRUD-интерфейс для работы со встроенными БД в Android (контакты, календарь и прочее)
Пошаговый гайд по созданию вашего первого приложения:
https://developer.android.com/training/basics/firstapp
Другие гайды:
https://developer.android.com/guide
У Гугла есть еще CodeLabs: https://developer.android.com/courses/fundamentals-training/toc-v2 - тоже полезная штука. Можно скачать код, поиграться с ним и все это в пошаговом туториале.
Android Developers
Create your first Android app | Android Developers
Learn how to create your first Android app.
👍1
Примеры_задач_для_подготовки_к_алгоритмам.pdf
33.5 KB
Кстати, по поводу собеседований.
Если вдруг кто-то захочет пообщаться с Яндекс вот вам примеры алгоритмических задачек, которые любезно предоставляют их hr-ы, чтобы подготовиться к собеседованию.
До сих пор не понимаю до конца зачем в Яндекс.Еде спрашивать всякие алгоритмические задачки, но дело Яндекса. Видимо чтобы бегать из проекта в проект и писать сходу супер-оптимизированный код. А может просто как обычно копируют Гугл? Хз.
Решать их в качестве хобби или зарядки для мозгов полезно - никто не спорит, а на практике - что-то действительно сложное, где нужны хорошие знания алгоритмов встречаются ну оооочень редко. Во всяком случае в обычных бизнес-приложения, коих 95%. И все это можно сесть за день разобрать с помощью инета или хорошей книги если хотя бы какие-то базовые знания есть. Да даже если и нет - множество классных книг уже выпущено. Бери и делай!
А чаще всего все проблемы приложения связаны с бекендом и работой с данными на самом клиенте.
Вообщем, такое, но может кому-то пригодится.
Если вдруг кто-то захочет пообщаться с Яндекс вот вам примеры алгоритмических задачек, которые любезно предоставляют их hr-ы, чтобы подготовиться к собеседованию.
До сих пор не понимаю до конца зачем в Яндекс.Еде спрашивать всякие алгоритмические задачки, но дело Яндекса. Видимо чтобы бегать из проекта в проект и писать сходу супер-оптимизированный код. А может просто как обычно копируют Гугл? Хз.
Решать их в качестве хобби или зарядки для мозгов полезно - никто не спорит, а на практике - что-то действительно сложное, где нужны хорошие знания алгоритмов встречаются ну оооочень редко. Во всяком случае в обычных бизнес-приложения, коих 95%. И все это можно сесть за день разобрать с помощью инета или хорошей книги если хотя бы какие-то базовые знания есть. Да даже если и нет - множество классных книг уже выпущено. Бери и делай!
А чаще всего все проблемы приложения связаны с бекендом и работой с данными на самом клиенте.
Вообщем, такое, но может кому-то пригодится.
Пост 12. UseCases vs Interactors (часть 1)
Начнем с небольшой теории.
UseСase и Interactor - сущности уровня domain, в которых обычно сосредоточена вся бизнес-логика приложения. Во всяком случае, стоит к этому стремиться.
Зачем это все?
Причин использовать юскейсы или интеракторы множество, но вот основные на мой взгляд.
1. Не писать бизнес-логику во вьюмоделях / презентерах. Тут можно снова вспомнить SOLID, но если говорить проще - код должен быть слабосвязным, чтобы его было проще поддерживать, тестировать и вот это вот все. Гораздо проще менять сущности, которые не сильно связаны между собой. Сегодня ViewModels / Presenters, завтра придумают или захотят ваши лиды что-то другое и когда логика отделена - поменять будет гораздо проще.
2. Бизнес-логику возможно придется использовать повторно в нескольких местах или даже в разных проектах. Мы же не будем использовать целый презентер, который всегда тесно связан с конкретным экраном в другом? Нет.
3. Во вьюмоделях и презентерах должна быть логика presеentation-слоя, то бишь всякие навигации, подсунуть данные во вьюшку, получить их из интерактора или юскейса.
4. Код читабельнее и проще. На самом деле важный пункт, который будет еще важнее по мере роста проекта и по мере добавления новых разработчиков в команду.
5. Можно разделять задачи между несколькими разработчиками. Кто-то пишет бизнес-логику, кто-то вьюмодели / презентеры или кто-то делает одни юйскейсы / интеракторы, кто-то другие. В итоге идет быстрее работа и меньше конфликтов в коде.
Забавно, что для простоты примеров Гугл в своей доке часто делает вызовы методов репозитория прямо во вьюмоделях. А еще смешнее, что подобное потом встречаешь в реальном проекте. Человек просто скопировал из доки, не задумываясь. И это кстати брокерское приложение!
Разница между ними не такая большая и часто на уровне вкусовщины конкретной команды.
Однако, в примерах Гугла часто можно встретить именно UseCases.
В чем на самом деле разница?
1. Количество файлов. В большом проекте будет очень много UseCases, что со временем будет довольно сложно поддерживать. Некоторые юскейсы можно объединить в одном kt-файле, конечно, но все равно от их огромного количества не спастись.
2. UseCase - это класс с одним методом, который часто лишь дергает один единственный метод репозитория (хорошее правило), но понятное дело, что там может быть (и должна) любая бизнес-логика. В интерфейсе интерактора может быть множество методов, но все они должны работать с одной сущностью. То бишь удовлетворять принципу S из SOLID. Но UseCases в этом плане еще лучше, ибо причина для изменения юскейса обычно всего одна, в отличии от интерактора. Плюс интерактор не совсем удовлетворяет принципу I.
3. UseCases более наглядны. Посмотрите скриншот - по названиям юскейсов уже можно примерно понять, что происходит в проекте. С интеракторами дело чуть сложнее.
На самом деле, сложно быть объективным - обычно я всегда использую интеракторы и лишь не так давно в своем проекте начал рефакторить на юскейсы, чтобы просто посмотреть разницу. Если давать осмысленные имена для юскейсов, то получается вцелом наглядно.
Начнем с небольшой теории.
UseСase и Interactor - сущности уровня domain, в которых обычно сосредоточена вся бизнес-логика приложения. Во всяком случае, стоит к этому стремиться.
Зачем это все?
Причин использовать юскейсы или интеракторы множество, но вот основные на мой взгляд.
1. Не писать бизнес-логику во вьюмоделях / презентерах. Тут можно снова вспомнить SOLID, но если говорить проще - код должен быть слабосвязным, чтобы его было проще поддерживать, тестировать и вот это вот все. Гораздо проще менять сущности, которые не сильно связаны между собой. Сегодня ViewModels / Presenters, завтра придумают или захотят ваши лиды что-то другое и когда логика отделена - поменять будет гораздо проще.
2. Бизнес-логику возможно придется использовать повторно в нескольких местах или даже в разных проектах. Мы же не будем использовать целый презентер, который всегда тесно связан с конкретным экраном в другом? Нет.
3. Во вьюмоделях и презентерах должна быть логика presеentation-слоя, то бишь всякие навигации, подсунуть данные во вьюшку, получить их из интерактора или юскейса.
4. Код читабельнее и проще. На самом деле важный пункт, который будет еще важнее по мере роста проекта и по мере добавления новых разработчиков в команду.
5. Можно разделять задачи между несколькими разработчиками. Кто-то пишет бизнес-логику, кто-то вьюмодели / презентеры или кто-то делает одни юйскейсы / интеракторы, кто-то другие. В итоге идет быстрее работа и меньше конфликтов в коде.
Забавно, что для простоты примеров Гугл в своей доке часто делает вызовы методов репозитория прямо во вьюмоделях. А еще смешнее, что подобное потом встречаешь в реальном проекте. Человек просто скопировал из доки, не задумываясь. И это кстати брокерское приложение!
Разница между ними не такая большая и часто на уровне вкусовщины конкретной команды.
Однако, в примерах Гугла часто можно встретить именно UseCases.
В чем на самом деле разница?
1. Количество файлов. В большом проекте будет очень много UseCases, что со временем будет довольно сложно поддерживать. Некоторые юскейсы можно объединить в одном kt-файле, конечно, но все равно от их огромного количества не спастись.
2. UseCase - это класс с одним методом, который часто лишь дергает один единственный метод репозитория (хорошее правило), но понятное дело, что там может быть (и должна) любая бизнес-логика. В интерфейсе интерактора может быть множество методов, но все они должны работать с одной сущностью. То бишь удовлетворять принципу S из SOLID. Но UseCases в этом плане еще лучше, ибо причина для изменения юскейса обычно всего одна, в отличии от интерактора. Плюс интерактор не совсем удовлетворяет принципу I.
3. UseCases более наглядны. Посмотрите скриншот - по названиям юскейсов уже можно примерно понять, что происходит в проекте. С интеракторами дело чуть сложнее.
На самом деле, сложно быть объективным - обычно я всегда использую интеракторы и лишь не так давно в своем проекте начал рефакторить на юскейсы, чтобы просто посмотреть разницу. Если давать осмысленные имена для юскейсов, то получается вцелом наглядно.
Пост 12. UseCases vs Interactors (часть 2)
Пара примеров:
И интерактор:
А что с тестированием?
Конечно, проще писать тесты на юскейсы просто потому что они меньше, они делают ровно одно действие и соответственно их проще замокать.
Хорошая статья про юскейсы: https://proandroiddev.com/why-you-need-use-cases-interactors-142e8a6fe576
Классический пример архитектуры куда можно иногда подсматривать: https://github.com/android10/Android-CleanArchitecture
А что обычно используете вы?
Пишите в комменты свой опыт работы с юскейсами или интеракторами. И что на ваш взгляд лучше, удобнее и разные нюансы из опыта.
Пара примеров:
// базовый интерфейс юскейса
interface BaseUseCase<in Params, out Type> where Type : Any {
suspend operator fun invoke(params: Params): Type
}
class SuggestionCityUseCase @Inject constructor(
private val repository: AddressSuggestionRepository
) : BaseUseCase<AddressRequest, SuggestionsData>t; {
override suspend fun invoke(params: AddressRequest): SuggestionsData {
return repository.addressSuggest(params.query)
}
}
// кстати, operator fun invoke() позволит вызывать код юскейса так:
suggestionUseCase(AddressRequest(query = query))
И интерактор:
interface SettingsAccountInteractor {
suspend fun getUserInfo(force: Boolean): UserAccount
fun getUserInfoSync(): UserAccount?
fun enableLocalization(): Boolean
}
class SettingsAccountInteractorImpl @Inject constructor(
private val userRepository: UserRepository,
private val config: Config
) : SettingsAccountInteractor {
override suspend fun getUserInfo(force: Boolean): UserAccount = userRepository.getUserInfo(force)
override fun getUserInfoSync(): UserAccount? = userRepository.getUserInfoSync()
override fun enableLocalization(): Boolean = config.enableLocalization
}А что с тестированием?
Конечно, проще писать тесты на юскейсы просто потому что они меньше, они делают ровно одно действие и соответственно их проще замокать.
Хорошая статья про юскейсы: https://proandroiddev.com/why-you-need-use-cases-interactors-142e8a6fe576
Классический пример архитектуры куда можно иногда подсматривать: https://github.com/android10/Android-CleanArchitecture
А что обычно используете вы?
Пишите в комменты свой опыт работы с юскейсами или интеракторами. И что на ваш взгляд лучше, удобнее и разные нюансы из опыта.
Medium
Why you need use-cases/interactors
Since Clean Architecture became the new hot topic in the Android world there have been loads of code samples and articles that tried to…
Что новенького в рассылках #3
Выбор девайса для работы всегда актуален. Небольшое сравнение скорости компиляции и нагрева Apple M1 с i7:
https://proandroiddev.com/apple-m1-vs-intel-the-ultimate-comparison-2a2f0d197dc
Разница впечатляет!
Compose. Тут как всегда куча всего.
Понеслась!
Начали клепать либы для Compose. Вот, например, либа для пунктов настроек:
https://github.com/alorma/Compose-Settings
Очередной пример использования Compose:
https://github.com/rock3r/Bundel/
Хороший пошаговый туториал по созданию списка с данными на Compose для начинающих:
https://www.waseefakhtar.com/android/recyclerview-in-jetpack-compose/
И еще один:
https://medium.com/mateedevs/bye-xml-it-was-nice-knowing-you-pt-1-50b195bab1a9
Пост с официального блога. Крис Бейнс сравнил разницу в размере приложения (количество строк + размер APK) и скорости сборки после миграции на Compose:
https://medium.com/androiddevelopers/jetpack-compose-before-and-after-8b43ba0b7d4f
И кстати, в репозитории Криса можно увидеть архитектуру вместе с Compose (ViewModels, навигация и прочее) - https://github.com/chrisbanes/tivi
Рекомендую!
Сравнение LiveData и Flow:
https://proandroiddev.com/flow-livedata-what-are-they-best-use-case-lets-build-a-login-system-39315510666d
А вы уже начали менять свои лайвдаты на StateFlow?
В Room добавили auto-migrations:
https://medium.com/androiddevelopers/room-auto-migrations-d5370b0ca6eb
Сравнение композиции и наследования в Kotlin:
https://kt.academy/article/ek-composition
Пример мультиплатформенного аппа:
https://github.com/joreilly/StarWars
Либа-сахарок для инициализации Android Lifecycle-aware свойств:
https://github.com/skydoves/Lazybones
Выбор девайса для работы всегда актуален. Небольшое сравнение скорости компиляции и нагрева Apple M1 с i7:
https://proandroiddev.com/apple-m1-vs-intel-the-ultimate-comparison-2a2f0d197dc
Разница впечатляет!
Compose. Тут как всегда куча всего.
Понеслась!
Начали клепать либы для Compose. Вот, например, либа для пунктов настроек:
https://github.com/alorma/Compose-Settings
Очередной пример использования Compose:
https://github.com/rock3r/Bundel/
Хороший пошаговый туториал по созданию списка с данными на Compose для начинающих:
https://www.waseefakhtar.com/android/recyclerview-in-jetpack-compose/
И еще один:
https://medium.com/mateedevs/bye-xml-it-was-nice-knowing-you-pt-1-50b195bab1a9
Пост с официального блога. Крис Бейнс сравнил разницу в размере приложения (количество строк + размер APK) и скорости сборки после миграции на Compose:
https://medium.com/androiddevelopers/jetpack-compose-before-and-after-8b43ba0b7d4f
И кстати, в репозитории Криса можно увидеть архитектуру вместе с Compose (ViewModels, навигация и прочее) - https://github.com/chrisbanes/tivi
Рекомендую!
Сравнение LiveData и Flow:
https://proandroiddev.com/flow-livedata-what-are-they-best-use-case-lets-build-a-login-system-39315510666d
А вы уже начали менять свои лайвдаты на StateFlow?
В Room добавили auto-migrations:
https://medium.com/androiddevelopers/room-auto-migrations-d5370b0ca6eb
Сравнение композиции и наследования в Kotlin:
https://kt.academy/article/ek-composition
Пример мультиплатформенного аппа:
https://github.com/joreilly/StarWars
Либа-сахарок для инициализации Android Lifecycle-aware свойств:
https://github.com/skydoves/Lazybones
Приятные новости подъехали!
В этом году (18-20 мая) Google IO пройдет онлайн и бесплатно для всех.
Ссылочка со счетчиком: https://events.google.com/io/?lng=en
Добавляйте к себе в календарь, чтобы не забыть.
В этом году (18-20 мая) Google IO пройдет онлайн и бесплатно для всех.
Ссылочка со счетчиком: https://events.google.com/io/?lng=en
Добавляйте к себе в календарь, чтобы не забыть.
Пост 13. Вытаскиваем аргументы фрагмента в одну строчку с помощью delegated properties
Магия Котлина помогает писать более лаконичный код, который можно легко повторно использовать в новых проектах. Например, можно воспользоваться delegated properties и написать небольшой, но удобный сахарок для получения аргументов фрагмента прямиком в поле класса всего одной строчкой:
Обычно, маленькие функции и функции-расширения (extensions) стоит помечать как inline. Особенно выигрыш происходит для extension с параметром в виде блока замыкания, где благодаря встраиванию не создаются дополнительные классы-обертки. Однако стоит помнить, что количество встраиваемых функций все равно ограничено и абсолютно все функции помечтать встраиваемыми нет смысла.
Ключевое слово reified позволяет нам подставить тип без использования рефлексии ("овеществлять") и может быть использовано только во встроенных (inline) функциях. Нет рефлексии - код безопаснее и чуть быстрее.
Функция вытаскивает из класса Bundle значение по ключу, либо подставляет значение по-умолчанию если ключа нет. В конце проверяет тип, который передается в функцию с типом значение, которое получено по ключу. На случай если передаете Int, а вытащить хотите Double.
Поехали дальше.
Теперь нам нужен делегат, который реализует интерфейс ReadWriteProperty (реализует операторы для присваивания и получения значений).
И собирает все воедино функция argument, которая как раз возвращает наш делегат:
Больше примеров использования делегируемых свойств в официальной доке:
https://kotlinlang.org/docs/delegated-properties.html#providing-a-delegate
Магия Котлина помогает писать более лаконичный код, который можно легко повторно использовать в новых проектах. Например, можно воспользоваться delegated properties и написать небольшой, но удобный сахарок для получения аргументов фрагмента прямиком в поле класса всего одной строчкой:
private val phone: String by argument(ARG_PHONE)
Давайте напишем для начала функцию, которая будет вытаскивать из поля arguments значения по ключу:inline fun <reified T> extractFromBundle(
bundle: Bundle?,
key: String,
defaultValue: T? = null
): T {
val result = bundle?.get(key) ?: defaultValue
if (result != null && result !is T) {
throw ClassCastException("Property $key has different class type")
}
return result as T
}
Ключевое слово inline как подсказка компилятору, что мы хотим сделать функцию встраиваемой (тело функции встраивалось при вызове целиком).Обычно, маленькие функции и функции-расширения (extensions) стоит помечать как inline. Особенно выигрыш происходит для extension с параметром в виде блока замыкания, где благодаря встраиванию не создаются дополнительные классы-обертки. Однако стоит помнить, что количество встраиваемых функций все равно ограничено и абсолютно все функции помечтать встраиваемыми нет смысла.
Ключевое слово reified позволяет нам подставить тип без использования рефлексии ("овеществлять") и может быть использовано только во встроенных (inline) функциях. Нет рефлексии - код безопаснее и чуть быстрее.
Функция вытаскивает из класса Bundle значение по ключу, либо подставляет значение по-умолчанию если ключа нет. В конце проверяет тип, который передается в функцию с типом значение, которое получено по ключу. На случай если передаете Int, а вытащить хотите Double.
Поехали дальше.
Теперь нам нужен делегат, который реализует интерфейс ReadWriteProperty (реализует операторы для присваивания и получения значений).
class BundleExtractorDelegate<R, T>(private val initializer: (R) -> T) : ReadWriteProperty<R, T> {
private object EMPTY
private var value: Any? = EMPTY
override fun setValue(thisRef: R, property: KProperty<*>, value: T) {
this.value = value
}
override fun getValue(thisRef: R, property: KProperty<*>): T {
if (value == EMPTY) {
value = initializer(thisRef)
}
@Suppress("UNCHECKED_CAST")
return value as T
}
}
Здесь ничего особенного. Реализация интерфейса делегата в котором мы будем вызывать нашу написанную функцию.И собирает все воедино функция argument, которая как раз возвращает наш делегат:
inline fun <reified T> argument(
key: String,
defaultValue: T? = null
): ReadWriteProperty<Fragment, T> = BundleExtractorDelegate { thisRef ->
extractFromBundle(
bundle = thisRef.arguments,
key = key,
defaultValue = defaultValue
)
}
Мы передаем в первый параметр типа класс Fragment и можем обратиться к полю arguments, а далее вся нужная работа происходит в функции extractFromBundle. Больше примеров использования делегируемых свойств в официальной доке:
https://kotlinlang.org/docs/delegated-properties.html#providing-a-delegate
Kotlin Help
Delegated properties | Kotlin
Находясь в отпуске хочется оглянуться назад и порассуждать. Благо под рукой есть интересное чтиво этому способствующее.
Сейчас мы ведём два проекта, при чем оба приложения банковские и наглядны два разных подхода.
В первом вообще нет процессов и все свои боли пытаются решить в виде звонков в Дискорд (я честно говоря до сих пор не понимаю зачем это приложение использовать в разработке, ну да ладно). Звонки оказывается пишутся, но разработчики не получают доступа к записям. Задачи пишутся в гитлабе, в виде строчки с заголовком. Никакого описания нет. API которое делается просто проксирует данные от API банка. До недавнего времени было именно так. Но после увольнения бекендера, который отвечал на наши вопросы через раз, а чаще всего даже не отвечал во все (в общем чате и с тегом его ника) стало немного лучше и коммуникации какие никакие с беком появились. Релиз уже откладывался раз 5. Проблем выше крыши. Делает это все богатство на коленке суммарно 2 андроид, 2 айос, человека 3-4 бек и непонятный то ли менеджер продукта, то ли овнер (никто никого не представлял да и вообще я подобный хаос первый раз вижу за 10 лет)
И второй банк, в котором множество мелких команд, с аналитиками, докой и нормальными задачами в Jira. Да, в нем есть другая проблема - множество согласований, звонков и ненужных встреч, но выглядит это все более целостно.
В нем вы меньше кодите, много общаетесь с коллегами. Не всем такое понравится. Но общение не пустое. С другой стороны, что будь больше гибкости, то и людей и общения станет меньше, да и Java в 2021 уже была бы переписана в проекте. Но это не так плачевно, как в проекте #1.
И как вы думаете у какого банка больше шансов выжить? Второй, нужно сказать уже давно в проде и вы его скорее всего знаете (топ 10 России).
Так вот о чем это я?
Авторы книги рассуждают, о том как быть программистом-прагматиком, призывают больше слушать коллег и продукт-овнеров, но блин что делать, когда люди вообще не понимают, что такое процесс разработки? Мы очень устали от этих товарищей. Отношение к нам удручающее, и самое странное - они при этом делают финансовое приложение.
Честно говоря, пока не понятно, что делать. Пытался с ними поговорить на эту тему, но если директор даже подписанный акт скинуть не может, то как вы думаете каков результат?
Сейчас тенденция не писать доку, а делать все на коленке встречается все чаще и чаще. Часто мы вытягиваем спеки напрямую из технического директора или из дизайнеров, которые рисовали диз после встреч с продуктами. И здесь хочется подчеркнуть ключевую разницу. Всегда должны быть адекватные люди, которые вам ответят и максимально подробно расскажут о том, что нужно сделать. Именно к такой коммуникации и нужно стремиться. Возможно в начале пути вы будет работать и в хаосе о котором я рассказал выше, но лучше от таких контор теряться как можно дальше. Кроме платы за ипотеку она вам ничем не поможет. Как ни крути долго работать в таком хаосе не получится. Виноваты окажутся программисты.
Самый адекватный пример отсутствия спеки на проект - это продуманный дизайн и люди у которых всегда можно узнать нужные ответы. И чем больше вы будете задавать разных вопросов тем качественнее получите код. Поставите себя на место клиента и продумаете заранее все риски. Лучше конечно если будет какая никакая спека для разработчиков, но сейчас это встречается все реже и реже и нужно быть к этому готовыми.
А в книге множество советов как стать продуктивнее и прагматичнее. Однозначно стоит вашего внимания. Я лишь решил задеть одну тему из нее - коммуникации и их важность.
Не скучайте. Приеду и сделаем обзор новых плюшек с IO.
Сейчас мы ведём два проекта, при чем оба приложения банковские и наглядны два разных подхода.
В первом вообще нет процессов и все свои боли пытаются решить в виде звонков в Дискорд (я честно говоря до сих пор не понимаю зачем это приложение использовать в разработке, ну да ладно). Звонки оказывается пишутся, но разработчики не получают доступа к записям. Задачи пишутся в гитлабе, в виде строчки с заголовком. Никакого описания нет. API которое делается просто проксирует данные от API банка. До недавнего времени было именно так. Но после увольнения бекендера, который отвечал на наши вопросы через раз, а чаще всего даже не отвечал во все (в общем чате и с тегом его ника) стало немного лучше и коммуникации какие никакие с беком появились. Релиз уже откладывался раз 5. Проблем выше крыши. Делает это все богатство на коленке суммарно 2 андроид, 2 айос, человека 3-4 бек и непонятный то ли менеджер продукта, то ли овнер (никто никого не представлял да и вообще я подобный хаос первый раз вижу за 10 лет)
И второй банк, в котором множество мелких команд, с аналитиками, докой и нормальными задачами в Jira. Да, в нем есть другая проблема - множество согласований, звонков и ненужных встреч, но выглядит это все более целостно.
В нем вы меньше кодите, много общаетесь с коллегами. Не всем такое понравится. Но общение не пустое. С другой стороны, что будь больше гибкости, то и людей и общения станет меньше, да и Java в 2021 уже была бы переписана в проекте. Но это не так плачевно, как в проекте #1.
И как вы думаете у какого банка больше шансов выжить? Второй, нужно сказать уже давно в проде и вы его скорее всего знаете (топ 10 России).
Так вот о чем это я?
Авторы книги рассуждают, о том как быть программистом-прагматиком, призывают больше слушать коллег и продукт-овнеров, но блин что делать, когда люди вообще не понимают, что такое процесс разработки? Мы очень устали от этих товарищей. Отношение к нам удручающее, и самое странное - они при этом делают финансовое приложение.
Честно говоря, пока не понятно, что делать. Пытался с ними поговорить на эту тему, но если директор даже подписанный акт скинуть не может, то как вы думаете каков результат?
Сейчас тенденция не писать доку, а делать все на коленке встречается все чаще и чаще. Часто мы вытягиваем спеки напрямую из технического директора или из дизайнеров, которые рисовали диз после встреч с продуктами. И здесь хочется подчеркнуть ключевую разницу. Всегда должны быть адекватные люди, которые вам ответят и максимально подробно расскажут о том, что нужно сделать. Именно к такой коммуникации и нужно стремиться. Возможно в начале пути вы будет работать и в хаосе о котором я рассказал выше, но лучше от таких контор теряться как можно дальше. Кроме платы за ипотеку она вам ничем не поможет. Как ни крути долго работать в таком хаосе не получится. Виноваты окажутся программисты.
Самый адекватный пример отсутствия спеки на проект - это продуманный дизайн и люди у которых всегда можно узнать нужные ответы. И чем больше вы будете задавать разных вопросов тем качественнее получите код. Поставите себя на место клиента и продумаете заранее все риски. Лучше конечно если будет какая никакая спека для разработчиков, но сейчас это встречается все реже и реже и нужно быть к этому готовыми.
А в книге множество советов как стать продуктивнее и прагматичнее. Однозначно стоит вашего внимания. Я лишь решил задеть одну тему из нее - коммуникации и их важность.
Не скучайте. Приеду и сделаем обзор новых плюшек с IO.
Новое с Google IO 2021. Инструменты разработки.
Ну что, друзья? Начнем погружаться в свежие новости с прошедшего Google IO?
Интересного, конечно, не так много.
Первым делом посмотрим, что нового принесет нам свежая версия Android Studio (на момент поста еще пока Beta 3), которая теперь получила свое отдельное имя - Arctic Fox.
Коротко и тезисно, а к посту прикреплена картинка на заметку со всеми изменениями.
* Обновили платформу IntelliJ на версию 2020.3.1
* Добавили Compose Design Preview с настройками (ориентация экрана, шрифт, ширина и высота), но нужно добавлять аннотацию @Preview. Можно будет просматривать изменения в дизайне "на лету" (поменяли условно текст в коде и изменения сразу будут видны в Preview). Наверное, самое интересное из всего списка.
* Добавили возможность просматривать векторную анимацию (ресурс animated-vector)
* Layout Validation - просмотр корректности макетов на разных разрешениях (одобряю)
* В эмуляторе добавили более удобную отладку датчиков
* Snapshots for test failures: добавив в builde.gradle опцию failureRetentation после запуска тестов можно видеть снепшоты (состояния) эмулятора на проваленных тестах
* Work Manager Inspector: поможет отлаживать ваши асинхронные задачи, запущенные через WorkManager. Посмотреть какая запущена, какие данные есть и прочее (показывает даже граф задач)
* Новые плюшки рефакторинга: migrate to non transitive R classes, refactoring preview для просмотра сделанных изменений (вот это точно одобряю)
* Android 12 Lint checks - новые правила и обновленный Lint
* Станет доступен Coroutines debugger - отладка корутин станет немного проще (давно пора бы)
Вообще, судя по разным видео с конфы, которые успел посмотреть - нас довольно активно готовят к переходу на Compose. Многие библиотеки получили привязки к Compose и пообещали совсем скоро версию Compose 1.0. Пожалуй, это основной посыл всего Google IO для Android.
Ну что, друзья? Начнем погружаться в свежие новости с прошедшего Google IO?
Интересного, конечно, не так много.
Первым делом посмотрим, что нового принесет нам свежая версия Android Studio (на момент поста еще пока Beta 3), которая теперь получила свое отдельное имя - Arctic Fox.
Коротко и тезисно, а к посту прикреплена картинка на заметку со всеми изменениями.
* Обновили платформу IntelliJ на версию 2020.3.1
* Добавили Compose Design Preview с настройками (ориентация экрана, шрифт, ширина и высота), но нужно добавлять аннотацию @Preview. Можно будет просматривать изменения в дизайне "на лету" (поменяли условно текст в коде и изменения сразу будут видны в Preview). Наверное, самое интересное из всего списка.
* Добавили возможность просматривать векторную анимацию (ресурс animated-vector)
* Layout Validation - просмотр корректности макетов на разных разрешениях (одобряю)
* В эмуляторе добавили более удобную отладку датчиков
* Snapshots for test failures: добавив в builde.gradle опцию failureRetentation после запуска тестов можно видеть снепшоты (состояния) эмулятора на проваленных тестах
* Work Manager Inspector: поможет отлаживать ваши асинхронные задачи, запущенные через WorkManager. Посмотреть какая запущена, какие данные есть и прочее (показывает даже граф задач)
* Новые плюшки рефакторинга: migrate to non transitive R classes, refactoring preview для просмотра сделанных изменений (вот это точно одобряю)
* Android 12 Lint checks - новые правила и обновленный Lint
* Станет доступен Coroutines debugger - отладка корутин станет немного проще (давно пора бы)
Вообще, судя по разным видео с конфы, которые успел посмотреть - нас довольно активно готовят к переходу на Compose. Многие библиотеки получили привязки к Compose и пообещали совсем скоро версию Compose 1.0. Пожалуй, это основной посыл всего Google IO для Android.
Новое с Google IO 2021. JetPack.
Продолжаем смотреть, что нового нам рассказали на Google IO. Теперь пройдемся немного по JetPack. Напомню, JetPack - набор библиотек от Google, который помогает ускорить разработку. Заточено все в основном под гугловский MVVM с ViewModel и LiveData (теперь уже StateFlow). По большому счету они растянули многие core классы в отдельные пакеты, что упрощает обновление и поддержку библиотек и бонусом делает меньше размер приложения. Раньше был огромный Support AppCompat в котором подключалось и все что нужно и все что нет.
А нового в JetPack, кстати, не так много:
* Обновили Experimental annotations
* Добавили LifecycleOwner для CameraX
* Добавили либу AppSearch для создания поисковых запросов из локальных данных приложения (поддерживает full-text search, индексацию).
Подробнее: https://developer.android.com/guide/topics/search/appsearch?hl=ru
* Добавили Security Crypto - либа для шифрования файлов и shared preferences
* Выкатили стабильные версии библиотек: Hilt, DataStore, WorkManager. Теперь наконец-то можно внедрять Hilt и не бояться, что все развалится с новой минорной версией, как однажды у меня было в пет-проекте.
* Довольно много изменений в Room:
- Experimental support for Kotlin Symbol Processing
- Появилась встроенная поддержка энамов
- Поддержка RxJava3
- Добавили QueryCallback для разных задач после/во время выполнения запроса к БД (например, можно добавить логгирование)
- @ProvidedTypeConverter для конвертеров типов
* Activity Result API (с версии androidx.fragment 1.3.0). Вообще, не новинка, но почему-то упоминается Гуглом как что-то новое.
Подробнее: https://developer.android.com/training/basics/intents/result
* Google Shortcuts library.
Подробнее: https://developer.android.com/guide/topics/ui/shortcuts/creating-shortcuts#gsi-library
* Обновление в Paging (библиотека для пагинации): добавили поддержку Coroutines and Flow, поддержку для headers, footers, dividers
* Добавили привязки к Compose в: Hilt, Paging, Navigation, Activity, ViewModel, ConstraintLayout (грядет эра еще большего говнокода 😞)
Кстати, изменения по либам JetPack можно смотреть по этой доке:
https://developer.android.com/jetpack/androidx/versions
Продолжаем смотреть, что нового нам рассказали на Google IO. Теперь пройдемся немного по JetPack. Напомню, JetPack - набор библиотек от Google, который помогает ускорить разработку. Заточено все в основном под гугловский MVVM с ViewModel и LiveData (теперь уже StateFlow). По большому счету они растянули многие core классы в отдельные пакеты, что упрощает обновление и поддержку библиотек и бонусом делает меньше размер приложения. Раньше был огромный Support AppCompat в котором подключалось и все что нужно и все что нет.
А нового в JetPack, кстати, не так много:
* Обновили Experimental annotations
* Добавили LifecycleOwner для CameraX
* Добавили либу AppSearch для создания поисковых запросов из локальных данных приложения (поддерживает full-text search, индексацию).
Подробнее: https://developer.android.com/guide/topics/search/appsearch?hl=ru
* Добавили Security Crypto - либа для шифрования файлов и shared preferences
* Выкатили стабильные версии библиотек: Hilt, DataStore, WorkManager. Теперь наконец-то можно внедрять Hilt и не бояться, что все развалится с новой минорной версией, как однажды у меня было в пет-проекте.
* Довольно много изменений в Room:
- Experimental support for Kotlin Symbol Processing
- Появилась встроенная поддержка энамов
- Поддержка RxJava3
- Добавили QueryCallback для разных задач после/во время выполнения запроса к БД (например, можно добавить логгирование)
- @ProvidedTypeConverter для конвертеров типов
* Activity Result API (с версии androidx.fragment 1.3.0). Вообще, не новинка, но почему-то упоминается Гуглом как что-то новое.
Подробнее: https://developer.android.com/training/basics/intents/result
* Google Shortcuts library.
Подробнее: https://developer.android.com/guide/topics/ui/shortcuts/creating-shortcuts#gsi-library
* Обновление в Paging (библиотека для пагинации): добавили поддержку Coroutines and Flow, поддержку для headers, footers, dividers
* Добавили привязки к Compose в: Hilt, Paging, Navigation, Activity, ViewModel, ConstraintLayout (грядет эра еще большего говнокода 😞)
Кстати, изменения по либам JetPack можно смотреть по этой доке:
https://developer.android.com/jetpack/androidx/versions
Android Developers
AppSearch | Android Developers
Новое с Google IO 2021. Android 12.
И напоследок изменения в новой Android 12:
* Deprecated @Deprecated
* Новые цвета в теме DayNight
* Rounded corners API
* Много изменений по виджетам рабочего стола, в том числе refreshing widgets.
Подробнее: https://developer.android.com/about/versions/12/features/widgets
* Добавили анимации сплеш-скрина. Любое приложение теперь с анимацией сплеш-скрина, которой можно управлять через метод Activity.getSplashScreen()
* Небольшая пачка изменений по допиливанию нотификаций
* Picture in Picture: теперь возврат на Home через Swipe Up жест
* Добавили блюрэффект из коробки: imageView.setRenderEffect(RenferEffect.createBlurEffect()) и атрибуты темы windowBlurBehindEnabledRadius, windowBlurBehindRadius, windowBackgroundBlurRadius можно делать даже заблюренные виджеты рабочего стола.
* Добавили новый ripple effect (довольно странный)
* Новый Edge effect (элемент меняет размер, так называемый stretch effect)
* Поддержка нового формата графики AVIIF
* Audio-coupled Haptic Playback для треков (не на всех девайсах доступно)
* Privacy: добавили новые permissions на сканирование и подключение к Bluetooth, новые опции для Location permission, Toast для копирования данных из клипборда, foreground restrictions (Expedited Jobs: WorkRequest.Builder.setExpedited ->
Pre android 12 -> Foreground services
Android 12 -> JobInfo.Builder.setExpedited())
* Объединили несколько обработчиков контента в один - setOnRecieveContentListener (один лисенер для drag & drop, keyboard stickers, copy & paste menu)
* Добавили библиотеку для замера производительности (теперь можно замерять скорость старта приложения).
Подробнее: https://developer.android.com/studio/profile/benchmark
Полный список изменений здесь: https://developer.android.com/about/versions/12/features
И напоследок изменения в новой Android 12:
* Deprecated @Deprecated
* Новые цвета в теме DayNight
* Rounded corners API
* Много изменений по виджетам рабочего стола, в том числе refreshing widgets.
Подробнее: https://developer.android.com/about/versions/12/features/widgets
* Добавили анимации сплеш-скрина. Любое приложение теперь с анимацией сплеш-скрина, которой можно управлять через метод Activity.getSplashScreen()
* Небольшая пачка изменений по допиливанию нотификаций
* Picture in Picture: теперь возврат на Home через Swipe Up жест
* Добавили блюрэффект из коробки: imageView.setRenderEffect(RenferEffect.createBlurEffect()) и атрибуты темы windowBlurBehindEnabledRadius, windowBlurBehindRadius, windowBackgroundBlurRadius можно делать даже заблюренные виджеты рабочего стола.
* Добавили новый ripple effect (довольно странный)
* Новый Edge effect (элемент меняет размер, так называемый stretch effect)
* Поддержка нового формата графики AVIIF
* Audio-coupled Haptic Playback для треков (не на всех девайсах доступно)
* Privacy: добавили новые permissions на сканирование и подключение к Bluetooth, новые опции для Location permission, Toast для копирования данных из клипборда, foreground restrictions (Expedited Jobs: WorkRequest.Builder.setExpedited ->
Pre android 12 -> Foreground services
Android 12 -> JobInfo.Builder.setExpedited())
* Объединили несколько обработчиков контента в один - setOnRecieveContentListener (один лисенер для drag & drop, keyboard stickers, copy & paste menu)
* Добавили библиотеку для замера производительности (теперь можно замерять скорость старта приложения).
Подробнее: https://developer.android.com/studio/profile/benchmark
Полный список изменений здесь: https://developer.android.com/about/versions/12/features
Android Developers
Android 12 widgets improvements | Android Developers
Эксперименты с Compose. Часть 1.
Начинаем новый цикл постов, который посвящен Compose от которого нам, видимо, никуда не деться.
План примерно таков:
1. Накидать быстро базовый UI
2. Отрефакторить то, что получилось в пункте 1.
3. Добавить несколько архитектур и разбить все грамотно, а не как в классических постах, где пример делается ради примера. Добавим сперва гугловый MVVM, потом попробуем MVI и в конце возможно классический и всеми любимый MVP. Пока сложно сказать понадобится ли нам разбиение на модули или слои по Clean, но добавим в дальнейшем и его.
4. Посмотреть как все это тестируется. Добавим тестирование.
За основу UI взят главный экран моего приложения. Его вторая версия как раз в разработке и отдельно любопытно насколько быстро этот экран можно переписать на Compose.
Также, в качестве вводной стоит отметить, что Compose до этого не изучал.
По итогу первого захода потрачено примерно 2,5 часа. Результат справа и результат довольно скромный.
По впечатлениям все довольно не обычно. Приходится перестраивать мозг, ибо теперь весь UI в коде.
Что понравилось:
* Понравилось как работает Preview в Android Studio. Build -> refresh и уже виден результат, при этом не нужен ни эмулятор ни устройство.
* Можно быстро накидать UI (быстро при условии, что вы хорошо въехали в основы).
* Composable-функции можно довольно гибко раскидывать по коду и переиспользовать (не только в текущем проекте).
* В теории, появятся тулзы, которые будут генерировать UI-код. Скорее всего к этому все и делалось.
* Можно быстро изучить основы (особенно по примерам кода)
Что не понравилось:
* Гуглобаги. Чтобы подключить и использовать ConstraintLayout пришлось обновить версию Kotlin и самого Compose (на моей текущей был краш), добавить отдельную либу.
* Видеть весь код UI в обычных .kt файлах проекта очень странно. Скорее всего дело привычки, но без четкой структуры явно будет каша от проекта к проекту.
* Пока не понятно как использовать constraints в дочерних элементах. Надо разбираться, сходу не вышло.
* Прописал в теме цвет фона, а при использовании Scaffold он перетерся. Нигде в доке не увидел почему так.
* Не хватает подробной доки. Благо есть куча примеров кода, которые и приходится смотреть.
Мой репозиторий:
https://github.com/Djangist/ComposeArchSample
Гугло-примеры:
https://github.com/android/compose-samples/
Интересный PlayGround:
https://foso.github.io/Jetpack-Compose-Playground/
Начинаем новый цикл постов, который посвящен Compose от которого нам, видимо, никуда не деться.
План примерно таков:
1. Накидать быстро базовый UI
2. Отрефакторить то, что получилось в пункте 1.
3. Добавить несколько архитектур и разбить все грамотно, а не как в классических постах, где пример делается ради примера. Добавим сперва гугловый MVVM, потом попробуем MVI и в конце возможно классический и всеми любимый MVP. Пока сложно сказать понадобится ли нам разбиение на модули или слои по Clean, но добавим в дальнейшем и его.
4. Посмотреть как все это тестируется. Добавим тестирование.
За основу UI взят главный экран моего приложения. Его вторая версия как раз в разработке и отдельно любопытно насколько быстро этот экран можно переписать на Compose.
Также, в качестве вводной стоит отметить, что Compose до этого не изучал.
По итогу первого захода потрачено примерно 2,5 часа. Результат справа и результат довольно скромный.
По впечатлениям все довольно не обычно. Приходится перестраивать мозг, ибо теперь весь UI в коде.
Что понравилось:
* Понравилось как работает Preview в Android Studio. Build -> refresh и уже виден результат, при этом не нужен ни эмулятор ни устройство.
* Можно быстро накидать UI (быстро при условии, что вы хорошо въехали в основы).
* Composable-функции можно довольно гибко раскидывать по коду и переиспользовать (не только в текущем проекте).
* В теории, появятся тулзы, которые будут генерировать UI-код. Скорее всего к этому все и делалось.
* Можно быстро изучить основы (особенно по примерам кода)
Что не понравилось:
* Гуглобаги. Чтобы подключить и использовать ConstraintLayout пришлось обновить версию Kotlin и самого Compose (на моей текущей был краш), добавить отдельную либу.
* Видеть весь код UI в обычных .kt файлах проекта очень странно. Скорее всего дело привычки, но без четкой структуры явно будет каша от проекта к проекту.
* Пока не понятно как использовать constraints в дочерних элементах. Надо разбираться, сходу не вышло.
* Прописал в теме цвет фона, а при использовании Scaffold он перетерся. Нигде в доке не увидел почему так.
* Не хватает подробной доки. Благо есть куча примеров кода, которые и приходится смотреть.
Мой репозиторий:
https://github.com/Djangist/ComposeArchSample
Гугло-примеры:
https://github.com/android/compose-samples/
Интересный PlayGround:
https://foso.github.io/Jetpack-Compose-Playground/
Пост 14. Parcelable vs Serializable.
В Android часто приходят разработчики из мира Java SE / Java EE и как только возникает задача сделать сериализацию для модельки, первое, что приходит на ум - использовать уже знакомый интерфейс Serializable. Однако в Android лучше использовать Parcelable для большинства задач, тем более что сейчас это стало значительно проще.
Самый частый пример использования сериализации - это передача сложной модели из одного экрана в другой. Например, есть список транзакций и при тапе на транзакцию нужно передать модель для экрана просмотра деталей. Для передачи модели между экранами (через Bundle) нам и нужна сериализация.
В чем проблема Serializable?
1. Рефлексия. Соответственно не очень быстро. Поля объекта конвертируются в байтовый поток при сериализации.
2. Поле serialVersionUID. Если поле не задано, то механизм сериализации сам будет вычислять его дефолтное значение, поэтому рекомендуется всегда добавлять это поле в свой класс. Но во-первых, очень просто забыть это сделать, а во-вторых, некоторые классы содержат только дефолтное значение и с расчетом значения для вашего класса могут быть проблемы.
3. Придуман в основном для сериализации данных на диске или отправки через сеть, но и для этих кейсов можно рассмотреть другие аналоги (Externalizable, ObjectInputStream и даже GSON)
В отличии от Serializable Parcel как раз реализован для межпроцессного взаимодействия (IPC). Поэтому, самое лучшее решение в случае передачи данных между экранами использовать Parcelable.
Раньше нужно было вручную реализовывать этот интерфейс, потом уже Android Studio через подсказку научилась подставлять дефолтную его реализацию, появились библиотеки (например Parceler), но сейчас лучше всего (и проще) использовать плагин kotlin-parcelize.
Добавили плагин в app/build.gradle:
Подробнее о плагине: https://developer.android.com/kotlin/parcelize
В Android часто приходят разработчики из мира Java SE / Java EE и как только возникает задача сделать сериализацию для модельки, первое, что приходит на ум - использовать уже знакомый интерфейс Serializable. Однако в Android лучше использовать Parcelable для большинства задач, тем более что сейчас это стало значительно проще.
Самый частый пример использования сериализации - это передача сложной модели из одного экрана в другой. Например, есть список транзакций и при тапе на транзакцию нужно передать модель для экрана просмотра деталей. Для передачи модели между экранами (через Bundle) нам и нужна сериализация.
В чем проблема Serializable?
1. Рефлексия. Соответственно не очень быстро. Поля объекта конвертируются в байтовый поток при сериализации.
2. Поле serialVersionUID. Если поле не задано, то механизм сериализации сам будет вычислять его дефолтное значение, поэтому рекомендуется всегда добавлять это поле в свой класс. Но во-первых, очень просто забыть это сделать, а во-вторых, некоторые классы содержат только дефолтное значение и с расчетом значения для вашего класса могут быть проблемы.
3. Придуман в основном для сериализации данных на диске или отправки через сеть, но и для этих кейсов можно рассмотреть другие аналоги (Externalizable, ObjectInputStream и даже GSON)
В отличии от Serializable Parcel как раз реализован для межпроцессного взаимодействия (IPC). Поэтому, самое лучшее решение в случае передачи данных между экранами использовать Parcelable.
Раньше нужно было вручную реализовывать этот интерфейс, потом уже Android Studio через подсказку научилась подставлять дефолтную его реализацию, появились библиотеки (например Parceler), но сейчас лучше всего (и проще) использовать плагин kotlin-parcelize.
Добавили плагин в app/build.gradle:
plugins {
Id: 'kotlin-parcelize'
}
Потом добавили к модельке аннотацию @Parcelize и само собой интерфейс Parcelable:@Parcelize
data class Transaction(val transactionId: Int, val userId: String, val sum: Double): Parcelable
И все у вас хорошо.Подробнее о плагине: https://developer.android.com/kotlin/parcelize
Android Developers
Parcelable implementation generator | Kotlin | Android Developers
Пост 15. Kotlin Nothing.
В Kotlin есть два типа, которые часто можно спутать между собой: Unit и Nothing. Можно подумать, что оба не возвращают ничего. Но между ними есть существенная разница (оба типа наследники от Any). В случае Nothing понятие возвращаемого значения просто не имеет смысла. Не смотря на то, что практических применений у Nothing около двух (или трех), об этом типе часто любят спрашивать на собеседованиях.
Функции/методы, которые возвращают Nothing не возвращают управление. Самый простой пример для понимания - это функции с бесконечным циклом. Пока какая-то логика внутри цикла не сработает и не остановит цикл - код после цикла не выполнится.
Какой практический смысл у Nothing?
Например, валидация полей. У вас есть функция, кидающая пользовательское исключение в случае какой-либо ошибки:
Такая функция позволит нам писать выражения вида:
Компилятор знает, что функция с таким типом не вернет управления, и использует эту информацию при анализе кода вызова функции. Если бы функция showError возвращала Unit, подобное выражение написать не получилось бы.
Кстати, стандартная функция TODO(), которая часто подставляется средой при генерации кода, реализована также с помощью Nothing:
В Kotlin есть два типа, которые часто можно спутать между собой: Unit и Nothing. Можно подумать, что оба не возвращают ничего. Но между ними есть существенная разница (оба типа наследники от Any). В случае Nothing понятие возвращаемого значения просто не имеет смысла. Не смотря на то, что практических применений у Nothing около двух (или трех), об этом типе часто любят спрашивать на собеседованиях.
Функции/методы, которые возвращают Nothing не возвращают управление. Самый простой пример для понимания - это функции с бесконечным циклом. Пока какая-то логика внутри цикла не сработает и не остановит цикл - код после цикла не выполнится.
Какой практический смысл у Nothing?
Например, валидация полей. У вас есть функция, кидающая пользовательское исключение в случае какой-либо ошибки:
fun showError(message: String): Nothing {
// перед этим можно еще аналитику добавить или логгирование
throw ValidationException(message)
}
Такая функция позволит нам писать выражения вида:
val name = userResponse.name ?: showError("No name field")
Компилятор знает, что функция с таким типом не вернет управления, и использует эту информацию при анализе кода вызова функции. Если бы функция showError возвращала Unit, подобное выражение написать не получилось бы.
Кстати, стандартная функция TODO(), которая часто подставляется средой при генерации кода, реализована также с помощью Nothing:
@kotlin.internal.InlineOnly
public inline fun TODO(reason: String): Nothing = throw NotImplementedError("An operation is not implemented: $reason")Наконец-то дожили до момента, когда в Navigation Component добавили поддержку multiple back stacks и можно даже задуматься о выпиливании сторонних библиотек для роутинга.
Пока еще в альфе.
Менять в коде ничего не нужно. Достаточно обновить либу фрагментов до 1.4.0-alpha01 и собственно сам navigation до 2.4.0-alpha01.
Детали:
https://www.youtube.com/watch?v=Covu3fPA1nQ
https://medium.com/androiddevelopers/multiple-back-stacks-b714d974f134
Пока еще в альфе.
Менять в коде ничего не нужно. Достаточно обновить либу фрагментов до 1.4.0-alpha01 и собственно сам navigation до 2.4.0-alpha01.
Детали:
https://www.youtube.com/watch?v=Covu3fPA1nQ
https://medium.com/androiddevelopers/multiple-back-stacks-b714d974f134
YouTube
Navigation: Multiple back stacks - MAD Skills
In this episode, Developer Advocate Murat Yener will dive into a highly requested feature, multiple back stack support for Navigation.
Blog post version → https://goo.gle/3gq2PJm
Repo → https://goo.gle/3pTHXNp
Multiple back stacks: A deep dive into what…
Blog post version → https://goo.gle/3gq2PJm
Repo → https://goo.gle/3pTHXNp
Multiple back stacks: A deep dive into what…
Просто оставлю эту ссылку здесь: https://refactoring.guru/ru/design-patterns
Паттерны проектирования доступным языком с примерами кода, а также описание техник рефакторинга.
И все это с удобной навигацией.
Сохраняйте в закладки, если вдруг еще не слышали о нем.
Паттерны проектирования доступным языком с примерами кода, а также описание техник рефакторинга.
И все это с удобной навигацией.
Сохраняйте в закладки, если вдруг еще не слышали о нем.
refactoring.guru
Паттерны/шаблоны проектирования
Паттерны проектирования описывают типичные способы решения часто встречающихся проблем при проектировании программ.
Что новенького в рассылках #4
Как вы уже догадались, большинство тем в рассылках еще долго будут посвящены Compose, с которым и я сейчас активно балуюсь.
Что интересного из статей?
Аналогичный моему опыту пример переноса UI из xml на Compose:
https://proandroiddev.com/migration-to-compose-c6eb63f187f9
Самое пока странное - это передача ViewModel в параметры composable-функций, чтобы можно было корректно оперировать стейтом (состоянием UI-элементов). Как раз на этом этапе я немного застрял, потому что передавая вьюмодель в параметры мы теряем слабую связность между частями приложения и хочется попробовать разные варианты.
Пример, кстати, живой: с Hilt и намёком на архитектуру. Отдельно порадовало упоминание в статьей сторонней лимбы Гугла для Compose - Appcompanist в которой есть SwipteToRefresh, Pager и другие часто используемые компоненты. Почему только они не в стандартной либе Compose? Загадка.
Хорошая статейка с примерами использования не самых частых, но весьма полезных функций для работы с коллекциями в Kotlin:
https://dev.to/kotlin/advanced-kotlin-collection-functionality-5e90
Статейка к видео о Multiple Back Stacks в Navigation Component:
https://medium.com/androiddevelopers/navigation-multiple-back-stacks-6c67ba41952f
Статья по Kotlin Symbol Processing:
https://proandroiddev.com/ksp-fact-or-kapt-7c7e9218c575
KSP быстрее Kapt и якобы со временем (когда многие популярные либы перейдут на него) поможет ускорить сборку проекта. Плюс есть свой Kotlin API.
Статья больше для любознательных и тех, кто поддерживает собственные библиотеки для Android.
Видео про новые фичи в Kotlin Mobile Multiplatform:
https://www.youtube.com/watch?v=QJqLpTw3vwI
Что по коду/либам?
Интересная штука - https://github.com/organicmaps/organicmaps - форк приложения MAPS.ME для Android/iOS. Можно подглядеть как делать оффлайн карты на базе данных OpenStreetMap.
Либа-коллекция расширений для Android классов:
https://github.com/zsmb13/requireKTX
Можно что-то и себе затянуть интересное.
В качестве p.s.
По поводу Hilt. Столкнулся с такой вот ошибкой, которая все еще актуальна: https://github.com/google/dagger/issues/2337
А вот если обновить Gradle до classpath "com.android.tools.build:gradle:7.1.0-alpha02" - о чудо, работает даже на последней версии 2.37.
Гугл как всегда.
Как вы уже догадались, большинство тем в рассылках еще долго будут посвящены Compose, с которым и я сейчас активно балуюсь.
Что интересного из статей?
Аналогичный моему опыту пример переноса UI из xml на Compose:
https://proandroiddev.com/migration-to-compose-c6eb63f187f9
Самое пока странное - это передача ViewModel в параметры composable-функций, чтобы можно было корректно оперировать стейтом (состоянием UI-элементов). Как раз на этом этапе я немного застрял, потому что передавая вьюмодель в параметры мы теряем слабую связность между частями приложения и хочется попробовать разные варианты.
Пример, кстати, живой: с Hilt и намёком на архитектуру. Отдельно порадовало упоминание в статьей сторонней лимбы Гугла для Compose - Appcompanist в которой есть SwipteToRefresh, Pager и другие часто используемые компоненты. Почему только они не в стандартной либе Compose? Загадка.
Хорошая статейка с примерами использования не самых частых, но весьма полезных функций для работы с коллекциями в Kotlin:
https://dev.to/kotlin/advanced-kotlin-collection-functionality-5e90
Статейка к видео о Multiple Back Stacks в Navigation Component:
https://medium.com/androiddevelopers/navigation-multiple-back-stacks-6c67ba41952f
Статья по Kotlin Symbol Processing:
https://proandroiddev.com/ksp-fact-or-kapt-7c7e9218c575
KSP быстрее Kapt и якобы со временем (когда многие популярные либы перейдут на него) поможет ускорить сборку проекта. Плюс есть свой Kotlin API.
Статья больше для любознательных и тех, кто поддерживает собственные библиотеки для Android.
Видео про новые фичи в Kotlin Mobile Multiplatform:
https://www.youtube.com/watch?v=QJqLpTw3vwI
Что по коду/либам?
Интересная штука - https://github.com/organicmaps/organicmaps - форк приложения MAPS.ME для Android/iOS. Можно подглядеть как делать оффлайн карты на базе данных OpenStreetMap.
Либа-коллекция расширений для Android классов:
https://github.com/zsmb13/requireKTX
Можно что-то и себе затянуть интересное.
В качестве p.s.
По поводу Hilt. Столкнулся с такой вот ошибкой, которая все еще актуальна: https://github.com/google/dagger/issues/2337
А вот если обновить Gradle до classpath "com.android.tools.build:gradle:7.1.0-alpha02" - о чудо, работает даже на последней версии 2.37.
Гугл как всегда.
Medium
Migration to compose
Let’s creating an app using compose instead of xml
Если у вас бесконечно крашится приложение Google - вы не одиноки!
Нужно зайти в Приложения -> Google -> тапнуть на троеточие - > Удалить обновления.
Проверил на себе - работает! А то уже хотелось свой Pixel 3a в окно выкинуть.
И да, мы тут над нашим банком иногда угараем, что фичи никто не тестирует, а тут такой фейл от Google.
Вообщем, тестирование, тестирование и еще раз тестирование.
Детали: https://www.androidauthority.com/google-app-crashing-1237738/
Нужно зайти в Приложения -> Google -> тапнуть на троеточие - > Удалить обновления.
Проверил на себе - работает! А то уже хотелось свой Pixel 3a в окно выкинуть.
И да, мы тут над нашим банком иногда угараем, что фичи никто не тестирует, а тут такой фейл от Google.
Вообщем, тестирование, тестирование и еще раз тестирование.
Детали: https://www.androidauthority.com/google-app-crashing-1237738/
Android Authority
Is your Google app crashing? You're not alone. (Updated: Fix incoming)
Is the Google app crashing on your phone? You are not alone. An update to the app seems to be causing the problem. Here's how you can fix it.
Эксперименты с Compose. Часть 2.
Продолжаем разбираться с Compose.
Впечатления стали более приятными.
Интерфейс приложения стал более интересным на вид и заметил, что во время "верстки" стал чаще пользоваться Preview.
Добавил горизонтальный (LazyRow) и вертикальный список (LazyColumn).
Прелесть в том, что не нужны больше адаптеры, но интересно будет рассмотреть пример с обновлением данных (кейс в котором часто используется DiffUtils, чтобы не обновлять список целиком).
Небольшой пример добавления списка элементов:
Вообще, многие вещи в Compose настолько интуитивны, что описывать их особо нет смысла. Как-то все само собой получается, даже без документации, которая все еще скудновата. Но это больше актуально для каких-то базовых вещей.
В корневом элементе теперь SwipeToRefresh, который вынесен почему-то в отдельную библиотеку Accompanist и вам нужно дополнительно ее подключать.
С этим элементом есть проблема. Сам жест свайп ту рефреш сейчас работает только где пустое пространство, а не поверх всего макета как обычно и должно быть. Придется скорее всего немного подправить макет. Есть вариант использовать модификатор nestedscroll, но пока примеров с ним особо не нашлось. А если добавить в Column модификатор verticallscroll все крашится. Вообщем, пока все со свайп ту рефреш сыровато.
Кстати, в Appcompanist еще есть и Pager (аналог ViewPager), Insets, расширения работы с библиотеками загрузки изображений (Coil и Glide) и Permissions. Скорее всего список утилит в этой либе будет расширяться.
Разобрался с ConstraintLayout. Его гибкость по-прежнему с нами, но задавать все констрейнты в коде, конечно, не привычно:
Управляет зависимостями Hilt. С ним чуть пришлось повозиться (баг с Gradle из поста выше). В итоге обошлось без передачи экземпляра вьюмодели в функцию, хотя сами вьюмодели можно получать и в скоупе composable-функции.
А в следующий раз поговорим про states (состояния), анимации и сделаем наш экран еще живее.
Репозиторий: https://github.com/Djangist/ComposeArchSample
Продолжаем разбираться с Compose.
Впечатления стали более приятными.
Интерфейс приложения стал более интересным на вид и заметил, что во время "верстки" стал чаще пользоваться Preview.
Добавил горизонтальный (LazyRow) и вертикальный список (LazyColumn).
Прелесть в том, что не нужны больше адаптеры, но интересно будет рассмотреть пример с обновлением данных (кейс в котором часто используется DiffUtils, чтобы не обновлять список целиком).
Небольшой пример добавления списка элементов:
LazyRow {
items(hourItems) {
HoursItem(it)
Spacer(modifier = Modifier.size(8.dp))
}
}
items по сути заменяет нам цикл по элементам - можно для каждой модели быстро добавить макет элемента списка.Вообще, многие вещи в Compose настолько интуитивны, что описывать их особо нет смысла. Как-то все само собой получается, даже без документации, которая все еще скудновата. Но это больше актуально для каких-то базовых вещей.
В корневом элементе теперь SwipeToRefresh, который вынесен почему-то в отдельную библиотеку Accompanist и вам нужно дополнительно ее подключать.
С этим элементом есть проблема. Сам жест свайп ту рефреш сейчас работает только где пустое пространство, а не поверх всего макета как обычно и должно быть. Придется скорее всего немного подправить макет. Есть вариант использовать модификатор nestedscroll, но пока примеров с ним особо не нашлось. А если добавить в Column модификатор verticallscroll все крашится. Вообщем, пока все со свайп ту рефреш сыровато.
Кстати, в Appcompanist еще есть и Pager (аналог ViewPager), Insets, расширения работы с библиотеками загрузки изображений (Coil и Glide) и Permissions. Скорее всего список утилит в этой либе будет расширяться.
Разобрался с ConstraintLayout. Его гибкость по-прежнему с нами, но задавать все констрейнты в коде, конечно, не привычно:
ConstraintLayout(modifier = Modifier.fillMaxWidth()) {
val (column, image, text1, text2) = createRefs()
Column(horizontalAlignment = Alignment.Start,
modifier = Modifier.constrainAs(column) {
start.linkTo(parent.start)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
}) {
Text(text = weatherData.dayOfMonth)
Text(text = weatherData.dayOfWeek)
}
Сперва нужно создать ссылки через createRefs(), которые мы потом присвоим нужным элементам, чтобы связывать их друг с другом.Text(
text = weatherData.minTemp.toString(),
style = Typography.caption,
modifier = Modifier.constrainAs(text2) {
end.linkTo(parent.end)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
}
)
Добавил базовую архитектуру: репозитории кидают мокированные данные, интерактор ими "управляет" и вьюмодель вытаскивает их из интерактора и на эти данные подписываются composable-функции. Управляет зависимостями Hilt. С ним чуть пришлось повозиться (баг с Gradle из поста выше). В итоге обошлось без передачи экземпляра вьюмодели в функцию, хотя сами вьюмодели можно получать и в скоупе composable-функции.
А в следующий раз поговорим про states (состояния), анимации и сделаем наш экран еще живее.
Репозиторий: https://github.com/Djangist/ComposeArchSample
Пост 16. Передача данных между фрагментом и диалогом через Navigation Component.
Недавно вспомнил о вариантах передачи данных между фрагментами, например, когда вызываемый фрагмент / диалог (DialogFragment) возвращает данные. Немного взгрустнув от того, что у нас в текущем проекте все еще старая версия библиотеки фрагментов и мы не можем использовать новый способ, вспомнил про еще одно решение с Navigation Component. Этот кейс тоже не для нашего старого проекта, в котором пришлось использовать setTargetFragment, но возможно, кому-то будет полезен.
1. Подписываемся на получение данных из вызываемого диалога / фрагмента:
2. Когда придет время - вызываем наш диалог или фрагмент обычным способом:
На этом все. Не нужны ни onActivityResult, ни разные костыли.
А про другие варианты передачи данных между фрагментами поговорим в следующих постах.
Недавно вспомнил о вариантах передачи данных между фрагментами, например, когда вызываемый фрагмент / диалог (DialogFragment) возвращает данные. Немного взгрустнув от того, что у нас в текущем проекте все еще старая версия библиотеки фрагментов и мы не можем использовать новый способ, вспомнил про еще одно решение с Navigation Component. Этот кейс тоже не для нашего старого проекта, в котором пришлось использовать setTargetFragment, но возможно, кому-то будет полезен.
1. Подписываемся на получение данных из вызываемого диалога / фрагмента:
private fun observeLocationDialogResult() {
findNavController().currentBackStackEntry
?.savedStateHandle
?.getLiveData<SetupLocationDialogFragment.Button>(KEY_BUTTON)
?.observe(viewLifecycleOwner, {
when (it) {
SetupLocationDialogFragment.Button.AUTO -> {
requestPermissions()
}
...
}
})
}
Здесь мы обращаемся к новому модулю SavedStateHandle (его, кстати, можно использовать и во ViewModel) и подписываемся на лайвдату, которую он любезно нам предоставил. Когда из диалога прилетит нужное нам значение (в нашем случае, когда пользователь нажмет на кнопку) - мы обработаем его нужным образом.2. Когда придет время - вызываем наш диалог или фрагмент обычным способом:
findNavController().navigate(MainFragmentDirections.actionMainFragmentToSetupLocationDialogFragment())
3. Обрабатываем нажатие на кнопку диалога или какое-либо другое событие в самом диалоге:binding.useAutoLocation.setOnClickListener {
findNavController().previousBackStackEntry?.savedStateHandle?.set(
KEY_BUTTON,
Button.AUTO
)
dismiss()
}
Здесь мы устанавливаем значение в поле, которое будем ловить в пункте 1. На этом все. Не нужны ни onActivityResult, ни разные костыли.
А про другие варианты передачи данных между фрагментами поговорим в следующих постах.
Столкнулся в пятницу с любопытным багом.
Если у вас в проекте используется библиотека Gson, то баг может быть актуальным и для вас.
Немного предистории. У нас есть Jenkins, который собирает билды тестеровщикам.
И вот в чем прикол: на моей локальной dev-сборке нужный запрос прекрасно работал, а у тестера нет.
Начали разбираться.
Сборка в дженкинсе обфусцируется и это была единственная разница между билдами.
И вот тут самое интересное.
Body для POST-запроса выглядит так:
А вот так выглядит наш класс для этого же Body:
Вроде бы все логично. Аннотации @SerializedName нет, ибо поле класса совпадает с названием поля тела POST-запроса.
Но штука вот в чем.
Если аннотации нет, поле обфусцируется в произвольное имя и запрос становится не корректным.
Поэтому лучше всегда дописывать к полям аннотацию @SerializedName:
Такие дела.
Если у вас в проекте используется библиотека Gson, то баг может быть актуальным и для вас.
Немного предистории. У нас есть Jenkins, который собирает билды тестеровщикам.
И вот в чем прикол: на моей локальной dev-сборке нужный запрос прекрасно работал, а у тестера нет.
Начали разбираться.
Сборка в дженкинсе обфусцируется и это была единственная разница между билдами.
И вот тут самое интересное.
Body для POST-запроса выглядит так:
{ query: "address string" }
А вот так выглядит наш класс для этого же Body:
data class SuggestionBody(
val query: String
)
Вроде бы все логично. Аннотации @SerializedName нет, ибо поле класса совпадает с названием поля тела POST-запроса.
Но штука вот в чем.
Если аннотации нет, поле обфусцируется в произвольное имя и запрос становится не корректным.
Поэтому лучше всегда дописывать к полям аннотацию @SerializedName:
data class SuggestionBody(
@SerializedName("query")
val query: String
)
Такие дела.