试图用Dagger2在Android上安装依赖注入 [英] Trying to get my head around dependency injection on Android with Dagger2

查看:170
本文介绍了试图用Dagger2在Android上安装依赖注入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码:

SomeClass

public class SomeClass {

    @Inject
    @Named("special")
    OkHttpClient mOkHttpClient;

    public SomeClass(Activity activity) {
        ((MyApplication) activity.getApplication()).getApplicationComponent().inject(this);
    }

}

ApplicationModule < >

ApplicationModule

@Module
public class ApplicationModule {

    private final Application mApplication;

    public ApplicationModule(Application application) {
        mApplication = Preconditions.checkNotNull(application);
    }

    @Provides
    @Singleton
    Application providesApplication() {
        return mApplication;
    }

    @Provides
    @Singleton
    SharedPreferences provideCustomSharedPreferences() {
        return mApplication.getSharedPreferences("my_custom_file", Context.MODE_PRIVATE);
    }

}

ApplicationComponent

@Singleton
@Component(modules = {
    ApplicationModule.class,
    NetworkModule.class
})
public interface ApplicationComponent {

    void inject(SomeClass someClass);

}

MainActivity

public class MainActivity extends AppCompatActivity {

    @Inject
    SharedPreferences mSharedPreferences;

    @Inject
    @Named("default")
    OkHttpClient mOkHttpClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ((MyApplication) getApplication()).getActivityComponent().inject(this);
    }

}

网络模块

@Module
public abstract class NetworkModule {

    private static final int DEFAULT_CACHE_SIZE = 10 * 1024 * 1024; // 10 Mib

    @Provides
    @Singleton
    static Cache provideOkHttpCache(Application application) {
        return new Cache(application.getCacheDir(), DEFAULT_CACHE_SIZE);
    }

    @Provides
    @Singleton
    @Named("default")
    static OkHttpClient provideDefaultOkHttpClient(Cache cache) {
        OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder();

        if (BuildConfig.DEBUG) {
            // Add logging interceptor here...
        }

        return okHttpClient.cache(cache).build();
    }

    @Provides
    @Singleton
    @Named("special")
    static OkHttpClient provideSpecialOkHttpClient(@Named("default") OkHttpClient okHttpClient) {
        return okHttpClient.newBuilder()
            // Add .certificatePinner() here...
            .build();
    }

}

ActivityComponent

@Singleton
@Component(modules = {
    ApplicationModule.class,
    NetworkModule.class
})
public interface ActivityComponent {

    void inject(MainActivity mainActivity);

}

MyApplication

public class MyApplication extends Application {

    private ApplicationComponent mApplicationComponent;
    private ActivityComponent mActivityComponent;

    @Override
    public void onCreate() {
        super.onCreate();
    }

    public ApplicationComponent getApplicationComponent() {
        if (mApplicationComponent == null) {
            mApplicationComponent = DaggerApplicationComponent.builder()
                .applicationModule(new ApplicationModule(this))
                .build();
        }

        return mApplicationComponent;
    }

    public ActivityComponent getActivityComponent() {
        if (mActivityComponent == null) {
            mActivityComponent = DaggerActivityComponent.builder()
                .applicationModule(new ApplicationModule(this))
                .build();
        }

        return mActivityComponent;
    }

}

但是有一些事情我不喜欢,我知道他们可以改进,范围,组件依赖或子组件。我也可以使用自定义限定符,而不是 @Named ,但我知道已经有一个。

But there are a few things that I don't like and I know they can be improved, with scopes, component dependencies or subcomponents. I can also use custom qualifiers instead of @Named, but I know that one already.

这是我想要的要做...

