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

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

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

Обсудить что-либо: @activitynotfound
Download Telegram
Тут сравнили скорость компиляции Android-проектов на:
1. 2021 14" MacBook Pro — M1 Pro (10 core)— 32gb RAM
2. Desktop (Pop_OS!) — 4.2ghz AMD 2950x (16 core) — 64gb RAM
3. 2019 16" MacBook Pro — 2.4ghz Intel i9 (8 core)–32gb RAM

И M1 Pro показал себя очень и очень неплохо, проиграв ДЕСКТОПУ лишь на Clean Build!
Впечатляет!
Поигрался немного с созданием, запуском проектов на Flutter и KMM.

1. Для обеих платформ предлагается плагин под Android Studio. И нужно сказать, под Flutter он пока выглядит интереснее. Например, есть возможность открыть каждый специфичный модуль либо в Xcode, либо в отдельном экземпляре Android Studio (AS). А вот у плагина для KMM такого нет.
2. Flutter-проект запустился на обеих платформах, и даже в Chrome, что несомненно круто! (но стоит сказать, что когда AS не видит симулятор iOS, приходится запускать проект из Xcode, благо это делается быстро через контекстное меню в AS).
3. KMM-проект так и не запустился на iOS. И, к слову, почему-то при настройке проекта в мастере, не выбирается нужный тул из доки (Xcode Build phases). Вообщем, тут нужно явно посидеть и понастраивать.
4. Flutter-овский Hot Reload - это не что! Как будто вы кодите веб-приложение. Что-то поменял в коде (например, текст заголовка) и тут же изменения видны в эмуляторе/симуляторе, без всяких Clean-Build и повторных запусков. Кайф!
5. Помните open source проект на KMM - https://github.com/joreilly/FantasyPremierLeague ? Думаю, попробую импортнуть в AS и запустить. Вдруг запустится нормально на iOS. Но… проект будто не узнает эту структуру и считает его обычным проектом под Android. И на Android-эмуляторе запустился (правда крашнулся после нажатия на Поиск, но не суть). А вот то, что его можно запустить под iOS, видимо, знает только автор этого проекта. AS не видит даже подобной настройки. Возможно, он был создан по другой структуре и новый плагин для KMM ее не понимает. Вообщем, тоже не вышло - нужны пляски с бубном и здесь.
6. Но дока, кстати, для обеих платформ (по крайней мере, чтобы быстро стартануть) более-менее. Под KMM чуть хуже, но есть отдельные гайды по запуску проекта под iOS и там, так что попробуем победить эту проблему.

Вообщем, Flutter, пока выглядит интереснее. Но немного отталкивает сам язык Dart - какие никакие отличия в нем между Java / Kotlin есть, хотя с виду он не очень сложный.
У KMM общие части можно писать на Kotlin, что для нас плюс, а какие-то платформенно-специфичные уже открывать в Xcode и кодить на Swift.
Нельзя жить с разбитыми окнами!

Во время чтения книги "Программист-прагматик" запомнился довольно интересный принцип, который действительно встречается в долгоиграющих проектах.

Далее, интересные фрагменты из книги.

Имеется немало факторов, способствующих деградации программного обеспечения, и самый важный из них, по-видимому, имеет отношение к психологии или культуре в работе над проектом. Какими бы совершенными ни были составленные планы или участники проекта, он все равно может быть подвергнут деградации и разрушению в течении всего срока своего действия. Тем не менее существуют и такие проекты, которые успешно противостоят естественной тенденции к беспорядку и ухитряются завершиться вполне удачно, несмотря на огромные трудности и постоянные задержки и помехи.

В чем же тогда отличия? В старых кварталах городов одни здания красивы и чисты, тогда как другие выглядят как трухлявые развалины. Исследователи в сфере преступности и упадка городов открыли замечательный пусковой механизм, очень быстро превращающий чистое, нетронутое, нежилое здание в разрушенную и заброшенную трущобу. Это разбитое окно.

