Старый Мобильщик
74 subscribers
34 photos
1 video
1 file
118 links
Разработка мобильных приложений, дедлайны и все, что вы любите в IT.

Будни. Сниппеты. Заметки.

Когда-то были AsyncTasks ... Android 2.3.3 и ни одной вакансии в городе-миллионнике

Обсудить что-либо: @activitynotfound
Download Telegram
RedMadRobot решили проверить "хаотичное" обучение с нуля на примере Kotlin Coroutines:
https://www.youtube.com/watch?v=cHERit7LNGM

Пробежались по основам и немного рассказали про StateFlow и SharedFlow.
В целом полезное видео для тех, кто собирается переходить на корутины. Есть сравнение корутиновских флоу с Rx флоу. И особенно радует, что ребята рассказали на каком уровне использовать скоупы и диспатчеры в контексте современной архитектуры на базе ViewModel в качестве presentation-слоя.

Впрочем это можно почитать самостоятельно из главной статьи, которая постоянно упоминается в видео:
https://developer.android.com/kotlin/coroutines/coroutines-best-practices

Мне как человеку, которому довелось поработать в команде RedMadRobot всегда приятно смотреть за их развитием. И именно в RMR я начал использовать впервые Котлин, кажется в 2017ом! Так что ребята всегда следят за трендами и самое главное - применяют их на практике.

Ждем вторую часть!
Давайте проведем небольшой интерактивчик.
Сколько лет вы занимаетесь Android-разработкой?
Anonymous Poll
64%
Только начинаю / начинающий (менее года)
24%
1-3 года
6%
3-5 лет
6%
Более 5 лет
Пост 7. Неявное в Data-классах Kotlin

Data-классы - идеальный инструмент для создания моделей на Domain и Data-слоях (по сути любых моделей данных).
Если вы знакомы только с Java, то самая простая аналогия - это Java-класс с геттерами и сеттерами, для которого реализован стандартный паттерн hashCode() - equals() и toString(). Kotlin реализовывает это все за нас, что делает код невероятно компактным!

Но нужно помнить одну важную вещь.
Только поля, которые переданы в конструктор Data-класса будут добавлены в дефолтную реализацию паттерна hashCode() - equals().
То бишь если объявить класс так - все будет хорошо - все поля попадут в реализацию:

data class User(val name: String, val age: Int)


Но если тот же класс объявить так:

data class User(val name: String) {
var age: Int = 0
}

В реализацию hashCode() и equals() от Kotlin не попадет поле age. Об этом нужно помнить. Особенно во время ревью Pull Requests ваших коллег.

Еще у Data-классов есть классный метод copy(), который очень часто выручает. Делает по-прежнему работу с данными immutable, но позволяет менять какие-то данные при создании копии объекта:

recipeData = recipeData?.copy(sourceName = sourceName, sourceDisplayName = sourceName, sourceUrl = sourceUrl)

Говоря простым языком: если у вас есть объект, поля которого заполняются поэтапно (например, большая форма/экран, которую(ый) пользователь может заполнить частично) - copy() прекрасный вариант сделать это безопасно и элегантно.

Про Data-классы в sealed-классах поговорим отдельно.

Ссылка на оф доку: https://kotlinlang.org/docs/data-classes.html
Старый Мобильщик pinned «Давайте проведем небольшой интерактивчик.
Сколько лет вы занимаетесь Android-разработкой?
»
Что новенького в рассылках #1
Добрался до своих еженедельных рассылок и решил поревьювить - что вообще интересного есть.
Спойлер: не особо много.

Разве что...

Любопытная либа-надстройка над Retrofit от Slack:
https://github.com/slackhq/EitherNet
Позволит упростить процесс обработки ошибок от бекенда, используя sealed-классы Kotlin вместо обработки исключений. Особых проблем с обработкой исключений вроде не замечено, но с sealed-классами более красиво выходит.

и...

Любопытная статья на тему настройки CI поверх GithubActions:
https://proandroiddev.com/continuous-integration-delivery-for-android-with-github-actions-part-1-b232ed2b1740
Прогонет тесты и сбилдит apk-шку после пуша в репу. Может пригодится если ваша репа на Github.

Было еще пару примеров очередных архитектур, хайпов на тему Kotlin Multiplatform и примерчик игры "Змейка" на Compose for Desktop - вообщем, все как обычно.
Пост 8. Моки в Charles

Я в восторге от Charles Proxy!
Работая в такой, местами очень не гибкой сфере (мягко говоря), как банковская - постоянно сталкиваешься с какими-то трудностями при отладке. Всегда может внезапно отвалиться бекенд (довольно часто в момент вашего багфикса) и как раз в таких ситуациях моки приходят нам на помощь.

