我对Android特定模式的问题是,如果你使用他们的AndroidInjection
类,除了Activities
/ Fragments
/自定义视图/适配器之外,成员无法注入其他对象,除了应用程序组件。这是因为您无法获得用于注入Subcomponent
/ AndroidInjector
的{{1}}(Activities
)的引用。
这使得注入对话框(如果使用Fragments
)。
DialogFragments
类似乎只支持核心Android类型。
答案 0 :(得分:11)
以下内容不是您的问题的答案,而是解释为什么您根本不应该问这个问题。
一般情况下,您应该避免注入自定义Views
。其原因列于this article。
在这种情况下使用方法注入的优点[注入自定义视图]是:
- 依赖关系需要从顶级组件(活动或片段)
传播- 方法注入无法打开单一责任原则违规行为
- 不依赖于框架
- 更好的表现
第一个优势可能会让人感到惊讶,因为传播来自 顶级组件比向字段添加注释更难,并且 涉及更多的样板代码。这肯定是件坏事,对吧? 不是在这种情况下。事实上,有两个好的方面相关联 这种依赖关系的传播。首先,依赖 将在顶级组件中显示。因此,只是通过寻找 在例如片段的字段,代码的读者将立即 了解这个片段显示图像。这样的优化 可读性使系统更容易维护 术语。其次,子类的用例并不多 查看需要其他依赖项。你需要实际的事实 工作以提供这些依赖关系会给你一点 是时候考虑提供它们是一个很好的设计决策 开头。
第二个优势与协作建设有关。您 可能是非常有经验的软件工程师,但你会 可能也有经验不足的队友。或者有可能 你有一天会离开这个项目,那个接管的人会 不如你好通过使用a注入一个单独的依赖项 框架,你基本上打开了其他注射的大门。想像 在自定义视图中需要来自SharedPreferences的一些数据 为了例如修复一个bug。经验不足的开发人员之一 可能会认为这是注入SharedPreferences的好方法 直接进入自定义View。这样做违反了单一责任 原则,但开发人员可能甚至不知道这样的 概念。因此,从长远来看,这种注射“后门”可以 降低设计质量并导致长时间的调试。
将方法注入与自定义视图一起使用的第三个优点是 您没有将View耦合到依赖注入框架。只是 想象从现在开始的几年(或其他一些穷人)需要 替换框架。事实上,你可能会有几十个 开始的活动和碎片会让你的生活变得悲惨。 如果您要处理额外的数十或数百个自定义视图, 那么它可能会让你陷入自杀心情。
最后(但并非最不重要)的优势是性能。一个屏幕即可 包含一个Activity,几个Fragments和数十个自定义视图。 使用依赖注入引导此数量的类 框架可能会降低应用程序的性能。特别是 对于基于反射的框架来说是正确的,但即便是Dagger也有一些 性能成本。
另外,我建议避免涉及AndroidInjection
类的新注入方法。它在this video tutorial中进行了讨论。
答案 1 :(得分:10)
首先,您应该考虑Vasily's answer。
但让我们想一想我们在Dagger Android之前做过这个怎么样的事情?
我们从Application
类中获取的组件构建了一个子组件。稍后,我们可以使用此子组件来注入字段,例如自定义视图。
所以,我们现在尝试做同样的事情。
假设,我们的目标是将MyAdapter
类注入MyButton
:
public class MyButton extends AppCompatButton {
@Inject MyAdapter adapter;
public MyButton(Context context) {
super(context);
...
}
}
让我们让适配器依赖于活动Context
,不应用Context
:
public class MyAdapter {
@Inject
public MyAdapter(@Named("activity") Context context) {
}
}
让我们从自定义Application
类开始。
<强> MyApplication.java 强>
public class MyApplication extends DaggerApplication {
@Inject
DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
public static MySubcomponent mySubcomponent;
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerAppComponent.builder()
.create(this);
}
}
<强> AppComponent.java 强>:
@Component(modules = {AndroidSupportInjectionModule.class, ActivityBindingModule.class, AppModule.class})
@Singleton
public interface AppComponent extends AndroidInjector<MyApplication> {
@Component.Builder
abstract class Builder extends AndroidInjector.Builder<MyApplication> {
}
}
<强> AppModule.java 强>
@Module
abstract class AppModule {
@Binds
@Singleton
@Named("app")
abstract Context providesContext(Application application);
}
ActivityBindingModule.java
@Module(subcomponents = MySubcomponent.class)
public abstract class ActivityBindingModule {
@Binds
@IntoMap
@ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindMainActivityInjectorFactory(MySubcomponent.Builder builder);
}
AndroidSupportInjectionModule.java
装有匕首本身。如果您不使用支持包中的类(即android.support.v4.app.Fragment
而不是android.app.Fragment
),请使用AndroidInjectionModule.java
。
<强> MySubcomponent.java 强>
@ActivityScope
@Subcomponent(modules = {SubcomponentModule.class/*, other modules here, if needed */})
public interface MySubcomponent extends AndroidInjector<MainActivity> {
void inject(MyButton button);
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {
public abstract MySubcomponent build();
}
}
<强> SubcomponentModule.java 强>
@Module
abstract class SubcomponentModule {
@Binds
@ActivityScope
@Named("activity")
abstract Context toContext(MainActivity activity);
}
<强> MainActivity.java 强>
public class MainActivity extends AppCompatActivity {
@Inject
MySubcomponent subcomponent;
@Override
protected void onCreate(Bundle savedInstanceState) {
// Will inject `subcomponent` field
AndroidInjection.inject(this);
// Saving this component in a static field
// Hereafter you are taking responsibility of mySubcomponent lifetime
MyApplication.mySubcomponent = subcomponent;
super.onCreate(savedInstanceState);
setContentView(new MyButton(this));
}
}
拥有所有这些,现在在MyButton
看起来像这样:
public class MyButton extends AppCompatButton {
@Inject MyAdapter adapter;
public MyButton(Context context) {
super(context);
MyApplication.mySubcomponent.inject(this);
}
}
我承认这看起来很丑陋,当然也不是坚持的方法。我很高兴看到更好的方法。
答案 2 :(得分:2)
这是因为您无法获得用于注入
Subcomponent
/AndroidInjector
的{{1}}(Activities
)的引用。
您可以随时注入组件本身。只需将组件的字段添加到Activity / Fragment中,然后让Dagger将其与其余部分一起注入。
Fragments
答案 3 :(得分:2)
下面的Github问题讨论了像AndroidInjector
这样的dagger-android类是否支持在Views内部注入的问题:
https://github.com/google/dagger/issues/720
引用其中一位图书馆作者:
这里有一个哲学观点和后勤/实施点。
首先,我们并不完全清楚注入观点是正确的做法。视图对象意味着绘制,而不是其他。控制器(在传统的MVC模式中)是可以协调并将适当的数据传递给视图的控制器。注入一个视图会模糊片段和视图之间的界限(也许一个子片段确实是相应的构造?)
从实现的角度来看,还存在一个问题,即没有规范的方法来检索View的父片段(如果有的话)或者Activity来检索父组件。有人建议建立这种关系,但到目前为止,我们还没有看到任何似乎暗示我们能够正确做到这一点的事情。我们可以调用View.getContext()。getApplicationContext()并从那里注入,但是跳过中间层而没有任何选项,这与我们设计的其余部分不一致,即使它起作用,也可能会让用户感到困惑。 / p>
这加强了瓦西里回答中表达的观点。
为了进一步增加,人们通常似乎想在自定义视图中注入模型层依赖项。这是一个坏主意,因为它违反了separation of concerns的软件工程原则。
关联视图和模型的正确解决方案是编写适配器,如RecyclerView和ListView的适配器。您可以在Fragment或Presenter级别注入模型层依赖关系,并在那里设置适配器。