Search
Write a publication
Pull to refresh
32
0
Глеб Гутник @glider_skobb

Мобильный разработчик в Wildberries

Send message

Не хочу сильно душнить, но использование composed модификатора больше не рекомендуется использовать - в пользу Composable фабрик.

В целом термины reference equality (сравнение по ссылке), instance equality (сравнение по экземпляру/инстансу) и "сравнение по адресу в памяти" используются в статье синонимично, потому что reference equality это по определению проверка, что две ссылки в стеке указывают на один и тот же адрес в куче (формулировка грубая, но вроде не грешащая против основ JMM).

И да, вы совершенно правы, это дефолтная реализация equals.

По поводу делегата - точнее будет сказать, что для переменных с делегатом ключом в remember добавляется делегат, а не значение. В байткоде это тоже видно

Третья строка снизу: композер проверяет изменение делегата. В этом случае пересоздавать лямбду не имеет смысла, потому что делегат при обращении всегда предоставит актуальное значение.
Аналогично, кстати, работает derivedStateOf, добавлять ключ в remember не нужно:

var imagesCount by remember { mutableStateOf(10) }
val isEmpty by remember { derivedStateOf { imagesCount == 0 } }

По поводу адреса в памяти - выразился так для простоты, в соответствии с гайдом гугла по strong skipping: "Unstable parameters are compared using instance equality (===)" (https://developer.android.com/develop/ui/compose/performance/stability/strongskipping). За что купил, за то и продаю)
Насчет equals действительно хорошее наблюдение, проверка по инстансу в памяти это дефолтная реализация equals, очевидно имеется в виду она. При этом если переопределить equals на вечный true, проверка по инстансу не сработает. Гугловая дока врет.

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

В абстрактных рассуждениях на эту тему очень мало смысла, все зависит от конкретики.

Каждый 22-23-летний senior специалист (включая меня), читая ваш комментарий, нервно сглотнул)
Интересно, может ли причинно-следственная связь работать в обратную сторону: не "молодых сеньоров мало, так как они неопытны", а "молодых сеньоров мало, так как их опыт никто не воспринимает всерьез лишь потому, что они молоды". Иначе говоря, можно ли "загасить" амбиции молодого и рвущегося в бой разраба подходом "всякий сверчок знай свой шесток"?
Возможно, меня закидают тухлыми помидорами, лишь хотел поделиться мыслями вслух

Интересный код однако)

CoroutineScope(Dispatchers.IO).launch(Dispatchers.IO)

Билдер корутин launch берет из скоупа, от которого вы запускаетесь, контекст, включая пуллы потоков, джобы, хендлеры ошибок и все прочее. Зачем тут дублирование?

Кроме того, флоу в объекте Downloader мутабельны и публичны, что дает возможность эмитить в них из любого места во всей кодовой базе.

val sizeFlow = MutableSharedFlow<Progress>()

Хорошая практика - оставлять мутабельную версию флоу приватной, а наружу выставлять только иммутабельный флоу:

private val _sizeFlow = MutableSharedFlow<Progress>()
val sizeFlow = _sizeFlow.asSharedFlow()

Забавно, вы как будто вообще не поняли, о чем статья) Речь идёт о загрузке данных с сервера, загрузке именно по сети. Вы будете использовать для этого parametersOf? Хотел бы на это посмотреть)

Далее, если вы используете KMP, лучше не выставлять наружу suspend методы вьюмодели, иначе в Свифте придется оборачивать в try await или работать с коллбэками. Синхронный метод для бо́льшего удобства.

Далее, если вы используете загрузку данных с сервера, можно, конечно, их закешировать в локальную БД, но тогда придется решать вопрос, когда инвалидировать этот кэш, как синхронизировать его с удаленным источником данных и так далее. Кроме того, если вы используете оптимистические апдейты, а сервер за вами "не поспевает" или запрос упал с ошибкой - опять же, возникнет рассинхрон с сервером.

Поэтому на практике часто "списочный" экран напрямую обновляется свежими данными с серва.

Изначально Котлин так и задумывался, но сейчас это уже давно не так. Во-первых, существует Kotlin Native. Это технология, которая позволяет кастомным компилятором собирать исходники на Kotlin в нативные бинарники. Благодаря ей можно писать приложения для iOS, например. Можно конечно писать и для винды или линукса, как на C, но это не очень популярный способ)

Есть ещё Kotlin JS, транспилятор, который преобразует код на котлине в код на JS, и Kotlin WASM, который может исполнять код на котлине в браузере при помощи WebAssembly.

К сожалению, тут хочется поправить скорее автора статьи

И от авторов Свифта тогда тоже. Из официальной документации Swift:

Swift uses variables to store and refer to values by an identifying name. Swift also makes extensive use of variables whose values can't be changed. - "Свифт использует переменные, чтобы сохранять и ссылаться на значения по имени. Свифт также активно использует переменные, значения которых не могут быть изменены."