Оказывается, что если не отремонтировать хотя бы одно разбитое окно в течении какого-нибудь значительного периода времени, у жильцов возникнет ощущение заброшенности здания, а следовательно, отсутствия заботы городских властей о его состоянии. И, как следствие, разбивается еще одно окно, а люди начинают захламлять здание всяким мусором. В течении относительно короткого периода времени здание разрушается настолько, что у его владельца не возникает никакого желания отремонтировать его, и тогда ощущение заброшенности становится вполне реальным.

Как показывают исследования, безнадежность оказывается заразной.

Совет: "Нельзя жить с разбитыми окнами"
По существу, означает, что нельзя мириться с неудачными проектными решениями или плохо написанными фрагментами кода, оставляя их неисправленными. Исправляйте их, как только обнаружите. Если же времени на исправление недостаточно, закомментируйте неисправный код, выведите сообщение "Не реализовано" или же подставьте вместо него фиктивные данные подобно тому, как временно заколачивают разбитые окна. Словом, предпримите какое-то действие, чтобы предотвратить дальнейший ущерб и тем самым показать, что владеете ситуацией.

Еще один совет из этой же серии: прежде всего не навредить!
Присоединившись к проекту, где код безупречен, вам следует действовать крайне осторожно, чтобы не навредить.

А вы встречали подобное в своей практике?
👍1
Telegram теперь встраивает собственные рекламные посты в каналах. Они все отмечены знаком «Спонсировано». Поэтому, если увидите такой пост (мало ли!) — знайте, влиять на спонсорский контент от Telegram авторы каналов пока никак не могут, а, следовательно, не могут отвечать за достоверность информации и ее качество.
Будьте аккуратны!
Старый Мобильщик pinned «Telegram теперь встраивает собственные рекламные посты в каналах. Они все отмечены знаком «Спонсировано». Поэтому, если увидите такой пост (мало ли!) — знайте, влиять на спонсорский контент от Telegram авторы каналов пока никак не могут, а, следовательно,…»
Небольшой пример Bottom Sheet на Compose

В сети можно встретить, например, такой вариант реализации Bottom Sheets:
https://proandroiddev.com/how-to-master-swipeable-and-nestedscroll-modifiers-in-compose-bb0635d6a760
Но, по-моему, он полезен скорее, как пример работы с разными модификаторами, а также показывает, что можно довольно просто создавать свои компоненты.
А вот для Bottom Sheets есть готовое решение, основанное на BottomSheetScaffold:

@ExperimentalMaterialApi
@Composable
fun BottomSheet(bottomSheetScaffoldState: BottomSheetScaffoldState) {
BottomSheetScaffold(
scaffoldState = bottomSheetScaffoldState,
sheetContent = {
LazyColumn {
items(6) {
Text("$it")
}
}
},
sheetPeekHeight = 0.dp,
sheetBackgroundColor = Color.Gray
){}
}

И да, список внутри Bottom Sheet прекрасно работает. Хотя тут он довольно простой.

Теперь нам нужна кнопка, которая будет менять состояние нашего Bottom Sheet (параметр bottomSheetScaffoldState) и в зависимости от него он и будет показываться / скрываться.

val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(
bottomSheetState = BottomSheetState(BottomSheetValue.Collapsed)
)
...
Button(onClick = {
coroutineScope.launch {
if (bottomSheetScaffoldState.bottomSheetState.isCollapsed) {
bottomSheetScaffoldState.bottomSheetState.expand()
} else {
bottomSheetScaffoldState.bottomSheetState.collapse()
}
}
}) {
Text(text = "Show/Hide BottomSheet")
}

Все довольно просто.

Но самое интересное, что для Bottom Sheet можно даже указать Top Bar, Floating Action Button и кучу других вещей (например, цвет контента, цвет шита и т.д.). Все потому что его реализовали как обычный Scaffold, который также имеет заготовку так называемых слотов: https://developer.android.com/jetpack/compose/layouts/basics#slot-based-layouts.

Почему они так сделали с Bottom Sheet не очень понятно, но возможность такая есть.
Тут оказывается в августе вышло потенциально интересное чтиво от команды Android:
https://chethaase.medium.com/androids-765c803d5ff6