Настроить их очень просто.
1. Сохраните себе в текстовый файл ответ от сервера, который будете подставлять в Charles (лучше сделать несколько заготовок с разными данными)
2. Выбираете Tools - Rewrite. Откроется окно в котором нужно будет добавить новое правило и поставить галочку Enable Rewrite.
3. При создании правила нужно выбрать тип Body (мокаем ответ от сервера), ставим галочку Response и в поле Value подставляем сохраненный ответ от сервера.
4. В окне Location нужно добавить URL-адрес вашего бекенда и путь к вызову метода.
5. По сути все. Дважды нажимаем Ок, в эмуляторе делаем нужный нам запрос (в моем случае обновляем данные главного экрана) и мок будет подставляться.
Даже эмулятор перезапускать не нужно! Всего три минуты и можно тестировать и отлаживать.
Главное, не забудьте потом убрать галочку Enable Rewrite, чтобы снова вернуть реальный ответ от сервера.

Самое прекрасное в этом - вы не теряете время и можете быстро тестировать работу приложения, подменяя нужные данные. Наглядно всплывают все косяки.

Чуть подробнее можно почитать, например, здесь:
https://medium.com/@migueloruiz/charles-tips-and-tricks-mocking-dfddf0ffaadd
Пост 9. Полезные extensions (часть 2)

Сегодня у нас будет целая пачка мелких, но полезных на практике extensions. В основном по работе с разными ресурсами.

inline fun Fragment?.dimenPx(@DimenRes resId: Int): Int {
return this?.context?.resources?.getDimensionPixelSize(resId) ?: 0
}

inline fun Fragment?.dimen(@DimenRes resId: Int): Float {
return this?.context?.resources?.getDimension(resId) ?: 0f
}

inline fun View.dimen(@DimenRes resId: Int): Float = resources.getDimension(resId)

inline fun View.dimenPx(@DimenRes resId: Int): Int = resources.getDimensionPixelSize(resId)

inline fun View.color(@ColorRes resId: Int): Int = ContextCompat.getColor(context, resId)

inline fun View.drawable(@DrawableRes resId: Int): Drawable? = ContextCompat.getDrawable(context, resId)

inline fun Context.dimen(@DimenRes resId: Int): Float = resources.getDimension(resId)

inline fun Context.dimenPx(@DimenRes resId: Int): Int = resources.getDimensionPixelSize(resId)

inline fun Context.color(@ColorRes resId: Int): Int = ContextCompat.getColor(this, resId)

inline fun Context.drawable(@DrawableRes resId: Int?): Drawable? = resId?.let { ContextCompat.getDrawable(this, it) }

inline fun View.getString(@StringRes resId: Int): String = context.getString(resId)

inline fun Fragment.color(@ColorRes resId: Int): Int =
context?.let { ContextCompat.getColor(it, resId) } ?: Color.TRANSPARENT

inline fun Fragment.drawable(@DrawableRes resId: Int): Drawable? =
context?.let { ContextCompat.getDrawable(it, resId) }

inline fun Fragment.font(@FontRes resId: Int): Typeface? = context?.font(resId)

inline fun View.font(@FontRes resId: Int) = context.font(resId)

inline fun Context.font(@FontRes resId: Int): Typeface? = ResourcesCompat.getFont(this, resId)
inline fun Context.fontSpan(@FontRes resId: Int): TypefaceSpanCompat? = font(resId)?.getSpan()

inline val Context?.screenWidth: Int; get() = this?.resources?.displayMetrics?.widthPixels ?: 0
inline val Context?.screenHeight: Int; get() = this?.resources?.displayMetrics?.heightPixels ?: 0

Обратите внимание на аннотации. Лучше всегда помечать нужные типы ресурсов подобными аннотациями, чтобы и среда и различные линтеры (вроде Lint, KtLint) различали в каком случае у вас именно int, а в каком случае Int ссылается на ресурс цвета. Подобные утилиты помогут пройтись по коду и составить список потенциальных проблем, а среда их подсветит в виде Warnings при сборке проекта.

А аннотации Nullable / NotNull пригодятся еще и в смешанном проекте (в котором и Kotlin и Java).

Почитать подробнее про аннотации можно здесь:
https://developer.android.com/studio/write/annotations
Пост 10. О полезных иногда EventBus-ах

Очередной пост вышел довольно длинным, поэтому оформил его через Телеграф.
Если такой формат зайдет - будем использовать чаще.

Думаю многие уже знают или со временем столкнуться с шинами или EventBus-ами. Сейчас, по большому счету - это антипаттерн.

Если совсем упрощать, то суть вот в чем: кто-то кидает события, а кто-то на них подписывается.