Может быть, если весь мир не прав, а вы правы, стоит задуматься?

Печально каждый раз видеть, как люди путают Kotlin Multiplatform, который уже давно Stable, и Compose Multiplatform, который Stable для Android и Desktop, Beta для iOS и Alpha для Web. В этой статье вообще про Compose Multiplatform пара предложений (видимо, автор оригинального материала не вполне в курсе последних апдейтов и релизов), а между тем фреймворк уже сейчас вполне может соперничать с Flutter. По пути своего развития он будет собирать все те же проблемы, что и Flutter: производительность, поддержка собственного канваса на iOS, баги платформенных реализаций под капотом, и т. д.

Но гибкость Compose Compiler и Compose в целом как инструмента реактивного декларативного UI рвет на части все. Будем дальше наблюдать за этой эпической битвой)

Полностью согласен с названием статьи, но содержание разочаровало. Гораздо полезнее для новичков было бы понять, почему лучше стартовать с языка со строгой типизацией и иммутабельностью, типа Go, Kotlin или Swift

Интересно, как обрабатывать состояния таких View Model в Swift UI. Если я правильно понимаю, интерфейс от них реактивно обновляться не будет? Было бы здорово, если бы Google выпустили официальный аналог https://github.com/rickclephas/KMM-ViewModel или предложили свое решение, если ksp-генерация это некрасиво.

Только ксамарин устарел и его поддержку дропнули, а KMP цветет и пахнет)))

Для использования CMP не обязателен Mac. Только если вы собираетесь разрабатывать под iOS. А судя по отсутствию мака, это не ваш случай)

В этой ситуации вам все ещё будет доступны Android, desktop и web (js или wasm) таргеты.

Даже если бы я матерился через слово (что часто не далеко от истины), это не отменяет факт того, что я окончил филологический факультет МГУ имени М.В. Ломоносова... Зря потратил время на него время, правда. А так да, я сейчас больше программист до мозга костей, тут вы абсолютно правы)

По поводу данных - чистейшей воды имхо, поэтому давайте не будем ломать копья и мы.

Не очень кратко все-таки, но надеюсь разрешил некоторые недоумения)

Постараюсь кратко, по сути, ответить, хотя сразу оговорюсь, что к команде Compose Multiplatform в Jb не имею никакого отношения и мои ответы могут не быть solid truth.

1) В compose.MaterialIconsExtended есть самые разные иконки практически на любой вкус. Используйте не только Default, но и Filled/Outlined (я для работы особенно часто пользуюсь Outlined). Кроме того, вы можете подгрузить изображение из ресурсов, с новым API ресурсов в версии 1.4.0 они наконец-то выкатили нормальную поддержку общих ресурсов. Можно грузить XML, PNG, JPG/JPEG, понятное дело, с SVG проблемы.

2) Влияние порядка вызова методов и расширений интерфейса Modifier действительно влияет на отображение, но это поведение не отличается от Android Jetpack Compose, поэтому смело читайте их документацию. Самый разительный пример - в отличие от CSS, в котором margin и padding разные вещи, для Modifier есть только padding. Но если padding применить до background, он будет работать как внешний отступ, а если после - как внутренний. Почему ребята в гугле решили так сделать - мне лично не понятно. Наверное, чтобы можно было наслаивать много padding и background в одном Modifier... Да, и так тоже можно.

То, что на скриншоте: weight - это функция-расширение интерфейса Modifier, которая реализована внутри интерфейса RowScope и ColumnScope. Вне этих интерфейсов она не существует для компилятора. Поэтому weight нужно объявлять в скоупе Row или Column. Как вариант - вы можете сделать Composable-функцию расширением к ColumnScope или RowScope, например

@Composable
fun ColumnScope.DrawView2(viewName: String) {
  Box(Modifier.weight(1f)) { // будет работать
    ...
  }
}

3) Что касается форматирования кода - в Intellij Idea и Android Studio не нужно возиться с установкой Black Formatter (обожаю его для питона, просто лучший имхо) или чем-то таким. После внесения любых изменений в файл нажмите Ctrl+L и он автоматически отформатируется в соответствии с лушчими практиками. В IDE есть еще настройки форматирования, которые можно кастомизировать. Например, в нашей компании принято включать trailing comma. Не вспомню, где настройки конкретно сейчас, но гуглится легко.

Сочту за комплимент)

Не юзал его ни секунды, пока писал

Спустя три года работы только поймали такое. У устройств проприетарная прошивка на матлабе/c++ для релейных защит. Постоянно выходят новые версии прошивки, новые устройства вводятся в эксплуатацию. Видимо, из-за этого произошла нестыковка

1

Information

Rating
Does not participate
Date of birth
Registered
Activity