【问题标题】:How to set up DAGGER dependency injection from scratch in Android project?如何在 Android 项目中从头开始设置 DAGGER 依赖注入?
【发布时间】:2015-01-18 03:46:06
【问题描述】:

如何使用匕首?如何配置 Dagger 以在我的 Android 项目中工作?

我想在我的 Android 项目中使用 Dagger,但我觉得它很混乱。

编辑:Dagger2 也于 2015 年 4 月 15 日推出,而且更加令人困惑!

[这个问题是一个“存根”,当我了解更多关于 Dagger1 和更多关于 Dagger2 的信息时,我将在这个问题上添加到我的答案中。这个问题更像是一个指南,而不是一个“问题”。]

【问题讨论】:

  • 感谢分享。你知道如何注入 ViewModel 类吗?我的 ViewModel 类没有任何 @AssistedInject 但它具有 Dagger graph 可以提供的依赖项?
  • 还有一个问题,对于 Dagger2,是否有可能拥有一个对象并且它的引用由 ViewModelPageKeyedDataSource 共享?就像我使用 RxJava2,并希望 CompositeDisposable 由两个类共享,如果用户按下后退按钮,我想清除 Disposable 对象。我在这里添加了案例:stackoverflow.com/questions/62595956/…
  • 您最好将compositeDisposable 放在ViewModel 中,并可能传递与您的自定义PageKeyedDataSource 的构造函数参数相同的compositeDisposable,但我不会真正使用Dagger,因为那时您需要子范围子组件,而 Hilt 不会让您真正轻松地支持。

标签: android dependency-injection dagger dagger-2


【解决方案1】:

Dagger 1.x指南:

步骤如下:

1.) 将Dagger 添加到依赖项的build.gradle 文件中

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    ...
    compile 'com.squareup.dagger:dagger:1.2.2'
    provided 'com.squareup.dagger:dagger-compiler:1.2.2'

另外,添加packaging-option 以防止出现关于duplicate APKs 的错误。

android {
    ...
    packagingOptions {
        // Exclude file to avoid
        // Error: Duplicate files during packaging of APK
        exclude 'META-INF/services/javax.annotation.processing.Processor'
    }
}

2.) 创建一个Injector 类来处理ObjectGraph

public enum Injector
{
    INSTANCE;

    private ObjectGraph objectGraph = null;

    public void init(final Object rootModule)
    {

        if(objectGraph == null)
        {
            objectGraph = ObjectGraph.create(rootModule);
        }
        else
        {
            objectGraph = objectGraph.plus(rootModule);
        }

        // Inject statics
        objectGraph.injectStatics();

    }

    public void init(final Object rootModule, final Object target)
    {
        init(rootModule);
        inject(target);
    }

    public void inject(final Object target)
    {
        objectGraph.inject(target);
    }

    public <T> T resolve(Class<T> type)
    {
        return objectGraph.get(type);
    }
}

3.) 创建一个RootModule 将您未来的模块链接在一起。请注意,您必须包含 injects 以指定您将使用 @Inject 注释的每个类,否则 Dagger 会抛出 RuntimeException

@Module(
    includes = {
        UtilsModule.class,
        NetworkingModule.class
    },
    injects = {
        MainActivity.class
    }
)
public class RootModule
{
}

4.) 如果您的模块中有其他子模块在您的 Root 中指定,请为这些子模块创建:

@Module(
    includes = {
        SerializerModule.class,
        CertUtilModule.class
    }
)
public class UtilsModule
{
}

5.) 创建叶模块,接收依赖项作为构造函数参数。就我而言,没有循环依赖,所以我不知道 Dagger 是否可以解决这个问题,但我觉得不太可能。构造函数参数也必须由 Dagger 在 Module 中提供,如果指定 complete = false 则它也可以在其他 Module 中。

@Module(complete = false, library = true)
public class NetworkingModule
{
    @Provides
    public ClientAuthAuthenticator providesClientAuthAuthenticator()
    {
        return new ClientAuthAuthenticator();
    }

    @Provides
    public ClientCertWebRequestor providesClientCertWebRequestor(ClientAuthAuthenticator clientAuthAuthenticator)
    {
        return new ClientCertWebRequestor(clientAuthAuthenticator);
    }

    @Provides
    public ServerCommunicator providesServerCommunicator(ClientCertWebRequestor clientCertWebRequestor)
    {
        return new ServerCommunicator(clientCertWebRequestor);
    }
}

6.) 扩展Application 并初始化Injector

@Override
public void onCreate()
{
    super.onCreate();
    Injector.INSTANCE.init(new RootModule());
}

7.) 在您的 MainActivity 中,调用 onCreate() 方法中的 Injector。

@Override
protected void onCreate(Bundle savedInstanceState)
{
    Injector.INSTANCE.inject(this);
    super.onCreate(savedInstanceState);
    ...

8.) 在您的 MainActivity 中使用 @Inject