Chet Haase в своей книге, которую он писал 4 года, расскажет почему Android стал таким успешным, как это было, как шла разработка и о команде, которая нам подарила сие чудо.

Кстати, хороший вариант на подарок.
Правда на русском пока не встречал или таки уже есть?
Полезный пост про Inline Classes в Kotlin.

Inline (value) classes появились еще в Kotlin 1.3 и наконец-то вышли в релизной версии вместе с Kotlin 1.5.0. В некоторых моментах напоминают структуры (struct) в других языках программирования.
Ждали эту фичу или не особо?
Судя по отзывам коллег, https://proxyman.io - отличная альтернатива Charles Proxy.
По крайней мере, настроить моки получается проще и удобнее.
Надо бы попробовать.

А вы что используете для моков API?
Накидывайте альтернативы Charles Proxy, если юзаете что-то подобное.
Некоторые мысли после нескольких часов создания и запуска проекта на KMM.

Спросите: почему так много часов?
На самом деле, хотелось, чтобы проект билдился и запускался и на iOS прямиком из Android Studio. C запуском на Android как раз проблем не было.
По итогу:
1. Текущая версия KMM-плагина 0.3 работает с Kotlin 1.6 и Gradle plugin 7.0.0+. Но штука вот в чем. Если у вас не последние версии Kotlin, Java SE ниже 11-версии и студия не самая свежая (вероятно дело больше в версии Gradle Plugin), то создавая KMM-проект в AS он вылетет с ошибкой и не до конца создаст структуру проекта. И потом плагин просто disabled-тся. Обновили окружение и потом вручную включили плагин снова.
2. Для успешного запуска проекта на iOS-симуляторе нужен Xcode 12.5+ на текущей версии плагина KMM. Вероятно и версия 12.0+ сгодится, но не проверял. Но боль лично для меня в том, что Xcode 12+ доступен только на BigSur, которую я пока не ставил на рабочий iMac. Благо обновил Mac Pro и удалось немного поэкспериментировать.
3. Кстати, про Xcode. Вероятно, его можно попробовать заменить на AppCode или какими-то плагинами для Swift под AS, чтобы можно было кодить в одной среде и на Kotlin и на Swift, но скорее всего, когда проект дойдет до полноценной разработки, Xcode понадобится.
4. Дока по KMM на kotlinlang.org оставляет желать лучшего. Странное структурирование с бесконечными перекрестными ссылками, быстро устаревает, но что-то интересное почерпнуть можно и нужно. Плюс ее маловато. Хотелось бы больше инфы и кейсов.
5. В AS (есть отдельный конфиг для запуска iOS app) даже можно настроить, например, на каком симуляторе запускать проект, что однозначно радует.
6. Самое забавное - это запуск проекта на iOS. Как же забавно видеть старт iOS-симулятора прямиком из AS. И оно работает! После некоторых телодвижений :)
7. В целом, прикольные ощущения. Есть общий модуль shared с общей кодовой базой (обычно бизнес-логика), есть конкретные реализации UI на Android/iOS, которые как раз используют эту самую бизнес-логику. А если вспомнить, что Kotlin можно попробовать развернуть и на Web (Kotlin JS), то эту логику в теории можно будет использовать и там. Круто? Очень! Но без нюансов, конечно, не обойдется.

Про структуру проекта поговорим подробнее чуть позже.

Итак, рабочая конфигурация для плагина KMM 0.3:
- Java SE 11 (можно попробовать и OpenJDK)
- AS Arctic Fox 2020.3.1 Patch 3
- Xcode 12.5.1
- Kotlin 1.6.0
- Gradle Plugin 7.0.3
И сразу небольшой анонс на ближайшие (и не очень) планы.

KMM понравился, поэтому будем пошагово ковырять. Есть ощущение, что за этим будущее, но поглядим.
Создал репу: https://github.com/Djangist/ToDoApp в которой будем делать кроссплатформенный Todo App на KMM. Уже можно стянуть пустую болванку, настроить окружение, попробовать собрать и запустить.

