Когда проект растет есть два пути (два стула?) по которому обычно идут команды.
1. Сделать рефакторинг пакетов и вынести функционал по фичам, чтобы проще было искать и поддерживать нужный код. Разбиение по Clean Architecture тогда делается обычно внутри каждого пакета с фичей.
2. Вынести фичи в отдельные модули.
По первому варианту все понятно. Основная проблема - это сложность поиска нужного кода и более медленная сборка проекта.
А вот у второго варианта много и преимуществ и недостатков.
Хорошая статейка, которая освещает эти плюсы и минусы вот: https://proandroiddev.com/the-abc-of-modularization-for-android-in-2021-e7b3fbe29fca
Но на практике все не так радужно и лучше выбрать один из подходов до того как проект станет очень большим.
В нашем текущем проекте тоже используется многомодульность, но штука в том, что огромный кусок пакетов с фичами не вынесен в модули и валяется в так называемом базовом модуле. Как итог: теряется главное преимущество - скорость сборки проекта. Да и командная работа тоже не выигрывает от этой ситуации, ведь сразу много разрабов может что-то править в этом базовом модуле.
Спросите почему до сих пор не вынесли?
Не знаю, но справедливости ради кусок там очень большой и сейчас проект с нуля собирается 6-7 минут на iMac с 64 ГБ ОЗУ.
Если закладываться на многомодульность сразу, то такой ситуации, понятное дело, не возникнет, но на старте придется потратить времени побольше.
1. Сделать рефакторинг пакетов и вынести функционал по фичам, чтобы проще было искать и поддерживать нужный код. Разбиение по Clean Architecture тогда делается обычно внутри каждого пакета с фичей.
2. Вынести фичи в отдельные модули.
По первому варианту все понятно. Основная проблема - это сложность поиска нужного кода и более медленная сборка проекта.
А вот у второго варианта много и преимуществ и недостатков.
Хорошая статейка, которая освещает эти плюсы и минусы вот: https://proandroiddev.com/the-abc-of-modularization-for-android-in-2021-e7b3fbe29fca
Но на практике все не так радужно и лучше выбрать один из подходов до того как проект станет очень большим.
В нашем текущем проекте тоже используется многомодульность, но штука в том, что огромный кусок пакетов с фичами не вынесен в модули и валяется в так называемом базовом модуле. Как итог: теряется главное преимущество - скорость сборки проекта. Да и командная работа тоже не выигрывает от этой ситуации, ведь сразу много разрабов может что-то править в этом базовом модуле.
Спросите почему до сих пор не вынесли?
Не знаю, но справедливости ради кусок там очень большой и сейчас проект с нуля собирается 6-7 минут на iMac с 64 ГБ ОЗУ.
Если закладываться на многомодульность сразу, то такой ситуации, понятное дело, не возникнет, но на старте придется потратить времени побольше.
Medium
The ABC of Modularization for Android in 2021
Modularization is not a recent topic at all. This concept have been around us for years! but probably there are some folks around there…
Пост 19. Fragment Result API.
Продолжаем собирать разные способы передачи данных между фрагментами.
Теперь рассмотрим самый новый, судя по доке - рекомендуемый способ, с помощью Fragment Result API.
Fragment Result API появился с версии Fragment 1.3.0-alpha04.
Все предельно просто.
1. Во фрагменте, где нужно получить данные ставим обработчик:
KEY - ключ по которому будем ловить результат. Он должен совпадать с тем, что вернет диалог.
2. В диалоге по нажатию на кнопку возвращаем нужный результат:
На этом все.
Запушил изменения, если кто-то захочет посмотреть на живом примере: https://github.com/Djangist/RoundedDialogDemo
Продолжаем собирать разные способы передачи данных между фрагментами.
Теперь рассмотрим самый новый, судя по доке - рекомендуемый способ, с помощью Fragment Result API.
Fragment Result API появился с версии Fragment 1.3.0-alpha04.
Все предельно просто.
1. Во фрагменте, где нужно получить данные ставим обработчик:
setFragmentResultListener(KEY) { key, bundle ->
if (bundle.containsKey(key)) {
Toast.makeText(requireContext(), bundle[key].toString(), Toast.LENGTH_LONG).show()
}
}
setFragmentResultListener - это extension для parentFragmentManager.setFragmentResultListener(). Напомню, что extensions для фрагмента доступны в зависимосте: androidx.fragment:fragment-ktx.KEY - ключ по которому будем ловить результат. Он должен совпадать с тем, что вернет диалог.
2. В диалоге по нажатию на кнопку возвращаем нужный результат:
setFragmentResult(KEY, bundleOf(KEY to "Pff"))
Если в bundleOf передать другое значение, отличное от KEY, Toast не отобразится. На этом все.
Запушил изменения, если кто-то захочет посмотреть на живом примере: https://github.com/Djangist/RoundedDialogDemo
GitHub
GitHub - Djangist/RoundedDialogDemo: Custom rounded dialog with insets
Custom rounded dialog with insets. Contribute to Djangist/RoundedDialogDemo development by creating an account on GitHub.
Пятница.
Давайте устроим новую рубрику #вопросыответы дабы немного оживить наш канал.
Пишите вопросы под этим постом - потрындим о всяком разном айтишном, андроидном и разработческом.
Давайте устроим новую рубрику #вопросыответы дабы немного оживить наш канал.
Пишите вопросы под этим постом - потрындим о всяком разном айтишном, андроидном и разработческом.
Что новенького в рассылках #5
Ура, уже вышла RC2 для Compose, а значит релиз уже совсем скоро.
Уже начали что-то пробовать и изучать?
В связи с этим многие начали смотреть в сторону MVI-архитектуры presеentation-слоя, ибо Compose должен прекрасно на нее "ложиться".
Признаюсь, мне это особенно интересно. Хочется выработать новую архитектуру на ближайшие годы, дабы все новые приложения разрабатывать на ней.
В рассылках как раз множество статей на эту тему.
Например, вот:
https://medium.com/google-developer-experts/jetpack-compose-missing-piece-to-the-mvi-puzzle-44c0e60b571
Или вот сравнение LiveData vs SharedFlow в MVVM и MVI:
https://proandroiddev.com/livedata-vs-sharedflow-and-stateflow-in-mvvm-and-mvi-architecture-57aad108816d
Советы как улучшить свою продуктивность в Android Studio и немного продуктивность самой Studio:
https://proandroiddev.com/android-studio-tips-for-faster-development-cb9a17c123f3
Наличие качественных скриншотов у приложения в Google Play довольно важная для скачивания и просмотров вещь. Автор рассказывает какие средства и сервисы в этом могут помочь. Особенно актуально если вы все делаете сами и не хочется долго искать дизайнера:
https://proandroiddev.com/how-i-made-beautiful-screenshots-for-google-play-developer-experience-61ce108fa6b4
На неделе прошла небольшая онлайн-конфа Google на тему Gamedev на которой анонсировали Android Game Development Kit:
https://android-developers.googleblog.com/2021/07/introducing-android-game-development-kit.html
На десерт.
Pacman на Compose: https://github.com/danielmbutler/Pacman_Compose
Если пропустил что-то интересное - пишите в комментарии. Обсудим.
Ура, уже вышла RC2 для Compose, а значит релиз уже совсем скоро.
Уже начали что-то пробовать и изучать?
В связи с этим многие начали смотреть в сторону MVI-архитектуры presеentation-слоя, ибо Compose должен прекрасно на нее "ложиться".
Признаюсь, мне это особенно интересно. Хочется выработать новую архитектуру на ближайшие годы, дабы все новые приложения разрабатывать на ней.
В рассылках как раз множество статей на эту тему.
Например, вот:
https://medium.com/google-developer-experts/jetpack-compose-missing-piece-to-the-mvi-puzzle-44c0e60b571
Или вот сравнение LiveData vs SharedFlow в MVVM и MVI:
https://proandroiddev.com/livedata-vs-sharedflow-and-stateflow-in-mvvm-and-mvi-architecture-57aad108816d
Советы как улучшить свою продуктивность в Android Studio и немного продуктивность самой Studio:
https://proandroiddev.com/android-studio-tips-for-faster-development-cb9a17c123f3
Наличие качественных скриншотов у приложения в Google Play довольно важная для скачивания и просмотров вещь. Автор рассказывает какие средства и сервисы в этом могут помочь. Особенно актуально если вы все делаете сами и не хочется долго искать дизайнера:
https://proandroiddev.com/how-i-made-beautiful-screenshots-for-google-play-developer-experience-61ce108fa6b4
На неделе прошла небольшая онлайн-конфа Google на тему Gamedev на которой анонсировали Android Game Development Kit:
https://android-developers.googleblog.com/2021/07/introducing-android-game-development-kit.html
На десерт.
Pacman на Compose: https://github.com/danielmbutler/Pacman_Compose
Если пропустил что-то интересное - пишите в комментарии. Обсудим.
Medium
Jetpack Compose: Missing piece to the MVI puzzle?
When I first started exploring Jetpack compose, I saw many examples of the screen state modeled as a combination of mutable properties :
Эксперименты с Compose. Часть 3.
Продолжаем вникать в тонкости Compose.
Темы становятся сложнее и местами ощущаешь, что все непривычно после классического подхода.
Решил добавить в наш примерчик сплеш скрин с переходом на главный экран.
И знаете, что интересно?
После этого подумал: а зачем нам теперь нужны фрагменты?
Но давайте поясню на примере.
Теперь в активность мы добавляем NavHost, который будет выступать роутером для наших двух экранов. Да, да, у Compose есть привязка к Navigation Component.
Все относительно просто. Ставим сплеш скрин как стартовый экран (startDestination). А далее уже в зависимости от обычного вызова navController.navigate(«route») произойдет переход на нужную Composable функцию, которая является для макета стартовой.
А вот так выглядит макет нашего сплеш-скрина:
Здесь основная магия происходит в переменной state. В качестве начального состояния мы ставим UIState.Showing (будет чуть нагляднее в коде вьюмодели) и после того как наш стейт (состояние) меняется на NavigateTo мы вызываем navigateTo("main") для перехода на главный экран.
Чтобы понять лучше как это все работает, стоит вникнуть в так называемую концепцию recomposition, которая является основной для Compose. Суть в том, что определенные composable-функции будут перерисовываться в зависимости от изменения состояния. Чуть подробнее мы поговорим об этом отдельно (нужно еще самому вникнуть как следует).
И последний пазл нашего примера - код SplashViewModel:
Ждем три секунды и меняем стейт на NavigateTo, что позволит нашей composable-функции отрисоваться заново, с новым состоянием, которое мы ловим и переходим на главный экран.
И самое интересное - у нас нет никаких фрагментов.
Как вам? Звучит все это сложно?
Местами, пожалуй, да.
Вникнуть будет проще тем, кто уже работал с разными либами на подобном реактивном подходе (redux, Flutter и прочие) с MVI в основе.
Код примера здесь: https://github.com/Djangist/ComposeArchSample
Далее, мы отдельно поговорим о рекомпозиции, remember и mutableStateOf.
Продолжаем вникать в тонкости Compose.
Темы становятся сложнее и местами ощущаешь, что все непривычно после классического подхода.
Решил добавить в наш примерчик сплеш скрин с переходом на главный экран.
И знаете, что интересно?
После этого подумал: а зачем нам теперь нужны фрагменты?
Но давайте поясню на примере.
Теперь в активность мы добавляем NavHost, который будет выступать роутером для наших двух экранов. Да, да, у Compose есть привязка к Navigation Component.
setContent {
ComposeAppArchitectureTheme {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "splash") {
composable("splash") {
SplashScreen(splashViewModel, navController)
}
composable("main") {
MainScreen(viewModel)
}
}
}
}
Все относительно просто. Ставим сплеш скрин как стартовый экран (startDestination). А далее уже в зависимости от обычного вызова navController.navigate(«route») произойдет переход на нужную Composable функцию, которая является для макета стартовой.
А вот так выглядит макет нашего сплеш-скрина:
@Composable
fun SplashScreen(viewModel: SplashViewModel, navController: NavController) {
val state = remember { viewModel.state }
if( state.value is UIState.NavigateTo ){
Log.d(TAG,"navigate to main")
navController.popBackStack()
navController.navigate("main")
}
Log.d(TAG,"splash recompose ${state.value}")
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.background(SplashColor),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Image(
painter = painterResource(id = R.drawable.splash),
contentDescription = null,
alignment = Alignment.Center
)
}
}
Здесь основная магия происходит в переменной state. В качестве начального состояния мы ставим UIState.Showing (будет чуть нагляднее в коде вьюмодели) и после того как наш стейт (состояние) меняется на NavigateTo мы вызываем navigateTo("main") для перехода на главный экран.
Чтобы понять лучше как это все работает, стоит вникнуть в так называемую концепцию recomposition, которая является основной для Compose. Суть в том, что определенные composable-функции будут перерисовываться в зависимости от изменения состояния. Чуть подробнее мы поговорим об этом отдельно (нужно еще самому вникнуть как следует).
И последний пазл нашего примера - код SplashViewModel:
@HiltViewModel
class SplashViewModel @Inject constructor() : ViewModel() {
private val _state = mutableStateOf<UIState>(UIState.Showing)
val state
get() = _state
init {
viewModelScope.launch {
delay(3000)
_state.value = UIState.NavigateTo("main")
}
}
}
Ждем три секунды и меняем стейт на NavigateTo, что позволит нашей composable-функции отрисоваться заново, с новым состоянием, которое мы ловим и переходим на главный экран.
И самое интересное - у нас нет никаких фрагментов.
Как вам? Звучит все это сложно?
Местами, пожалуй, да.
Вникнуть будет проще тем, кто уже работал с разными либами на подобном реактивном подходе (redux, Flutter и прочие) с MVI в основе.
Код примера здесь: https://github.com/Djangist/ComposeArchSample
Далее, мы отдельно поговорим о рекомпозиции, remember и mutableStateOf.
Скоро мы все будем не нужны...
Отчасти шутка, конечно, но насколько долго она будет именно шуткой - вопрос интересный.
Уже видели?
https://copilot.github.com
Отчасти шутка, конечно, но насколько долго она будет именно шуткой - вопрос интересный.
Уже видели?
https://copilot.github.com
GitHub
GitHub Copilot
AI that builds with you
Немного изменений в демке. Времени пока не так много на нее, увы.
На полноценную часть про Compose не тянет, но пару интересных изменений о которых хочется рассказать все же есть.
1. Звучит как шутка, но наконец-то победил SwipeRefresh и сам свайп теперь работает из любой части макета, без краша. Пришлось добавить во все нужные функции ниже корневой модификатор verticalScroll(rememberScrollState()). Если добавлять в корневой Column - краш!
2. Теперь экземпляр вьюмодели получаем прямиком из Composable-функции через hiltViewModel()
Эта функция доступна в отдельной либе androidx.navigation:navigation-compose:2.4.0-alpha04
Кстати, на более свежую версию пока обновляться не советую. Вся навигация ломается на хрен! Начинается бесконечный recompose сплеш-скрина.
3. Убрал не нужные элементы, вроде вложенных Column. Стало немного чище.
Из интересного еще вот что.
hiltViewModel() стоит использовать если у вас есть навигация через NavComponent и инжекты через Hilt. В MainScreen эта функция как раз и выручает, а если будем использовать обычный метод viewModel() - угадайте что? Правильно! Краш.
Хотя в SplashScreen прекрасно работает и viewModel(), но на всякий случай решил использовать и там hiltViewModel(), для консистентности.
Код, как обычно, здесь: https://github.com/Djangist/ComposeArchSample
На полноценную часть про Compose не тянет, но пару интересных изменений о которых хочется рассказать все же есть.
1. Звучит как шутка, но наконец-то победил SwipeRefresh и сам свайп теперь работает из любой части макета, без краша. Пришлось добавить во все нужные функции ниже корневой модификатор verticalScroll(rememberScrollState()). Если добавлять в корневой Column - краш!
2. Теперь экземпляр вьюмодели получаем прямиком из Composable-функции через hiltViewModel()
Эта функция доступна в отдельной либе androidx.navigation:navigation-compose:2.4.0-alpha04
Кстати, на более свежую версию пока обновляться не советую. Вся навигация ломается на хрен! Начинается бесконечный recompose сплеш-скрина.
3. Убрал не нужные элементы, вроде вложенных Column. Стало немного чище.
Из интересного еще вот что.
hiltViewModel() стоит использовать если у вас есть навигация через NavComponent и инжекты через Hilt. В MainScreen эта функция как раз и выручает, а если будем использовать обычный метод viewModel() - угадайте что? Правильно! Краш.
Хотя в SplashScreen прекрасно работает и viewModel(), но на всякий случай решил использовать и там hiltViewModel(), для консистентности.
Код, как обычно, здесь: https://github.com/Djangist/ComposeArchSample
GitHub
GitHub - Djangist/ComposeArchSample
Contribute to Djangist/ComposeArchSample development by creating an account on GitHub.
Проблема с минификацией в com.android.tools.build:gradle:4.2.2
Не знаете как провести рабочий день? Скучно пилить новые бизнес-фичи?
Можно, например, попытаться понять в чем заключается проблема
java.lang.IllegalArgumentException: Method return type must not include a type variable or wildcard
с API-методом в Retrofit, который возвращает обычный Single<ResponseDto>.
Оказывается, в версии 4.2.2 плагина com.android.tools.build:gradle намудрили что-то с минификацией и он выкашивает dto-объекты ответа, которые не используются.
Суть проблемы: https://github.com/square/retrofit/issues/3588
Распространите коллегам, чтобы были в курсе, а то минус день-другой обеспечены.
Не знаете как провести рабочий день? Скучно пилить новые бизнес-фичи?
Можно, например, попытаться понять в чем заключается проблема
java.lang.IllegalArgumentException: Method return type must not include a type variable or wildcard
с API-методом в Retrofit, который возвращает обычный Single<ResponseDto>.
Оказывается, в версии 4.2.2 плагина com.android.tools.build:gradle намудрили что-то с минификацией и он выкашивает dto-объекты ответа, которые не используются.
Суть проблемы: https://github.com/square/retrofit/issues/3588
Распространите коллегам, чтобы были в курсе, а то минус день-другой обеспечены.
GitHub
Method return type must not include a type variable or wildcard · Issue #3588 · square/retrofit
Using com.squareup.retrofit2:retrofit:2.9.0 After upgrading gradle plugin from com.android.tools.build:gradle:4.1.3 to com.android.tools.build:gradle:4.2.2 I'm getting following error when ...
Вышел Compose 1.0!
C одной стороны прекрасная новость и можно активнее учить основы, пробовать внедрять какие-то кусочки UI (без фанатизма), а с другой привязки разных библиотек к Compose все еще в альфа, бета-версиях, что не очень.
Кстати, обновил демо проект на версию 1.0.
Немного радости от Compose Team:
https://www.youtube.com/watch?v=kLA1QwDjioc
C одной стороны прекрасная новость и можно активнее учить основы, пробовать внедрять какие-то кусочки UI (без фанатизма), а с другой привязки разных библиотек к Compose все еще в альфа, бета-версиях, что не очень.
Кстати, обновил демо проект на версию 1.0.
Немного радости от Compose Team:
https://www.youtube.com/watch?v=kLA1QwDjioc
YouTube
Announcing Jetpack Compose 1.0
Today, we're launching version 1.0 of Jetpack Compose, Android's modern, native UI toolkit to help you build better apps faster. It's stable, and ready for you to adopt in production.
Our team has been developing Compose in the open with feedback and participation…
Our team has been developing Compose in the open with feedback and participation…
Любопытная библиотечка для дебаггинга на самом девайсе.
Показывает логи HTTP-запросов, собирает краши приложения, а еще можно смотреть shared preferences и подправлять на нужное значение для отладки.
Вообщем, может пригодится в повседневной работе.
Ссылка на либу: https://github.com/mocklets/pluto
Показывает логи HTTP-запросов, собирает краши приложения, а еще можно смотреть shared preferences и подправлять на нужное значение для отладки.
Вообщем, может пригодится в повседневной работе.
Ссылка на либу: https://github.com/mocklets/pluto
GitHub
GitHub - androidPluto/pluto: Android Pluto is a on-device debugging framework for Android applications, which helps intercept Network…
Android Pluto is a on-device debugging framework for Android applications, which helps intercept Network calls, capture Crashes & ANRs, manipulate application data on-the-go, and much more....
Кстати, а вы обновили студию до Arctic Fox?
Она уже где-то неделю в stable. Я обновился и работаю в Arctic Fox и над старым (основным рабочим) проектом и над новыми обучающими с Compose на борту.
Полет пока нормальный, но есть нюансы:
1. Аккуратнее с R8 в релизных билдах. Теперь он более рьяно выпиливает неиспользуемые классы. Недавний пример есть на канале.
2. Выпилена инструкция compile и ее производные. Теперь только implementation.
3. Теперь AGP 7.0 (Android Gradle Plugin) требует Gradle 7.0 и Java 11.
4. Новый Layout Inspector теперь позволяет строить дерево composable-функций, смотреть как они отрисованы, просматривать параметры и т.д. - довольно удобно!
5. Preview для Compose. Его можно настроить довольно гибко, добавляя по нескольку превьюшек для одной функции, включать системный интерфейс, фоны и прочее. Здесь пока есть проблемы с отрисовкой (превью иногда не работает), но думаю допилят в ближайших обновлениях.
6. Теперь можно вытащить sqlite базу к себе на диск и выбрать при экспорте нужный формат. Не то чтобы вау-новость, но теперь можно быстрее вытаскивать БД и смотреть проблемы с таблицами, хотя и раньше можно было.
7. И самое любопытное для меня. В AGP 7.0 убрали build cache, при этом заявляют, что скорость сборки не просядет. Убрали задачу cleanBuildCache, свойства android.enableBuildCache, android.buildCacheDir.
Видосик: https://www.youtube.com/watch?v=-8tSZr7iMcw
Она уже где-то неделю в stable. Я обновился и работаю в Arctic Fox и над старым (основным рабочим) проектом и над новыми обучающими с Compose на борту.
Полет пока нормальный, но есть нюансы:
1. Аккуратнее с R8 в релизных билдах. Теперь он более рьяно выпиливает неиспользуемые классы. Недавний пример есть на канале.
2. Выпилена инструкция compile и ее производные. Теперь только implementation.
3. Теперь AGP 7.0 (Android Gradle Plugin) требует Gradle 7.0 и Java 11.
4. Новый Layout Inspector теперь позволяет строить дерево composable-функций, смотреть как они отрисованы, просматривать параметры и т.д. - довольно удобно!
5. Preview для Compose. Его можно настроить довольно гибко, добавляя по нескольку превьюшек для одной функции, включать системный интерфейс, фоны и прочее. Здесь пока есть проблемы с отрисовкой (превью иногда не работает), но думаю допилят в ближайших обновлениях.
6. Теперь можно вытащить sqlite базу к себе на диск и выбрать при экспорте нужный формат. Не то чтобы вау-новость, но теперь можно быстрее вытаскивать БД и смотреть проблемы с таблицами, хотя и раньше можно было.
7. И самое любопытное для меня. В AGP 7.0 убрали build cache, при этом заявляют, что скорость сборки не просядет. Убрали задачу cleanBuildCache, свойства android.enableBuildCache, android.buildCacheDir.
Видосик: https://www.youtube.com/watch?v=-8tSZr7iMcw
YouTube
What's new in Android Studio Arctic Fox
Get an overview of what tools and features are available in Android Studio Arctic Fox and the new updates and requirements to Android Gradle Plugin 7.0.
New features:
•Compose support (preview, interactive preview, layout inspector)
•Accessibility Scanner…
New features:
•Compose support (preview, interactive preview, layout inspector)
•Accessibility Scanner…
Небольшая quiz-задачка по Compose, которая прекрасно раскрывает суть recomposing (рекомпозиции) - одной из основных концепций нового декларативного UI.
Грубо говоря, суть ее в том, что элемент UI будет обновлен (отрисован повторно), если изменится его состояние, а если часть UI остается в прежнем состояние, то Compose просто возьмет уже закешированное (ранее уже отрисованное состояние). Таким образом меняется лишь часть UI, а не все дерево целиком, как обычно происходит в xml.
У нас есть такой код:
Грубо говоря, суть ее в том, что элемент UI будет обновлен (отрисован повторно), если изменится его состояние, а если часть UI остается в прежнем состояние, то Compose просто возьмет уже закешированное (ранее уже отрисованное состояние). Таким образом меняется лишь часть UI, а не все дерево целиком, как обычно происходит в xml.
У нас есть такой код:
@Composable
fun Foo() {
Log.d(TAG,"recompose Foo")
var text by remember { mutableStateOf("") }
Log.d(TAG,"recompose Foo text")
Button(onClick = { text = "$text\n$text" }) {
Log.d(TAG,"recompose Button's lambda")
Text(text)
}
}Какой текст выведется в лог если несколько раз нажать на кнопку?
Final Results
33%
recompose Foo, recompose Foo, recompose Foo
22%
recompose Foo text, recompose Foo text, recompose Foo text
44%
recompose Button's lambda, recompose Button's lambda, recompose Button's lambda
0%
Ни один из перечисленных - напишу свой в комментарий
И так, друзья.
Правильный ответ на наш квиз - вариант №3: recompose Button's lambda, recompose Button's lambda, recompose Button's lambda
Некоторые из вас ответили именно так!
Поздравляю!
Вся штука в переменной:
Это утверждение можно легко проверить, изменив наш код, и при клике подставив в text статичное значение:
Правильный ответ на наш квиз - вариант №3: recompose Button's lambda, recompose Button's lambda, recompose Button's lambda
Некоторые из вас ответили именно так!
Поздравляю!
Вся штука в переменной:
var text by remember { mutableStateOf("") }
При клике она меняется и таким образом меняется состояние элемента Text и скоуп кнопки, что заставляет Compose отрисовать ее с новым состоянием и кнопка становится все больше и больше.Это утверждение можно легко проверить, изменив наш код, и при клике подставив в text статичное значение:
Button(onClick = { text = "123" }) {
Log.d(TAG,"recompose Button's lambda $text")
Text(text)
}
Тогда при первом клике кнопка отобразит один раз наш текст 123 и далее стейт (состояние) при каждом клике уже меняться не будет.Еще немного про состояние (states) в Compose, но в контексте нашего демо-приложения:
https://github.com/Djangist/ComposeArchSample
Кто внимательно следит за постами, возможно помнит, что недавно наконец-то заработал SwipeRefresh, но если сделать свайп до конца - данные не обновляются.
Хотя в репозитории уже есть случайное генерирование температуры для моделек.
Чего же нам не хватает?
1. У нас есть вьюмодель, с данными, которые хранятся в StateFlow.
2. Мы подписались на эти данные и в случае их изменения, обновим UI
Вот наша главная composable-функция:
А не хватает нам всего лишь одного метода: collectAsState(),который уже добавлен как расширение для StateFlow.
Пара изменений в коде и данные по свайпу стали обновляться:
В описании к методу все наглядно (см скрин).
Каждое новое значение в StateFlow теперь будет приводить к recomposition, а это то, что нам и нужно!
Выглядит пока что это все неплохо. Поток данных можно довольно просто контролировать, обновляя лишь нужные кусочки UI.
Осталось добавить какой-то механизм, который будет управлять разными состояниями (например, Reducer в качестве части MVI-архитектуры/UI-паттерна) для полной красоты и усложнить наш пример, дабы посмотреть какие-то сложные кейсы и сложные состояния.
https://github.com/Djangist/ComposeArchSample
Кто внимательно следит за постами, возможно помнит, что недавно наконец-то заработал SwipeRefresh, но если сделать свайп до конца - данные не обновляются.
Хотя в репозитории уже есть случайное генерирование температуры для моделек.
Чего же нам не хватает?
1. У нас есть вьюмодель, с данными, которые хранятся в StateFlow.
2. Мы подписались на эти данные и в случае их изменения, обновим UI
Вот наша главная composable-функция:
@Composable
fun MainScreen() {
val viewModel: MainViewModel = hiltViewModel()
val mainWeather = viewModel.mainWeatherData.value
val daysWeather = viewModel.daysWeatherData.value
val hoursWeather = viewModel.hoursWeatherData.value
ShowWeather(
viewModel,
mainWeather,
daysWeather,
hoursWeather
)
}
А не хватает нам всего лишь одного метода: collectAsState(),который уже добавлен как расширение для StateFlow.
Пара изменений в коде и данные по свайпу стали обновляться:
val viewModel: MainViewModel = hiltViewModel()
val mainWeather = viewModel.mainWeatherData.collectAsState()
val daysWeather = viewModel.daysWeatherData.collectAsState()
val hoursWeather = viewModel.hoursWeatherData.collectAsState()
ShowWeather(
viewModel,
mainWeather.value,
daysWeather.value,
hoursWeather.value
)
В описании к методу все наглядно (см скрин).
Каждое новое значение в StateFlow теперь будет приводить к recomposition, а это то, что нам и нужно!
Выглядит пока что это все неплохо. Поток данных можно довольно просто контролировать, обновляя лишь нужные кусочки UI.
Осталось добавить какой-то механизм, который будет управлять разными состояниями (например, Reducer в качестве части MVI-архитектуры/UI-паттерна) для полной красоты и усложнить наш пример, дабы посмотреть какие-то сложные кейсы и сложные состояния.
Еще никогда не было так просто настроить CI/CD для Android-приложения.
После появления Github Actions (у Gitlab есть аналогичная штука) это можно сделать за три минуты!
Кайф!
Пост: https://proandroiddev.com/continuous-integration-delivery-for-android-with-github-actions-part-1-b232ed2b1740
После появления Github Actions (у Gitlab есть аналогичная штука) это можно сделать за три минуты!
Кайф!
Пост: https://proandroiddev.com/continuous-integration-delivery-for-android-with-github-actions-part-1-b232ed2b1740
Medium
“Continuous Integration/Delivery” for Android with GitHub Actions — Part 1
Dear developers, In this article I will show you how to automate the test execution and build of your Android app…
Забавный баг в Android Studio Arctic Fox (хотя, возможно, был и раньше).
Если остановить выполнение задачи (хорошо воспроизводится на задаче по сборке проекта), нажав на крестик (скрин 1), после остановки задачи, зеленая кнопка для запуска сборки проекта повторно не появляется (скрин 2) 🙂
Нужно запустить, например, Clean, чтобы кнопка появилась повторно.
Если остановить выполнение задачи (хорошо воспроизводится на задаче по сборке проекта), нажав на крестик (скрин 1), после остановки задачи, зеленая кнопка для запуска сборки проекта повторно не появляется (скрин 2) 🙂
Нужно запустить, например, Clean, чтобы кнопка появилась повторно.
Прекрасный доклад по модуляризации на посмотреть вечером, который покрывает и многие темы разработки ПО в более широком смысле!
https://www.youtube.com/watch?v=oAQlKiF91Ks
Кстати, на канале конференции еще множество интересных видосов.
https://www.youtube.com/watch?v=oAQlKiF91Ks
Кстати, на канале конференции еще множество интересных видосов.
YouTube
Степан Гончаров — Абсолютная модуляризация
Подробнее о конференции Mobius: https://jrg.su/ojGU3B
— —
. . .
Что, если вам больше не нужно быть Gradle экспертом, чтобы проектировать, поддерживать и эффективно масштабировать современные многомодульные Android-приложения, но при этом сразу же получить…
— —
. . .
Что, если вам больше не нужно быть Gradle экспертом, чтобы проектировать, поддерживать и эффективно масштабировать современные многомодульные Android-приложения, но при этом сразу же получить…
А вот и часть 3 хаотичного изучения Coroutines от RMR.
В новом выпуске ребята разобрали:
- Для чего был нужен SingleLiveEvent
- Как его приготовить без LiveData
- Channel
- О трате ресурсов в бекграунде
- buffer, conflate, flowOn, shareIn
- WhileSubscribed
- и многое другое.
Получился очень полезный выпуск.
В новом выпуске ребята разобрали:
- Для чего был нужен SingleLiveEvent
- Как его приготовить без LiveData
- Channel
- О трате ресурсов в бекграунде
- buffer, conflate, flowOn, shareIn
- WhileSubscribed
- и многое другое.
Получился очень полезный выпуск.
YouTube
Coroutines. Хаотичное изучение. Часть 3
Третья часть "хаотичного изучения" Kotlin Coroutines о том как безопасно слушать из UI. 🧔🏻
Зашли издалека:
- Для чего был нужен SingleLiveEvent 📟
- Как его приготовить без LiveData 🔫
- Channel (кажется это спойлер 😆)
- О трате ресурсов в бекграунде 🔦
- buffer…
Зашли издалека:
- Для чего был нужен SingleLiveEvent 📟
- Как его приготовить без LiveData 🔫
- Channel (кажется это спойлер 😆)
- О трате ресурсов в бекграунде 🔦
- buffer…
Пост 20. "Паттерн" репозиторий.
Многие уже знают о таком понятии как репозиторий. Однако, я все равно часто наблюдаю в коде проектов некое непонимание как правильно его "готовить".
Давайте разбираться.
В доках Гугла часто можно встретить примеры в которых во ViewModel напрямую ижектят репозитории, что мягко говоря, не очень правильно.
На самом деле, ViewModel или Presenter (смотря что используете) не должен ничего знать о репозиториях. Обычно с репой работает UseCase или Interactor. И именно он инжектится во ViewModel.
Но давайте о главном.
Что же такое репозиторий (Repository)?
Репозиторий можно рассматривать как коллекцию, которая работает с какой-либо моделью данных.
И только с одной моделью.
Это важно.
Если хочется работать с несколькими моделями, то самое правильное - это создать на каждую модель свой отдельный репозиторий.
Тут не стоит лениться - в дальнейшем будет меньше проблем.
Обычно репозиторий возвращает вам список моделей или конкретную модель, кеширует какие-либо локальные данные и возвращает эти данные из кеша и вот это вот все. Но в нем нет и не должно быть НИКАКОЙ другой логики (особенно бизнес-логики). Чем он тупее - тем лучше. Положили модель куда-то, отдали модель.
Теперь немного про слои.
Самые первые реализации Clean Arctitecture под Android делили слои репозиториев так:
1. Интерфейс репозитория мы помещаем на уровне domain.
2. Реализацию (класс, наследующий интерфейс репозитория) на уровне data.
3. UseCases / Interactors инжектит в конструктор именно интерфейс репозитория, а не реализацию! Это тоже очень важно. Реализацию подставит DI-фреймворк.
Сейчас же, когда проекты стали делить на features (фичи), а еще большие проекты на features в отдельных модулях, принцип разделения на слои остался прежним (в каждом модуле фичи своя структура domain - data - presentation).
Еще стоит отдельно упомянуть, что репозитории могут реализовывать разные DataSource (обычно инжектится в конструктор), например:
Для чего это нужно?
Ну например, один заинжекченный репозиторий работает с локальным кешем данных, а другой всегда получает данные из сети. Интерфейс репозитория один и тот же, но за счет подстановки нужной реализации DataSource в конструктор поведение меняется.
При этом сохраняется исходный интерфейс, что всегда удобно во время тестирования или рефакторинга.
И такое на практике встречается частенько.
По большому счет это все основы, которые стоит четко для себя понимать при работе с репозиторием.
Главное следуем картинке ниже, чтобы Dependency Rule не нарушался и будет вам счастье.
Кстати, стоит ли отдельно рассказать про этот самый Dependency Rule?
Пишите в комменты.
Многие уже знают о таком понятии как репозиторий. Однако, я все равно часто наблюдаю в коде проектов некое непонимание как правильно его "готовить".
Давайте разбираться.
В доках Гугла часто можно встретить примеры в которых во ViewModel напрямую ижектят репозитории, что мягко говоря, не очень правильно.
На самом деле, ViewModel или Presenter (смотря что используете) не должен ничего знать о репозиториях. Обычно с репой работает UseCase или Interactor. И именно он инжектится во ViewModel.
Но давайте о главном.
Что же такое репозиторий (Repository)?
Репозиторий можно рассматривать как коллекцию, которая работает с какой-либо моделью данных.
И только с одной моделью.
Это важно.
Если хочется работать с несколькими моделями, то самое правильное - это создать на каждую модель свой отдельный репозиторий.
Тут не стоит лениться - в дальнейшем будет меньше проблем.
Обычно репозиторий возвращает вам список моделей или конкретную модель, кеширует какие-либо локальные данные и возвращает эти данные из кеша и вот это вот все. Но в нем нет и не должно быть НИКАКОЙ другой логики (особенно бизнес-логики). Чем он тупее - тем лучше. Положили модель куда-то, отдали модель.
Теперь немного про слои.
Самые первые реализации Clean Arctitecture под Android делили слои репозиториев так:
1. Интерфейс репозитория мы помещаем на уровне domain.
2. Реализацию (класс, наследующий интерфейс репозитория) на уровне data.
3. UseCases / Interactors инжектит в конструктор именно интерфейс репозитория, а не реализацию! Это тоже очень важно. Реализацию подставит DI-фреймворк.
Сейчас же, когда проекты стали делить на features (фичи), а еще большие проекты на features в отдельных модулях, принцип разделения на слои остался прежним (в каждом модуле фичи своя структура domain - data - presentation).
Еще стоит отдельно упомянуть, что репозитории могут реализовывать разные DataSource (обычно инжектится в конструктор), например:
class UserRepositoryImpl @Inject constructor(private val source: DataSource): ...
Для чего это нужно?
Ну например, один заинжекченный репозиторий работает с локальным кешем данных, а другой всегда получает данные из сети. Интерфейс репозитория один и тот же, но за счет подстановки нужной реализации DataSource в конструктор поведение меняется.
При этом сохраняется исходный интерфейс, что всегда удобно во время тестирования или рефакторинга.
И такое на практике встречается частенько.
По большому счет это все основы, которые стоит четко для себя понимать при работе с репозиторием.
Главное следуем картинке ниже, чтобы Dependency Rule не нарушался и будет вам счастье.
Кстати, стоит ли отдельно рассказать про этот самый Dependency Rule?
Пишите в комменты.