Pull to refresh
55
0
Данил Перевалов @princeparadoxes

Android developer

Send message

А не лучше ли будет использовать debounce вместо delay? С debounce отбросятся ненужные события и сразу будет нарисован нужный (последний) вариант. Это уменьшит общее количество отрисовок.

Более того, debounce можно повесить на события с UI на ViewModel, которые сообщают о смене позиции камеры, что уберёт часть запросов на сервер.

P.S. Compose рассматривает List (а они основа вашего MapUiState), как Unstable. Это приводит к лишним рекомпозициям, так как Compose не может понять, изменилось ли что-то в списке или нет и принудительно рекомпозирует. Попробуйте использовать неизменяемые коллекции.

Есть такое) В статье это больше для примера того, как усложнить восприятие кода для атакующего.
И насколько я понимаю, при 25 или даже 100 итерациях погрешность из-за чисел с плавающей запятой всё равно будет слишком мала и при округлении "нивелируется".
Это больше актуально когда все вычисления во float (особенно умножение) или когда итераций цикла миллионы или миллиарды.

1) С ходу профита особого не вижу. Это просто property в которую идёт запись и чтение. Но если есть сценарий, где SharedFlow что-то сильно улучшит, то можно без проблем и его применить.

2) В таком случае можно GlobalEvent сделать не sealed class, а interface. Реализации хранить в нескольких модулях (например, api-модуль фичей), которые подключать к тем модулям, которым этот GlobalEvent нужен. Правда тут стоит быть аккуратным, если GlobalEvent нужен лишь ограниченному числу модулей, то возможно он не такой уж и глобальный? И стоит рассмотреть другие варианты общения между компонентами.

А, Вы про это. Да, есть такое, в проде такой код использовать нельзя. Он тут скорее для иллюстрации логики подмешивания дополнительных данных. У меня в нём ещё и корнер-кейсы, вроде того, что android_get_device_api_level может вернуть -1, не учтены.

Я на C++ давно уже не писал, так что это точно не хороший код)

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

В целом это весьма хорошее решение. Но конкретно у нас с таким подходом есть ряд проблем:

  • Нет прямой поддержки dynamic-feature, так как их код не виден из главного модуля.

  • Для демоприложений такой граф надо настраивать для каждого из демоприложений. Потом ещё и поддерживать. С системой с meta-data достаточно просто подключить модуль к приложению и он сам заработает.

  • У app есть два варианта "знать" о подключённых к нему модулях: implementation и runtimeOnly. Первый вариант позволяет видеть код из подключённых модулей в app, но в тоже время, если в каком-то из подключённых модулей меняется код, то и app вынужден пересобраться (иногда лишь частично, но не суть). Это било по времени горячей сборки и мы перешли на второй вариант - runtimeOnly. С ним код в app уже не доступен, зато и лишних пересборок нет. Так как код недоступен, то и от мультибиндингов смысла нет.

Он используется в C++ слое. Файловый дескриптор используется двумя утилитами eventfd и epoll, они позволяют сделать что-то вроде wait/notify на уровне системы. Файловый дескриптор выступает чем-то вроде монитора.

Например, приложение сделало все действия которые хотело. Ему больше нечего делать и оно ставится в режим ожидания (wait). Когда системе надо будет его разбудить, она использует файловый дескриптор чтобы его уведомить о том, что пора обрабатывать новые действия (notify).

Если интересна тема, то есть статья про Looper в C++ слое, там подробнее про эту логику написано.

А не размышляли над тем, чтобы уменьшить количество подключаемых модулей к demo-приложению через моки?

Все зависимости, реальная работа которых не очень важна для функциональности demo-приложения и его логики, можно банально замокать через, например, MockK.

mockk<BduCustomizeDependencies>(relaxed = true)

Выглядит как костыль, не спорю, но это работает. Правда, при условии, что у вас фичи делятся на api/(impl, ui, bl) модули.

В нашем случае это помогало избегать подключения аналогов :legacy-heavy-module. Что резко сокращало количество подключаемых модулей.

Справедливо. Как-то не подумал об этом

non-blocking IO подход же реализует не фреймворк многопоточки, а конкретная сущность, которая делает IO вызов, например, сетевой клиент. Соответственно, от фреймворков многопоточки ничего зависеть не будет. Я хотел именно их между собой сравнить. И, как мне показалось, на сравнение фреймворков многопоточки, блокирующий IO или нет, не должно повлиять. Поэтому взял блокирующий, так как он сильно проще визуально и нагляднее.

Или я ошибся?

Я генерировал основной график в Google Docs. Затем обрисовывал его в https://app.diagrams.net/ и добавлял дополнительную информацию. Остальные иллюстрации тоже там делал.

К сожалению количество строк сгенерированного кода не считал, а вот по размеру сборки подскажу. Для debug сборки (без обфускации, AppBundle и прочего) размер такой:
- Dagger. Всего 240.6 Мб. Вес именно кода 73.3 Мб.
- Yatagan + kapt. Всего 239.1 Мб. Вес именно кода 71.8 Мб.
- Yatagan + Reflect. Всего 239 Мб. Вес именно кода 71.7 Мб.
- При использовании KSP вес примерно такой же, как и с Yatagan + Kapt.