https://telegra.ph/Post-10-O-poleznyh-inogda-EventBus-ah-04-16-2
У RedMadRobot вышла вторая часть хаотичного изучения Coroutines.
На этот раз ребята разобрали:
- Как же создавать корутины в Data и Domain слоях
- coroutineScope и supervisorScope
- Job
- Deferred и async/await
- Использование внешнего скоупа
- Как создать внешний скоуп
- Избегай GlobalScope
- CoroutineScope
- CoroutineContext
- CoroutineScope vs CoroutineContext
- Делай свои корутины cancellable
- Помни про исключения

https://youtu.be/6Apj_v9ZkBs
Что новенького в рассылках #2

Вышел Kotlin 1.5.0-RC.
Из интересного:
* доводят до ума (стабилизируют) беззнаковые целые типы UInt, ULong, UByte, UShort, которые появились в Kotlin 1.3 Beta.
* Extensions для пакета java.nio.file.Path в kotlin.io.path
* Накидали разных полезных функций для Char: Char.isDigit(), Char.isLetter(), Char.isLowerCase(), Char.isUpperCase(), Char.isTitleCase()
* Для более удобной работы с остатками и делением добавили функции floorDiv() и mod()
* Методы коллекций firstNotNullOf() и firstNotNullOfOrNull(), которые можно теперь юзать вместо двух методов mapNotNull() и first()

Познавательная статья на тему компиляции в Art
https://proandroiddev.com/android-runtime-how-dalvik-and-art-work-6e57cf1c50e5
И в ней же любопытная ссылка на доку: https://source.android.com/devices/tech/dalvik/configure#how_art_works которая даже ценнее исходной статьи.
Будете готовиться к собеседованиям - самое-то! Иногда спрашивают как это все работает и в каких версиях Android были серьезные изменения. Сложно сказать зачем это спрашивать в условной Яндекс.Еде, но думаю отдельно поговорим об этом.

Очередной пример использования Compose: https://github.com/GuilhE/KitchenTimer в рамках Гуглового #AndroidDevChallenge
Выглядит это все удручающе, если честно (скрин к посту). Привет, макароны. Хороший пример как не надо писать код. Хотя для участия в event-е от Гугла наверное норм. Если не забуду - набросаем более вменяемый примерчик с архитектурой. Уже давно хочется самому поковырять Compose!

Mark Allison снова пишет про DataStore: https://blog.stylingandroid.com/datastore-1-0-0-alpha08/
Вот весь цикл статей: https://blog.stylingandroid.com/category/jetpack/datastore/
DataStore - хранилище для сохранения пар ключ-значение или типизированных объектов на базе протокола ProtoBuf. DataStore использует Coroutines и Flow, но привязки есть и для RxJava. Если юзаете SharedPreferences - можно потихоньку в отдельной ветке мигрировать на DataStore. Правда все еще alpha08, а Гугловые альфы могут идти годами.

Если такой формат вам полезен - пишите в комментах, что узнали или хотите поизучать!
Пост 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
Примеры_задач_для_подготовки_к_алгоритмам.pdf
33.5 KB
Кстати, по поводу собеседований.
Если вдруг кто-то захочет пообщаться с Яндекс вот вам примеры алгоритмических задачек, которые любезно предоставляют их 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 более наглядны. Посмотрите скриншот - по названиям юскейсов уже можно примерно понять, что происходит в проекте. С интеракторами дело чуть сложнее.
На самом деле, сложно быть объективным - обычно я всегда использую интеракторы и лишь не так давно в своем проекте начал рефакторить на юскейсы, чтобы просто посмотреть разницу. Если давать осмысленные имена для юскейсов, то получается вцелом наглядно.
Пост 12. UseCases vs Interactors (часть 2)

Пара примеров:
// базовый интерфейс юскейса
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

А что обычно используете вы?
Пишите в комменты свой опыт работы с юскейсами или интеракторами. И что на ваш взгляд лучше, удобнее и разные нюансы из опыта.
Что новенького в рассылках #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
Приятные новости подъехали!
В этом году (18-20 мая) Google IO пройдет онлайн и бесплатно для всех.
Ссылочка со счетчиком: https://events.google.com/io/?lng=en
Добавляйте к себе в календарь, чтобы не забыть.
Пост 13. Вытаскиваем аргументы фрагмента в одну строчку с помощью delegated properties

Магия Котлина помогает писать более лаконичный код, который можно легко повторно использовать в новых проектах. Например, можно воспользоваться 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
Находясь в отпуске хочется оглянуться назад и порассуждать. Благо под рукой есть интересное чтиво этому способствующее.

Сейчас мы ведём два проекта, при чем оба приложения банковские и наглядны два разных подхода.

В первом вообще нет процессов и все свои боли пытаются решить в виде звонков в Дискорд (я честно говоря до сих пор не понимаю зачем это приложение использовать в разработке, ну да ладно). Звонки оказывается пишутся, но разработчики не получают доступа к записям. Задачи пишутся в гитлабе, в виде строчки с заголовком. Никакого описания нет. 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.