Here's what I want to do...


  1. 我想要将 provideSpecialOkHttpClient 的范围绑定到 SomeClass 生命周期。我知道我需要这样的范围...但是这个特殊的 OkHttpClient 取决于默认的 OkHttpClient 实例,它本身取决于一个 okhttp3.Cache 实例。最后两个实例是 @Singleton ,因为它们可以在任何地方使用。只有特殊的 OkHttpClient 实例与 SomeClass 绑定,因为它是唯一可以使用它的地方。如何用范围完成这个?我所有的尝试导致我错误,因为我正在使用自定义范围,例如 @SomeClassScope @Singleton ApplicationComponent 它本身是一个 @Singleton 。简而言之,如何将一些单例依赖关系绑定到应用程序生命周期,同时将其他依赖关系绑定到其他生命周期(无论是活动还是 SomeClass 在我的示例中)当它们依赖单例依赖关系时

  1. I want the scope of provideSpecialOkHttpClient to be tied to SomeClass lifecycle. I know I need scopes for this... But this special OkHttpClient depends on the default OkHttpClient instance which itself depends on a okhttp3.Cache instance. These last two instances are @Singleton because they can be used everywhere. Only the special OkHttpClient instance exists tied to SomeClass because it's the only place where it will be used. How can I accomplish this with scopes? All my attempts led me to errors because I was using a custom scope, like @SomeClassScope, and @Singleton within the ApplicationComponent which itself is a @Singleton. In a nutshell, how can I have some singleton dependencies tied to the application lifecycle while having other dependencies tied to some other lifecycle (be it an Activity or SomeClass like in my example) when they are dependent of singleton dependencies?

如你所见我正在调用新的ApplicationModule(this)两次,击败 @Singleton 注释组件的目的,并提供方法。如何使 ActivityComponent 依赖于 ApplicationComponent ,以便我只需要实例化 ApplicationModule ApplicationComponent 中一次?子?组件依赖我不能用任何方法使这项工作...

As you can see I'm calling new ApplicationModule(this) twice, defeating the purpose of @Singleton annotated components and provide methods. How can I make the ActivityComponent dependent on the ApplicationComponent so that I only have to instantiate ApplicationModule once in the ApplicationComponent? Subcomponents? Component dependencies? I couldn't make this work with any approach...

我很难把握所有匕首方面,如果您可以在回答时提供一些代码示例,那么真的可以帮助我很多的可视化,并且了解每一件事如何联系在一起。

I'm having a hard time grasping all Dagger aspects so if you could provide some code examples when answering, that would really help me a lot visualize and understand how everything ties together.

推荐答案

首先让我们来看看你到目前为止。 ActivityComponent 有点奇怪。 @Singleton 代表应用范围的单身人士。由于 ActivityComponent 注入了具有活动范围而不是整个应用程序的成员,因此我们可能需要一个新的范围,例如:

First let's have a look at what you have so far. The ActivityComponent is a little bit strange. @Singleton represents app-scoped singleton. Since ActivityComponent injects members that have the scope of an Activity and not the entire app, we probably need a new scope for that Component like this:

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

现在我们可以更改该组件:

Now we can change that component:

@PerActivity
@Component(dependencies = { AppComponent.class 
})
public interface ActivityComponent {

    void inject(MainActivity mainActivity);

}

请注意,我们现在将其作为 AppComponent 。我们将稍微重构 AppComponent ,以发布它们到依赖组件的绑定。

Notice we have now made it a dependent component of AppComponent. We will have to refactor AppComponent slightly to publish it's bindings to the dependent components.

理由:我们希望 ActivityComponent 能够使用 OkHttpClient 绑定在 NetworkModule 中。但是, NetworkModule 不是 ActivityComponent 的模块 - 它是父 AppComponent 。依赖组件不会自动继承父母的所有绑定。为了使 ActivityComponent 使用 OkHttpClient 作为特殊 OkHttpClient 它需要由父组件公开。您可以通过在接口中创建一个方法来显示一个绑定到一个依赖组件,该类型是您要公开的。没有必要公开所有绑定,只是您将在依赖组件中使用的绑定。

Rationale: we want the ActivityComponent to be able to use the OkHttpClient that is bound in NetworkModule. However, NetworkModule is not a module of ActivityComponent - it is part of the parent AppComponent. Dependent components do not "automatically" inherit all of the bindings from their parents. In order for ActivityComponent to use the OkHttpClient as a dependency for the "special" OkHttpClient it needs to be exposed by the parent component. You can expose a binding to a dependent component by creating a method in the interface with the type you wish to expose. It's not necessary to expose all the bindings, just the very ones that you will use in the dependent components.

@Singleton
@Component(modules = {
    ApplicationModule.class,
    NetworkModule.class
})
public interface ApplicationComponent {

    //injection sites

    void inject(SomeClass someClass);

    //bindings published to subcomponents

    OkHttpClient okHttpClient(); //default

}

现在提取一个特殊 OkHttpClient

@Module
public class SpecialNetworkModule {

    @Provides
    @PerActivity
    @Named("special")
    static OkHttpClient provideSpecialOkHttpClient(@Named("default") OkHttpClient okHttpClient) {
        return okHttpClient.newBuilder()
        // Add .certificatePinner() here...
        .build();
    }
}

