如何在自定义视图或其他Android类中使用AndroidInjection类?

Viv*_*ara 20 android dependency-injection dagger dagger-2

我对Android特定模式的问题是,如果你使用他们的AndroidInjection类,除了使用Application Component之外,除了Activities/ Fragments/自定义视图/适配器之外,成员无法注入其他对象.这是因为你无法获得用于注入/ 的Subcomponent(AndroidInjector)的引用.这使得注入Dialogs(如果你使用).ActivitiesFragmentsDialogFragments

AndroidInjection级似乎只支持Android核心类型.

Vas*_*liy 12

以下内容不是您的问题的答案,而是解释为什么您根本不应该问这个问题.

Views一般来说,你应该避免注入习惯.其中的原因列在本文中.

在这种情况下使用方法注入的优点[注入自定义视图]是:

  • 需要从顶级组件(活动或片段)传播依赖关系
  • 方法注入不会打开单一责任原则违规
  • 不依赖于框架
  • 更好的性能

第一个优势可能会让人感到意外,因为从顶级组件传播比向字段添加注释更难,并且涉及更多的样板代码.这肯定是件坏事,对吧?不是在这种情况下.实际上,有两个与这种依赖性传播相关的好方面.首先,依赖关系将在顶级组件中可见.因此,仅通过查看例如片段的字段,代码的读者将立即理解该片段显示图像.对可读性的这种优化使得系统在长期内更容易维护.其次,没有很多用例,其中View的子类需要额外的依赖性.您需要实际工作以提供这些依赖关系这一事实将为您提供一些时间来考虑提供它们是否是一个良好的设计决策.

第二个优势与协作构建有关.您自己可能是非常有经验的软件工程师,但您可能也会遇到经验不足的队友.或者有可能你有一天会离开这个项目,那个接管的人将不会像你一样好.通过使用框架注入一个依赖关系,您基本上可以为其他注入打开一扇门.想象一下,自定义视图中需要来自SharedPreferences的一些数据,以便修复错误.其中一个经验不足的开发人员可能会认为将SharedPreferences直接注入自定义View是一种很好的方法.这样做违反了单一责任原则,但开发人员可能甚至不知道这样的概念.因此,从长远来看,这种注入"后门"会降低设计质量并导致长时间的调试.

将方法注入与自定义视图一起使用的第三个优点是您不将View耦合到依赖注入框架.想象一下,从现在起几年(或其他一些穷人)需要更换框架.事实上,你可能会有数十个活动和碎片开始,这将使你的生活变得悲惨.如果您要处理额外的数十或数百个自定义视图,那么它可能会让您陷入自杀心情.

最后(但并非最不重要)的优势是性能.一个屏幕可以包含一个Activity,几个Fragments和数十个自定义视图.使用依赖注入框架引导此数量的类可能会降低应用程序的性能.对于基于反射的框架尤其如此,但即使是Dagger也会带来一些性能成本.

另外,我建议避免涉及AndroidInjection类的新注入方法.本视频教程对此进行讨论.


azi*_*ian 10

首先,你应该考虑瓦西里的答案.

但是,让我们想一想,在Dagger Android之前我们是如何做到这一点的?我们从Application类中取出的组件构建了一个子组件.稍后,我们可以使用此子组件来注入字段,例如自定义视图.

所以,我们现在会尝试做同样的事情.

假设,我们的目标是将MyAdapter课程注入MyButton:

public class MyButton extends AppCompatButton {

    @Inject MyAdapter adapter;

    public MyButton(Context context) {
        super(context);

        ...
    }

}
Run Code Online (Sandbox Code Playgroud)

让我们让适配器依赖于活动Context,而不是应用程序Context:

public class MyAdapter {

    @Inject
    public MyAdapter(@Named("activity") Context context) {
    }

}
Run Code Online (Sandbox Code Playgroud)

让我们从自定义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);
    }

}
Run Code Online (Sandbox Code Playgroud)

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> {
    }
}
Run Code Online (Sandbox Code Playgroud)

AppModule.java

@Module
abstract class AppModule {

    @Binds
    @Singleton
    @Named("app")
    abstract Context providesContext(Application application);
}
Run Code Online (Sandbox Code Playgroud)

ActivityBindingModule.java

@Module(subcomponents = MySubcomponent.class)
public abstract class ActivityBindingModule {

    @Binds
    @IntoMap
    @ActivityKey(MainActivity.class)
    abstract AndroidInjector.Factory<? extends Activity>
    bindMainActivityInjectorFactory(MySubcomponent.Builder builder);
}
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

SubcomponentModule.java

@Module
abstract class SubcomponentModule {

    @Binds
    @ActivityScope
    @Named("activity")
    abstract Context toContext(MainActivity activity);

}
Run Code Online (Sandbox Code Playgroud)

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

拥有所有这些,现在这里MyButton将是如何:

public class MyButton extends AppCompatButton {

    @Inject MyAdapter adapter;

    public MyButton(Context context) {
        super(context);

        MyApplication.mySubcomponent.inject(this);
    }

}
Run Code Online (Sandbox Code Playgroud)

我承认这看起来很丑陋,当然也不是坚持的方法.我很高兴看到更好的方法.


Dav*_*jak 3

这是因为您无法获取用于注入/的Subcomponent( )的引用。AndroidInjectorActivitiesFragments

您始终可以只注入组件本身。只需将组件的字段添加到您的 Activity / Fragment 中,然后让 Dagger 将其与其余部分一起注入。

// will be injected
@Inject MainActivitySubcomponent component;
Run Code Online (Sandbox Code Playgroud)