Что хотелось бы сделать в этом аппе?
1. Общую бизнес-логику заметок для обеих платформ.
2. Общий Rest API c репозиториями, интеракторами и вот этим всем. Например, прикрутить что-то вроде: https://developers.google.com/tasks/reference/rest для синхронизации задач из Google-акка. Но надо подумать.
3. Compose / SwiftUI, а может к этому времени сделают поддержку Compose и на iOS? Есть уже Compose Multiplatform.
4. MVI-архитектура для presеntation-слоя.
5. Базовый функционал: CRUD-интерфейс, sharing заметок, оффлайн-режим.
6. Кроссплатформенный DI? Уфф...
7. Предлагайте свои идеи!

Flutter тоже будем ковырять. Он не менее интересен, чем KMM. По крайней мере, для ниши «создать быстрый кроссплатформенный прототип» под все платформы - самое то!

Как раз, есть мысль переписать свой старенький апп, но пока еще не решил на какую платформу.

Будем дальше разбираться с Compose и MVI.
Доделаю собственный велосипед для MVI. Пока там есть проблемы, но не хватает времени.

И само собой, будем и дальше вникать в тонкости самого Android, Kotlin, и немножко изучать Swift / iOS.
Старый Мобильщик pinned «И сразу небольшой анонс на ближайшие (и не очень) планы. KMM понравился, поэтому будем пошагово ковырять. Есть ощущение, что за этим будущее, но поглядим. Создал репу: https://github.com/Djangist/ToDoApp в которой будем делать кроссплатформенный Todo App…»
Принцип проектирования по контракту

Бертран Мейер разработал принцип проектирования по контракту для языка Eiffel. Это простая, но эффективная методика, направленная на документирование (и согласование) прав и обязанностей программных модулей, чтобы обеспечить правильность программы. А что такое правильная программа? Это программа, которая выполняет только то, что от нее требуется, - ни больше и ни меньше. Документирование и верификация такого требования и составляет саму суть проектирования по контракту.

Каждая функция и метод в программной системе выполняет какое-то действие. Прежде чем начать это действие, функция может предполагать какое-то состояние окружающего мира, а по завершении она может сделать заявление о состоянии окружающего мира (перевод на русский, вероятно, не самый удачный, но суть отражает). Эти предположения и требования Мейер описывает следующим образом.
- Предусловия. Это требования подпрограммы, которые определяют, что должно быть истинным для ее вызова. Подпрограмма вообще не должна вызываться, если ее предусловия будут нарушены.
- Постусловия. Это состояние окружающего мира по завершении подпрограммы, т.е. То, что ею гарантируется. Наличие постусловия у подпрограммы подразумевает, что она непременно завершится, а следовательно, бесконечные циклы не допускаются.
- Инварианты класса. Класс гарантирует, что данное условие для вызывающего кода всегда истинно. В процессе внутренней обработки в подпрограмме инвариант может и не соблюдаться, но к моменту выхода из подпрограммы и передачи управления вызывающему коду инвариант должен быть непременно истинным.

Таким образом, контракт между подпрограммой и любым потенциально вызывающим кодом может быть составлен следующим образом.
- Если все предусловия подпрограммы удовлетворяются вызывающим кодом, то подпрограмма гарантирует истинность всех постусловий и инвариантов при своем завершении.

К чему это все?
Оказывается, в Kotlin 1.3 появился пакет kotlin.contracts и у нас есть возможность попробовать использовать этот принцип на практике, делая наши приложения более документированными и безопасными.
А вот примеры использования этого пакета: https://blog.kotlin-academy.com/understanding-kotlin-contracts-f255ded41ef2

Кстати, а вы слышали про подобный принцип проектирования ранее?
Небольшой дайджест изменений во Fragment API 1.4.0 (релиз от 17 Ноября)

- Появился метод FragmentContainerView.getFragment()
Раньше, чтобы получить фрагмент нужно было дергать метод:

val fragment = fragmentManager.findFragmentById()

и приводить к нужному типу, а теперь же фрагмент можно получить через метод контейнера:

val fragment = binding.container.getFragment<NavHostFragment>()


