如何在Android项目中从头开始设置DAGGER依赖注入?

Epi*_*rce 96 android dependency-injection dagger dagger-2

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

我想在我的Android项目中使用Dagger,但我发现它令人困惑.

编辑:Dagger2也从2015年04月15日开始,它更令人困惑!

[这个问题是一个"存根",我在我的答案中添加了更多关于Dagger1的知识,并且更多地了解了Dagger2.这个问题更多的是指导而不是"问题".

Epi*_*rce 183

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

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

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

public interface AppContextComponent {
    CustomApplication application(); //provision method
    Context applicationContext(); //provision method
    LocationManager locationManager(); //provision method
}
Run Code Online (Sandbox Code Playgroud)

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

注意::您需要在模块的注释方法上提供@Scope注释(如@Singleton@ActivityScope)@Provides以在生成的组件中获取范围提供者,否则它将是未编组的,并且每次注入时都会获得一个新实例.

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

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

3.3.)对于您可以自己通过构造函数创建的依赖项,并且不希望使用@Module(例如,您使用构造flavor来改变实现的类型)来重新定义,您可以使用带@Inject注释的构造函数.

public class Something {
    OtherThing otherThing;

    @Inject
    public Something(OtherThing otherThing) {
        this.otherThing = otherThing;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

public class Something {
    @Inject
    OtherThing otherThing;

    @Inject
    public Something() {
    }
}
Run Code Online (Sandbox Code Playgroud)

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

一个@Singleton范围的@Inject构造函数的类会在可见@Singleton范围的部件.

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

    @Inject
    public Something(OtherThing otherThing) {
        this.otherThing = otherThing;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

public interface Something {
    void doSomething();
}

@Singleton
public class SomethingImpl {
    @Inject
    AnotherThing anotherThing;

    @Inject
    public SomethingImpl() {
    }
}
Run Code Online (Sandbox Code Playgroud)

您需要使用a将特定实现"绑定"到接口@Module.

@Module
public class SomethingModule {
    @Provides
    Something something(SomethingImpl something) {
        return something;
    }
}
Run Code Online (Sandbox Code Playgroud)

自Dagger 2.4以来的一个简写如下:

@Module
public abstract class SomethingModule {
    @Binds
    abstract Something something(SomethingImpl something);
}
Run Code Online (Sandbox Code Playgroud)

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

(注意:使用APT Rebuild Project创建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;
    }
}
Run Code Online (Sandbox Code Playgroud)

5.)创建你的CustomApplication课程

public class CustomApplication
        extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Injector.initialize(this);
    }
}
Run Code Online (Sandbox Code Playgroud)

6.)加入CustomApplication你的AndroidManifest.xml.

<application
    android:name=".CustomApplication"
    ...
Run Code Online (Sandbox Code Playgroud)

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

8.)享受!

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

有关subscoping的更多信息,请查看Google的指南.另请参阅此站点有关提供方法以及组件依赖性部分)和此处.

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

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface YourCustomScope {
}
Run Code Online (Sandbox Code Playgroud)

要创建子范围,您需要在组件上指定范围,并指定ApplicationComponent其依赖关系.显然,您还需要在模块提供程序方法上指定子范围.

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

    void inject(YourScopedClass scopedClass);
}
Run Code Online (Sandbox Code Playgroud)

@Module
public class CustomScopeModule {
    @Provides
    @YourCustomScope
    public CustomScopeClass customScopeClass() {
        return new CustomScopeClassImpl();
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,只能将一个范围组件指定为依赖项.可以想象它与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);
}
Run Code Online (Sandbox Code Playgroud)

变成这样:

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

@Subcomponent(modules={CustomScopeModule.class})
@YourCustomScope
public interface YourCustomScopedComponent {
    CustomScopeClass customScopeClass();
}
Run Code Online (Sandbox Code Playgroud)

还有这个:

DaggerYourCustomScopedComponent.builder()
      .applicationComponent(Injector.get())
      .customScopeModule(new CustomScopeModule())
      .build();
Run Code Online (Sandbox Code Playgroud)

变成这样:

Injector.INSTANCE.newYourCustomScopedComponent(new CustomScopeModule());
Run Code Online (Sandbox Code Playgroud)

+3.):请检查有关Dagger2的其他Stack Overflow问题,它们提供了大量信息.例如,我的当前Dagger2结构在此答案中指定.

谢谢

感谢Github,TutsPlus,Joe Steele,Froger MCSGoogle的指南.

此外,我在撰写此帖后发现了一步一步的迁移指南.

而对于Kirill的范围说明.

官方文档中的更多信息.

  • 有点晚但可能以下示例将帮助任何人:https://github.com/dawidgdanski/android-compass-api https://github.com/dawidgdanski/Bakery (2认同)

Epi*_*rce 11

匕首指南1.x:

步骤如下:

1.)为依赖项添加Daggerbuild.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'
Run Code Online (Sandbox Code Playgroud)

另外,添加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'
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

@Module(
    includes = {
        UtilsModule.class,
        NetworkingModule.class
    },
    injects = {
        MainActivity.class
    }
)
public class RootModule
{
}
Run Code Online (Sandbox Code Playgroud)

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

@Module(
    includes = {
        SerializerModule.class,
        CertUtilModule.class
    }
)
public class UtilsModule
{
}
Run Code Online (Sandbox Code Playgroud)

5.)创建接收依赖关系的叶子模块作为构造函数参数.在我的情况下,没有循环依赖,所以我不知道Dagger是否可以解决这个问题,但我发现它不太可能.构造函数参数也必须在Module by Dagger中提供,如果您指定,complete = false那么它也可以在其他模块中.

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

6.)扩展Application并初始化Injector.

@Override
public void onCreate()
{
    super.onCreate();
    Injector.INSTANCE.init(new RootModule());
}
Run Code Online (Sandbox Code Playgroud)

7.)在你MainActivityonCreate()方法中,调用Injector .

@Override
protected void onCreate(Bundle savedInstanceState)
{
    Injector.INSTANCE.inject(this);
    super.onCreate(savedInstanceState);
    ...
Run Code Online (Sandbox Code Playgroud)

8.)使用@Inject你的MainActivity.

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

...
Run Code Online (Sandbox Code Playgroud)

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

  • `dagger-compiler`的范围应该是'提供',否则它将被包含在应用程序中,并且它是在GPL许可下. (3认同)