KSP у нас не давал больших профитов по сравнению с kapt (Yatagan KSP vs Yatagan kapt, когда я делал замеры).
А есть идеи почему так вышло? Интуитивно KSP должен быть быстрее, так как не надо генерить стабы. Или у вас остались в модуле подключенные kapt зависимости?
«Но если у нас в активити инджектится DispatchingAndroidInjector (для внедрения фрагментов), то ссылка на сабкомпонент активити замкнется через билдер сабкомпонента фрагмента (так как он является inner классом).»
Замкнется через провайдер билдера сабкомпонента фрагмента — напутал.
«Если заглянуть в код DispatchingAndroidInjector#maybeInject, то видно что там создается инстанс билдера». Создается инстанс сабкомпонента, а не билдера — напутал.
1) AndroidInjectionModule нужен для пустых мап. Пустые мапы могут возникнуть когда мы не вставляем никаких сабкомпонентов Activtiy в компонент Application'a, но при этом инджектим в Application инстанс DispatchingAndroidInjector. Если же у нас есть хотя бы один IntoMap то все будет ok. Цитата из документации «You do not have to use @Multibinds for sets or maps that have at least one @IntoSet, @ElementsIntoSet, or @IntoMap binding, but you do have to declare them if they may be empty.».
2) «К примеру активити сабкомпонент создается в момент вызова AndroidInjection.inject() и живет до тех пор пока активити не уничтожена.». Казалось бы это не совсем правда? Если заглянуть в код DispatchingAndroidInjector#maybeInject, то видно что там создается инстанс билдера, а сам он никуда не сохраняется. Но если у нас в активити инджектится DispatchingAndroidInjector (для внедрения фрагментов), то ссылка на сабкомпонент активити замкнется через билдер сабкомпонента фрагмента (так как он является inner классом). В итоге получается что сабкомпоненты самых глубоких сущностей умирают сразу после инджекта.
3) Касательно передачи динамических параметров. 1-й вариант плох тем, что требует контракта, что мы не используем userId, пока не вызван setUserId. 2-й вариант плох тем, что вьюха управляет презентером. 3-й способ наиболее чист, но как верно подмечено, слегка громоздок.
Добавлю еще 2 способа, которые нашел в issues даггера.
a) Инстанс активити или фрагмента, добавляется в граф после вызова AndroidInjection.inject(this), и поэтому все необходимые аргументы можно достать оттуда (то есть активити можно передавать как аргумент в Provides методы модуля и доставать оттуда что нужно).
b) Мы можем добавить BindsInstance методы в билдер, отнаследованный от AndroidInjector.Factory. Далее переопределяем метод seedInstance у билдера, и вызываем все методы BindsInstance, при этом достаем параметры из активити/фрагмента переданного аргументом в seedInstance. Вообще использование BindsInstance более предпочтительно чем конструкторы модуля. Из доков «Binding an instance is equivalent to passing an instance to a module constructor and providing that instance, but is often more efficient. When possible, binding object instances should be preferred to using module instances.»
Я как понял абстрактный модуль нельзя использовать в plus (ибо нужна реализация). Зато заработало если включить абс. модуль в неабстрактный модуль (includes в аннотации).
Спасибо за статью.
А есть идеи почему так вышло? Интуитивно KSP должен быть быстрее, так как не надо генерить стабы. Или у вас остались в модуле подключенные kapt зависимости?
Таким образом в граф добавятся Integer и String из активити и их можно будет инджектить.
билдерсабкомпонента фрагмента (так как он является inner классом).»Замкнется через провайдер билдера сабкомпонента фрагмента — напутал.
билдера». Создается инстанс сабкомпонента, а не билдера — напутал.Хотел бы кое-что добавить:
1) AndroidInjectionModule нужен для пустых мап. Пустые мапы могут возникнуть когда мы не вставляем никаких сабкомпонентов Activtiy в компонент Application'a, но при этом инджектим в Application инстанс DispatchingAndroidInjector. Если же у нас есть хотя бы один IntoMap то все будет ok. Цитата из документации «You do not have to use @Multibinds for sets or maps that have at least one @IntoSet, @ElementsIntoSet, or @IntoMap binding, but you do have to declare them if they may be empty.».
2) «К примеру активити сабкомпонент создается в момент вызова AndroidInjection.inject() и живет до тех пор пока активити не уничтожена.». Казалось бы это не совсем правда? Если заглянуть в код DispatchingAndroidInjector#maybeInject, то видно что там создается инстанс билдера, а сам он никуда не сохраняется. Но если у нас в активити инджектится DispatchingAndroidInjector (для внедрения фрагментов), то ссылка на сабкомпонент активити замкнется через билдер сабкомпонента фрагмента (так как он является inner классом). В итоге получается что сабкомпоненты самых глубоких сущностей умирают сразу после инджекта.
3) Касательно передачи динамических параметров. 1-й вариант плох тем, что требует контракта, что мы не используем userId, пока не вызван setUserId. 2-й вариант плох тем, что вьюха управляет презентером. 3-й способ наиболее чист, но как верно подмечено, слегка громоздок.
Добавлю еще 2 способа, которые нашел в issues даггера.
a) Инстанс активити или фрагмента, добавляется в граф после вызова AndroidInjection.inject(this), и поэтому все необходимые аргументы можно достать оттуда (то есть активити можно передавать как аргумент в Provides методы модуля и доставать оттуда что нужно).
b) Мы можем добавить BindsInstance методы в билдер, отнаследованный от AndroidInjector.Factory. Далее переопределяем метод seedInstance у билдера, и вызываем все методы BindsInstance, при этом достаем параметры из активити/фрагмента переданного аргументом в seedInstance. Вообще использование BindsInstance более предпочтительно чем конструкторы модуля. Из доков «Binding an instance is equivalent to passing an instance to a module constructor and providing that instance, but is often more efficient. When possible, binding object instances should be preferred to using module instances.»
Возник вопрос с Binds. Может ли сабкомпонент иметь абстрактный модуль в качестве зависимости? Если да, то как создать такой модуль?