Если что, размер релизной сборки после обфускации и AppBundle - 80 Мб.

По размеру сборки и кода понятно, что Yatagan у нас генерирует где-то 100 Кб кода, а Dagger 1.5 Мб. Так что, где-то в 15 раз разница по количеству генерируемого кода.

Ну и у нас для Dagger проброшенны аргументы formatGeneratedSource как disabled и fastInit как enable. По идее, оба флага могут чуть уменьшать размер сгенерированного Dagger кода.

Протестировать количество каналов хорошая идея. Спасибо!

  1. Количество каналов памяти, как я понимаю, в основном повысит пропускную способность памяти. Поднятие частоты памяти тоже на это влияет. Но по тестам частота/пропускная способность памяти почти не повлияла. Как будто увеличение количества каналов не должно помочь. Или я заблуждаюсь?
    По поводу кэша - да. Чем его больше - тем лучше) Но тут сложно определить насколько именно. Наверно только если сравнить Ryzen 5800X и Ryzen 5800X3D которые только размером кэша и отличаются.

  2. Тут речь скорее про то, что замена HDD на SSD достаточно дешёвый способ увеличить скорость сборки. Относительно замены процессора или оперативной памяти, конечно же. Так что, честно говоря, не вижу особого смысла собираться на HDD.

Я правильно понимаю, что отсутствует генерация фреймворком Builder для компонента?
И теперь обязательно надо создавать вложенный класс с аннотацией Component.Builder у каждого компонента.

И сделано это по всей видимости, чтобы поддержать реализацию с рефлексией.

Dagger, например, сам генерирует Dagger<ComponentName>.Builder если отсутствует костомный Component.Builder.

Просто фича то прикольная была) К пример, если в проекте есть единая точка получения зависимостей, то можно было вообще выкинуть из компонента код с его созданием, оставив только метод create. Ну, а подстановку зависимостей делать под капотом с помощью рефлексии или вот пример с KSP (там в конце).

Было бы круто добавить в будущем, чтобы они генерировались, например, с помощью отдельного процессора. Иначе кажется, что многие в любом случае свою реализацию такого процессора у себя сделают)

Получится примерно так

internal class SomeEventBuilder(
   private val userId: Int,
   private val itemsIds: List<Int>,
   private val timestampProvider: () -> Long
) : EventBuilder {

   override fun buildEvent() = event {
       analytics1 {
           eventName = "SomeEventName"
           reason = "SomeReason"
           action = CommonAnalyticsAction.CLICK
           source {
               feature = Feature1AnalyticsFeature.FEATURE_1
               screen = Feature1AnalyticsScreen.FEATURE_1_SCREEN
               block = customBlock("SomeBlock")
           }
       }
       analytics2 {
           eventName = "SomeName"
           source = "SomeBlockOfUser$userId"
           action = customAction("Click")
           screen = Feature1AnalyticsScreen.FEATURE_1_SCREEN
       }
   }
}

Если отбросить из описанных мной в статье плюсов те, что можно реализовать в других подходах, то, пожалуй, мой любимый - то, как выглядит и читается всё это на Pull Request'ах. Даже при беглом взгляде считывается структура и значения.

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

В теории подменить один модуль другим во время сборки мне кажется возможным. Например создать новый BuildType в котором будут зависимости на заглушки, использовать его в AndroidStudio. А вот в сборке debug/release уже подставлять реальные модули. В рамках эксперимента, ради интереса, это можно попробовать провернуть.

Но тогда мы от многомодульности возьмём только один аспект - разделение кода. Может сильно упасть скорость сборки, так как при изменении реализации, у нас вынуждены будут собраться все зависимые модули. А стоимость поддержки будет схожа с использованием api/feature.

Но это всё весьма теоритические рассуждения)

Если говорить в контексте данного примера, то логика фичи-1 (feature-1-impl) знает только об интерфейсах фичи-2 (feature-2-api), но не о реализации (feature-2-impl). Соответственно реализацию всё ещё надо подключать к app. Так как на неё связей нет.

Если говорить в целом, то обычно фичи при написании подключают к app, а связи между фичами формируются позже в процессе написания дополнительной логики. Чтобы постоянно не отслеживать есть ли транзитивная связь на фичу, связь между app и фичей остаётся. Иначе пришлось бы переодически подключать/отключать фичу от app. Что не удобно. Gradle самостоятельно справляется с таким и делает это на отлично.

Есть инструменты:
https://github.com/savvasdalkitsis/module-dependency-graph

https://github.com/ivancarras/graphfity

https://github.com/JakeWharton/SdkSearch/blob/master/gradle/projectDependencyGraph.gradle
Но, честно говоря, первые пару раз на их результат посмотреть весело, а вот когда количество модулей переваливает за 50, то уже слишком много информации.

1

Information

Rating
Does not participate
Location
Омск, Омская обл., Россия
Works in
Date of birth
Registered
Activity