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

Android developer

Send message

Он используется в 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, то уже слишком много информации.

"Освоенная технология" не равно "готовый продукт". Надо, чтобы кто-то что-то спроектировал и заказал под 90 нм, потом это произвели, а затем ещё и протестировали. Должно пройти время, чтобы появились продукты.

Как пример, Samsung заявил, что освоил 5 нм в апреле 2019
https://www.cnews.ru/news/top/2019-04-16_samsung_osvoila_proizvodstvo_5_nm_protsessorov
А первый процессор на 5 нм представили в ноябре 2020
https://en.wikipedia.org/wiki/Exynos#Exynos_1000_series
https://habr.com/ru/company/selectel/blog/528372/
Прошло полтора года.

В случае с Микрон чипы более простые, чем навороченный процессор топового смартфона, но вероятно ещё полгода-год до первого продукта.

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

В моем примере property только добавлялись, но они могут и удалятся. В таком случае придется удалять или делать nullable property в базовом классе. В итоге объекты вообще могут разойтись и у них не будет ни одного общего property. И это нормально. Так как это изначально по логике - абсолютно разные объекты, предназначенные для разных фичей. Просто в определенный момент они были одинаковы. В итоге базовый Image может быть вообще удален, а вот связи между модулями останутся и с ними придется разбираться. Поэтому проблема и бъёт, по большей части, по многомодульным проектам.

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

Добрый день. Это просто допущение.

У Яндекс.Карты есть возможность добавлять свои слои, но там весьма ограниченный набор функций и к сожалению возможности работать с OpenGL напрямую там нет.
https://yandex.ru/dev/maps/mapkit/doc/android-ref/full/com/yandex/mapkit/layers/package-summary.html
В целом бы конечно свой слой с OpenGL по сути решил бы многие проблемы)

Не до конца понял про "параллельно рисовать в шаренную память". Тут проблема не только и не столько в рисовании, а в том как потом добавить это на карту.
Пробовали например вместо 4 объектов с большими Bitmap добавлять на карту 16-32-64 объекта на карту с Bitmap меньшего размера. Но по производительности получалось хуже.

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

А по поводу рендеринга тайлов на мобиле - мы пробовали и визуально нам это меньше понравилось, поэтому развивали наш текущий подход
Вот пример из экспериментов тайлами
https://youtu.be/39_qCY_ij9Y

1

Information

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