- Новый менеджер состояний (до 1.4.0 был экспериментальным)

- Поддержка множественных back stacks (также доступно в Navigation Component)
Подробнее здесь: https://medium.com/androiddevelopers/multiple-back-stacks-b714d974f134

- FragmentStrictMode - разные проверки потенциальных проблем при работе с фрагментами и возможность на них реагировать.
К примеру, можно отправить какие-то кастомные исключения/события в Firebase Crashlytics.
Задать настройки можно через метод setStrictModePolicy()
Подробнее:https://developer.android.com/guide/fragments/debugging#strictmode

- Новые проверки для Android Lint

- FragmentScenario теперь реализует Closeable и их можно использовать в Kotlin-овском use и try-with-resources.
Подробнее про FragmentScenario и тестирование фрагментов: https://developer.android.com/guide/fragments/test
Друзья, а вы пользуетесь итераторами?
Просто пост-напоминание, что иногда и они могут быть полезны.

Простая задачка: поменять значение одного из элементов списка, индекс которого мы не знаем.

Можно воспользоваться listIterator() таким вот образом:

val someList = mutableListOf(1, 20, 10, 55, 30, 22, 11, 0, 99) // или arrayListOf()
val iterate = someList.listIterator()
while (iterate.hasNext()) {
val oldValue = iterate.next()
if (oldValue == 20) {
iterate.set(oldValue + 20)
}
}

Но изменять значения итератор само собой позволит только у MutableList. У обычного List метода set нет, что логично.
Давайте поиграем в игру "Собеседование".
Представьте, что собеседуете человека.
На скрине код кандидата.

1. Что в нем не так? Сколько проблем сходу видите?
2. На какой уровень должен претендовать кандидат с таким кодом? (jun / middle / senior / lead )
3. Вопросы по каким темам вы бы у него спросили после ознакомления с таким кодом?
И так, продолжим с ответов на вопросы выше про код кандидата. Придется отвечать мне, раз уж вы такие молчаливые 🙂

Кандидат хотел устроиться к нам в банк, в довольно сложный стрим. Особо про его резюме не будем. Парень окончил курсы на Udemy, поработав годик-около двух.
Хотелось бы послушать про его опыт, а то там уже и джунов полидить успел, при том что в коде, который он прислал нам, активно участвовал еще один разраб. Однако, кандидат принял оффер другой кампании и к нам на собес не пришел.

Но давайте все таки про код.
1. Напрямую в репозиторий передается экземпляр Application. Во-первых, это мягко говоря не очень из-за возможных проблем с утечками, а во-вторых в репозитории не нужно передевать никакие зависимости от Android.
2. Так называемый mapper (CoinMapper) не передается в качестве зависимости в конструкторе и сам маппер написано неправильно.
3. И вообще, зависимость подставляется вручную, не через DI-контейнер. Для примера не прям уж критично, если человек может рассказать почему так и как работает DI, но даже использовав условный Hilt можно добавить себе "дополнительных очков" в коде.
4. Экземпляр БД тоже лучше передавать в зависимость в конструкторе.
5. Репозиторий не должен возвращать LiveData (опять же, пункт про Application). Репозиторий должен возвращать список моделей, а дальше уже на уровне Presentation решается как с этим быть дальше.
6. Небрежное форматирование. Писали быстро. Не критично, но такая небрежность настораживает.
7. Работу с WorkManager можно смело вынести в отдельную зависимость.

Кандидата стоит смело погонять по Clean Architecture, MVVM, MVP, leak memory, многомодульности (у нас очень много модулей), DI и само собой по основам Android.

Исходя из проблем выше, кандидата можно оценить как Джуниор-разработчика, может быть Джуниор+/начинающий Миддл если бы мы послушали про его опыт. Здесь важно сопоставить архитектуру и проблемы вашего проекта и опыт кандидата. Но судя по резюме, человек себя позиционирует выше Джуна, что немножко настораживает после увиденного кода.

Таких кандидатов бояться не стоит, но на сложный стрим если и брать, то в пару с ментором, который будет его вести, что в реальной жизни крайне редко встречается.
👍1