Область и подкомпоненты кинжала 2
Я пытаюсь улучшить свое приложение и код с большей поддержкой, используя Dagger2
. Я понял общую идею, но до сих пор не могу понять, как управлять областями с помощью Dagger2
Я вложил кинжал в свой проект (звучит смешно).
Я создал компонент ApplicationComonent
, и он отлично работает в моем проекте.
Вот мой код.
@Singleton
@Component(modules = {
ApplicationModule.class,
ThreadingModule.class,
NetworkModule.class,
DatabaseModule.class,
ServiceModule.class,
ParseModule.class,
PreferencesSessionModule.class})
public interface ApplicationComponent {
ActivityComponent activityComponent(ActivityModule activityModule);
void inject(BaseActivity baseActivity);
void inject(MainAppActivity mainAppActivity);
void inject(MyApplication application);
void inject(BaseFragment baseFragment);
void inject(MyService service);
void inject(RegistrationIntentService service);
}
Я создаю экземпляр компонента в классе MyApplication
, как этот
private void initializeAndInjectComponent() {
mApplicationComponent =
DaggerApplicationComponent
.builder()
.threadingModule(new ThreadingModule(1))
.applicationModule(new ApplicationModule(this))
.networkModule(new NetworkModule(
MyService.API_SERVER_BASE_URL,
MyService.TIMEOUT))
.build();
mApplicationComponent.inject(this);
}
И я могу получить компонент для ввода в моем Activities
MyApplication application = MyApplication.get(this);
application.getApplicationComponent().inject(this);
Все работает отлично.
Чтобы добавить каждый метод, а также класс модуля, аннотируется с областью @Singleton
, все модули, относящиеся к ApplicationComponent
Теперь я хочу улучшить зависимости, и я видел множество примеров с пользовательскими областями, например @PerActivity
, @PerFragment
. У меня много вопросов, но об этом позже.
Итак, я создал ActivityComponent
@PerActivity
@Subcomponent(
modules = {
NetworkServiceModule.class,
ActivityModule.class,
PermissionModule.class
})
public interface ActivityComponent {
Activity activity();
void inject(BaseActivity baseActivity);
}
Все модули выглядят как
@PerActivity
@Module
public class ActivityModule {
private Activity mActivity;
public ActivityModule(Activity activity) {
this.mActivity = activity;
}
@Provides
@PerActivity
Activity provideActivity() {
return this.mActivity;
}
}
У меня есть следующие зависимости в моем BaseActivity
// Dependencies from ApplicationComponent
@Inject
protected ApplicationSettingsManager mApplicationSettingsManager;
@Inject
protected ScheduledThreadPoolExecutor mPoolExecutor;
// Dependencies from ActivityComponent
@Inject
protected SpiceManager mSpiceManager;
@Inject
protected PermissionController mPermissionController;
И в моем методе onCreate()
я впрыскиваю следующее
MyApplication application = MyApplication.get(this);
application.getApplicationComponent().activityComponent(new ActivityModule(this)).inject(this);
До создания подкомпонента ActivityComponent
это было
MyApplication application = MyApplication.get(this);
application.getApplicationComponent().inject(this);
Теперь я получил сообщение об ошибке
Error:(34, 10) error: com.octo.android.robospice.SpiceManager cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.
BaseActivity.mSpiceManager
[injected field of type: com.octo.android.robospice.SpiceManager mSpiceManager]
Я не могу понять, где проблема, что я пропустил.
Мои вопросы о облаках в кинжале2.
Все, кроме @Singleton
, игнорируется Кинжалом 2, верно?
Я не понимаю, как управляется жизнь компонента? У меня есть только одна идея.
-
Когда вы используете @Singleton
, кинжал-аннотация создает объект в каком-то статическом пуле, который будет существовать в течение всего жизненного цикла приложения, и будет уничтожен, когда экземпляр JVM-процесса (dalvik VM, ART) будет уничтожен.
-
Когда вы используете какую-либо другую аннотацию, для вас, как разработчика, лучше поддерживать код, @PerActivity
, @PerFragment
- это просто нестандартная аннотация. И если вы разместите компонент @PerFragment
в классе Application, он будет жить до тех пор, пока приложение будет работать. Я прав?
-
Итак, я понимаю это так: если кинжал найдет аннотацию @Singleton
, она добавит статическую ссылку на компонент, когда она будет создана впервые, а в случае любой другой аннотации она не будет ссылаться на компонент.
Я был бы очень благодарен за любую помощь в описанных выше проблемах.
UPDATE
Спасибо David Medenjak
за отличный ответ, я получил гораздо лучшее понимание Dagger2
.
Я также нашел проблему, поскольку теперь я использую отдельный компонент Activity
, я забыл о двух строках в ApplicationComponent
и изменил inejction в моих MainActivity
на ActivityComponent
вместо ApplicationComponent
, поэтому наверняка не удалось разрешить зависимости от подкомпонента.
void inject(BaseActivity baseActivity);
void inject(MainAppActivity mainAppActivity);
Теперь все работает отлично, мне нравится Dagger2
и разделенная архитектура.
Ответы
Ответ 1
Немного радикально, но для упрощения вещей:
Все аннотации Scope - не что иное, как синтаксический сахар, включая @Singleton
.
Области в основном просто обеспечивают проверку времени компиляции. Циклические зависимости, или ошибки о вещах, которые вы, возможно, пропустили. @Singleton
точно так же, как и любая другая область, единственное отличие состоит в том, что это уже существующая аннотация, и вам не нужно ее создавать самостоятельно. Вместо этого вы можете использовать @MySingleton
.
[...] кинжал создает объект в некотором статическом пуле, который будет существовать в течение всего жизненного цикла приложения
Нет. Кинжал ничего не статично. У вас есть компоненты. Эти компоненты содержат ваши объекты, созданные модулями. Если объект в компоненте имеет область действия компонента, он будет создан только один раз в этом точном компоненте. Если вы решили создать 2 AppComponent
объекта, у вас будет 2 объекта каждого аннотированного объекта @Singleton
, каждый из которых будет находиться внутри его компонента. Вот почему вы должны сохранить ссылку на компонент. Большинство реализаций, которые я видел или использовал, сохраняют их AppComponent
в пределах Application
. Если вы сделаете это, вы можете использовать его как singleton &mdash, это все еще просто POJO.
[...] вы размещаете компонент @PerFragment в классе Application, он будет работать до тех пор, пока Application будет работать.
Да. Как уже описано выше, это просто объект. Сохраните ссылку, вы держите объекты. Выбросьте его или создайте новый, и у вас есть новые объекты (определенные внутри этого компонента/области). Вы должны, хотя и не сохранять компоненты, зависящие от активности или фрагмента, в каком-либо месте, кроме как в действиях или фрагментах, соответственно, так как их хранение, например. в вашем компоненте приложения, скорее всего, приведет к утечке памяти. (Если это не так, вам, вероятно, не понадобится область действия или фрагмента.)
Если кинжал найдет аннотацию @Singleton
, она добавит статическую ссылку на компонент, когда она будет создана впервые, а в случае любой другой аннотации она не будет ссылаться на компонент.
Опять же, нет. Ничего статичного. Обычные старые объекты java. У вас может быть несколько компонентов @Singleton
со своими собственными объектами, но вы, вероятно, не должны (хотя это то, что делает возможным тестирование инструментария /easy &mdash, просто заменяйте компоненты.)
Указанная ошибка
SpiceManager не может быть предоставлен без конструктора @Inject или из метода @Provides- или @Produces-annotated.
Это означает, что компонент, который вы пытаетесь ввести ваш объект, не может найти способ создать или предоставить SpiceManager
. Убедитесь, что вы предоставили его из своего AppComponent или в каком-либо другом месте, не пропустите никаких комментариев и т.д.