public class MainActivity extends ActionBarActivity
{  
    @Inject
    public ServerCommunicator serverCommunicator;

...

如果您收到错误 no injectable constructor found,请确保您没有忘记 @Provides 注释。

【讨论】:

  • 这个答案部分基于Android Bootstrap生成的代码。所以,感谢他们解决这个问题。解决方案使用Dagger v1.2.2
  • dagger-compiler 的范围应该是provided 否则将被包含在应用程序中,并且它是在 GPL 许可下的。
  • @deniskniazhev 哦,我不知道!感谢您的提醒!
【解决方案2】:

Dagger 2.x指南(修订版 6)

步骤如下:

1.)Dagger 添加到您的build.gradle 文件中:

  • 顶级build.gradle

.

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.0'
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' //added apt for source code generation
    }
}

allprojects {
    repositories {
        jcenter()
    }
}
  • 应用级build.gradle

.

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt' //needed for source code generation

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.2"

    defaultConfig {
        applicationId "your.app.id"
        minSdkVersion 14
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        debug {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    apt 'com.google.dagger:dagger-compiler:2.7' //needed for source code generation
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:24.2.1'
    compile 'com.google.dagger:dagger:2.7' //dagger itself
    provided 'org.glassfish:javax.annotation:10.0-b28' //needed to resolve compilation errors, thanks to tutplus.org for finding the dependency
}

2.) 创建提供依赖项的 AppContextModule 类。

@Module //a module could also include other modules
public class AppContextModule {
    private final CustomApplication application;

    public AppContextModule(CustomApplication application) {
        this.application = application;
    }

    @Provides
    public CustomApplication application() {
        return this.application;
    }

    @Provides 
    public Context applicationContext() {
        return this.application;
    }

    @Provides
    public LocationManager locationService(Context context) {
        return (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
    }
}

3.) 创建AppContextComponent 类,该类提供接口以获取可注入的类。

public interface AppContextComponent {
    CustomApplication application(); //provision method
    Context applicationContext(); //provision method
    LocationManager locationManager(); //provision method
}

3.1.) 以下是创建具有实现的模块的方式:

@Module //this is to show that you can include modules to one another
public class AnotherModule {
    @Provides
    @Singleton
    public AnotherClass anotherClass() {
        return new AnotherClassImpl();
    }
}

@Module(includes=AnotherModule.class) //this is to show that you can include modules to one another
public class OtherModule {
    @Provides
    @Singleton
    public OtherClass otherClass(AnotherClass anotherClass) {
        return new OtherClassImpl(anotherClass);
    }
}

public interface AnotherComponent {
    AnotherClass anotherClass();
}

public interface OtherComponent extends AnotherComponent {
    OtherClass otherClass();
}

@Component(modules={OtherModule.class})
@Singleton
public interface ApplicationComponent extends OtherComponent {
    void inject(MainActivity mainActivity);
}

注意::您需要在模块的@Provides 注释方法上提供@Scope 注释(如@Singleton@ActivityScope),以在生成的组件中获取范围提供程序,否则它将是无作用域的,每次注入时都会得到一个新实例。

3.2.) 创建一个应用程序范围的组件,指定您可以注入的内容(这与 Dagger 1.x 中的 injects={MainActivity.class} 相同):

@Singleton
@Component(module={AppContextModule.class}) //this is where you would add additional modules, and a dependency if you want to subscope
public interface ApplicationComponent extends AppContextComponent { //extend to have the provision methods
    void inject(MainActivity mainActivity);
}

3.3.) 对于您可以通过构造函数自己创建并且不想使用@Module 重新定义的依赖项(例如,您使用构建风格而不是更改实现的类型),您可以使用@Inject带注释的构造函数。

public class Something {
    OtherThing otherThing;

    @Inject
    public Something(OtherThing otherThing) {
        this.otherThing = otherThing;
    }
}

另外,如果你使用@Inject构造函数,你可以使用字段注入而无需显式调用component.inject(this)

public class Something {
    @Inject
    OtherThing otherThing;

    @Inject
    public Something() {
    }
}

这些@Inject 构造函数类会自动添加到相同作用域的组件中,而无需在模块中显式指定它们。

@Singleton 范围内的 @Inject 构造函数类将出现在 @Singleton 范围内的组件中。

@Singleton // scoping
public class Something {
    OtherThing otherThing;

    @Inject
    public Something(OtherThing otherThing) {
        this.otherThing = otherThing;
    }
}

3.4.) 在为给定接口定义特定实现之后,如下所示:

public interface Something {
    void doSomething();
}

@Singleton
public class SomethingImpl {
    @Inject
    AnotherThing anotherThing;

    @Inject
    public SomethingImpl() {
    }
}

您需要使用@Module 将特定实现“绑定”到接口。

@Module
public class SomethingModule {
    @Provides
    Something something(SomethingImpl something) {
        return something;
    }
}

自 Dagger 2.4 以来的简写如下:

@Module
public abstract class SomethingModule {
    @Binds
    abstract Something something(SomethingImpl something);
}