并撰写 ActivityComponent 同一个模块:

@PerActivity
@Component(modules = { SpecialNetworkModule.class }
           dependencies = { AppComponent.class })
public interface ActivityComponent {

    void inject(MainActivity mainActivity);

}

SomeClass 基本上在你的活动范围内,所以你可以通过这样做来重构它,以便注入你的活动:

SomeClass is basically in the scope of your activity, so you can refactor it to get injected inside your activity by doing this:

public class SomeClass {

    private final Activity activity;
    private final OkHttpClient mOkHttpClient;

    @Inject
    public SomeClass(Activity activity, @Named("special") OKHttpClient client) {
        this.activity = activity;
        this.mOkHttpClient = client;
    }
}

现在使 SomeClass 一个字段 MainActivity (我假设你在那里使用它,因为它依赖于Activity,它是你提供的唯一的Activity代码)

Now make SomeClass a field of MainActivity (I am assuming you are using it there because it has a dependency on Activity and it is the only Activity code you have provided):

@Inject SomeClass someClass

@Override
public void onCreate() {

并确保您的 ActivityComponent 提供活动。为此,您将需要一个新的模块:

And make sure your ActivityComponent provides Activity. To do this you will need a new Module:

@Module
@PerActivity
public class ActivityModule {

    private final Activity activity;

    public ActivityModule(Activity activity) {
        this.activity = activity;
    }

    @Provides
    @PerActivity
    Activity activity() {
        return this.activity;
    }

    @Provides
    @PerActivity
    Context context() {
         return this.activity;
    }
}

并使用此模块撰写您的ActivityComponent:

And compose your ActivityComponent with this module:

modules = { SpecialNetworkModule.class, ActivityModule.class }

现在,组件的消耗需要一点点的工作。在你的应用程序中摆脱 public ActivityComponent getActivityComponent()。 ActivityComponent应该在内部,以及一个Activity中生成,以便他们正确地跟踪范围和生命周期。

Now the consumption of the components needs a little bit of work. Get rid of the public ActivityComponent getActivityComponent() inside your Application. ActivityComponents should be generated inside, well, an Activity in order for them to track the scopes and lifecycles correctly.

所以在Activity中消耗组件应该看起来像这样:

So consuming the component inside your Activity should look something like this:

@Inject SomeClass someClass;

@Override
public void onCreate() {
     AppComponent appComponent = ((((MyApplication) getApplication()).getActivityComponent());
     DaggerActivityComponent.builder()
         .appComponent(appComponent)
         .activityModule(new ActivityModule(this))
         .build()
         .inject(this);
}

最后,明确回答你的两个问题:

Finally, to answer your two questions explicitly:

简而言之,如果依赖于单例,我可以将某些单例依赖关系绑定到应用程序生命周期,同时将其他依赖关系绑定到其他生命周期(无论是在我的示例中是Activity还是SomeClass)依赖关系?

In a nutshell, how can I have some singleton dependencies tied to the application lifecycle while having other dependencies tied to some other lifecycle (be it an Activity or SomeClass like in my example) when they are dependent of singleton dependencies?

通过创建自定义范围( @PerActivity ),跟踪该范围( ActivityComponent ),并使用单独的模块( SpecialNetworkModule ActivityModule )更窄范围依赖。在这样做时,您将需要更宽范围的范围更窄的组件之间的某种形式的关系。这有助于您的下一个问题:

By creating a custom scope (@PerActivity), a component that tracks that scope (ActivityComponent), and using separate modules (SpecialNetworkModule, ActivityModule) for the narrower scoped dependencies. In doing this, you will need some form of relationship between the wider-scoped and narrower-scoped components. Which leads well to your next question:


如何使ActivityComponent取决于ApplicationComponent,以便我只需要将ApplicationModule一次实例化ApplicationComponent?子?组件依赖我不能使用任何方法使这项工作...

How can I make the ActivityComponent dependent on the ApplicationComponent so that I only have to instantiate ApplicationModule once in the ApplicationComponent? Subcomponents? Component dependencies? I couldn't make this work with any approach...

如上例所示,使用依赖组件(子组件也是考虑的可能性)。在这样做时,确保更广泛的组件明确地将其绑定发布到其依赖组件。

As in the above example, using dependent components (subcomponents are also a possibility to consider). In doing this, make sure that wider-scoped components explicitly publish their bindings to their dependent components.

这篇关于试图用Dagger2在Android上安装依赖注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