4.)创建一个Injector 类来处理您的应用程序级组件(它取代了单体ObjectGraph

(注意:Rebuild Project 使用 APT 创建 DaggerApplicationComponent 构建器类)

public enum Injector {
    INSTANCE;

    ApplicationComponent applicationComponent;

    private Injector(){
    }

    static void initialize(CustomApplication customApplication) {
        ApplicationComponent applicationComponent = DaggerApplicationComponent.builder()
           .appContextModule(new AppContextModule(customApplication))
           .build();
        INSTANCE.applicationComponent = applicationComponent;
    }

    public static ApplicationComponent get() {
        return INSTANCE.applicationComponent;
    }
}

5.) 创建您的 CustomApplication

public class CustomApplication
        extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Injector.initialize(this);
    }
}

6.)CustomApplication 添加到您的AndroidManifest.xml

<application
    android:name=".CustomApplication"
    ...

7.)MainActivity中注入你的类

public class MainActivity
        extends AppCompatActivity {
    @Inject
    CustomApplication customApplication;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Injector.get().inject(this);
        //customApplication is injected from component
    }
}

8.)享受吧!

+1.)您可以为您的组件指定Scope,您可以使用它来创建活动级别范围的组件。子范围允许您提供只需要给定子范围的依赖项,而不是整个应用程序。通常,每个 Activity 都会通过此设置获得自己的模块。请注意,每个组件都存在一个范围提供程序,这意味着为了保留该活动的实例,组件本身必须在配置更改后继续存在。例如,它可以通过onRetainCustomNonConfigurationInstance() 或迫击炮范围存活。

有关子范围的更多信息,请查看the guide by Google。另请参阅 this site about provision methods 以及 component dependencies section) 和 here

要创建自定义范围,必须指定范围限定符注解:

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface YourCustomScope {
}

要创建子作用域,您需要在组件上指定作用域,并指定ApplicationComponent 作为其依赖项。显然,您还需要在模块提供程序方法上指定子范围。

@YourCustomScope
@Component(dependencies = {ApplicationComponent.class}, modules = {CustomScopeModule.class})
public interface YourCustomScopedComponent
        extends ApplicationComponent {
    CustomScopeClass customScopeClass();

    void inject(YourScopedClass scopedClass);
}

@Module
public class CustomScopeModule {
    @Provides
    @YourCustomScope
    public CustomScopeClass customScopeClass() {
        return new CustomScopeClassImpl();
    }
}

请注意,只有 一个 范围的组件可以指定为依赖项。可以把它想象成 Java 不支持多重继承。

+2.) 关于@Subcomponent:本质上,作用域@Subcomponent 可以替换组件依赖项;但您需要使用组件工厂方法,而不是使用注释处理器提供的构建器。

所以这个:

@Singleton
@Component
public interface ApplicationComponent {
}

@YourCustomScope
@Component(dependencies = {ApplicationComponent.class}, modules = {CustomScopeModule.class})
public interface YourCustomScopedComponent
        extends ApplicationComponent {
    CustomScopeClass customScopeClass();

    void inject(YourScopedClass scopedClass);
}

变成这样:

@Singleton
@Component
public interface ApplicationComponent {
    YourCustomScopedComponent newYourCustomScopedComponent(CustomScopeModule customScopeModule);
}

@Subcomponent(modules={CustomScopeModule.class})
@YourCustomScope
public interface YourCustomScopedComponent {
    CustomScopeClass customScopeClass();
}

还有这个:

DaggerYourCustomScopedComponent.builder()
      .applicationComponent(Injector.get())
      .customScopeModule(new CustomScopeModule())
      .build();

变成这样:

Injector.INSTANCE.newYourCustomScopedComponent(new CustomScopeModule());

+3.):请查看其他有关 Dagger2 的 Stack Overflow 问题,它们提供了很多信息。比如我当前的Dagger2结构是在this answer中指定的。

谢谢

感谢GithubTutsPlusJoe SteeleFroger MCSGoogle 的指导。

也为这个step by step migration guide I found after writing this post.

对于scope explanation,Kirill。

official documentation 中的更多信息。

【讨论】:

  • 我相信我们缺少 DaggerApplicationComponent 的实现
  • @ThanasisKapelonis DaggerApplicationComponent 由 APT 在构建时自动生成,但我会添加它。
  • 我只需要公开 Injector.initializeApplicationComponent 方法,因为我的 CustomApplication 超出了包范围,并且一切正常!谢谢!
  • 有点晚了,但也许下面的例子对任何人都有帮助:github.com/dawidgdanski/android-compass-apigithub.com/dawidgdanski/Bakery
  • 如果您收到“警告:使用不兼容的插件进行注释处理:android-apt。这可能会导致意外行为。在步骤 1 中,将 apt 'com.google.dagger:dagger-compiler:2.7' 更改为 annotationProcessor 'com.google.dagger:dagger-compiler:2.7' 并删除所有 apt 配置。详细信息可以在这里找到bitbucket.org/hvisser/android-apt/wiki/Migration
猜你喜欢
  • 2017-03-17
  • 2018-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多