Dagger 2 - injecting non Android classes

2019-02-15 08:05发布

问题:

I'm implimenting Dagger 2 in my Android app. I have it setup in the following way:

AppComponent.java

@Singleton
@Component(modules = {
  AndroidInjectionModule.class,
  AndroidSupportInjectionModule.class,
  ActivityBuilder.class,
  AppModule.class,
  DataBaseDaoModule.class
})

public interface AppComponent {
  @Component.Builder
  interface Builder {
    @BindsInstance
    Builder application(Application aApplication);

    AppComponent build();
  }

  Application application();
  void inject(MyApplication aApplication);
}

AppInjector.java

ublic class AppInjector {

  public static void init(MyApplication aApplication) {

    //Initialize dagger and inject the aApplication
    DaggerAppComponent.builder().application(aApplication).build().inject(aApplication);

    aApplication.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
      @Override
      public void onActivityCreated(Activity aActivity, Bundle aBundle) {
        handleActivity(aActivity);
      }

      @Override
      public void onActivityStarted(Activity aActivity) {
      }

      @Override
      public void onActivityResumed(Activity aActivity) {
      }

      @Override
      public void onActivityPaused(Activity aActivity) {
      }

      @Override
      public void onActivityStopped(Activity aActivity) {
      }

      @Override
      public void onActivitySaveInstanceState(Activity aActivity, Bundle aBundle) {
      }

      @Override
      public void onActivityDestroyed(Activity aActivity) {
      }
    });
  }

  private static void handleActivity(Activity aActivity) {
    if (aActivity instanceof HasActivityInjector) {
      AndroidInjection.inject(aActivity);
      Timber.d("injected Activity");
    }
    if (aActivity instanceof FragmentActivity) {
      ((FragmentActivity) aActivity).getSupportFragmentManager()
        .registerFragmentLifecycleCallbacks(
          new FragmentManager.FragmentLifecycleCallbacks() {
            @Override
            public void onFragmentCreated(FragmentManager fm, Fragment f,
                                          Bundle savedInstanceState) {
              if (f instanceof Injectable) {
                Timber.d("injected Fragment");
                AndroidSupportInjection.inject(f);
              }
            }
          }, true);
    }
  }
}

AppModule.java

Module(includes = ViewModelModule.class)
class AppModule {

  @Singleton
  @Provides
  ApiService providesApiService(OkHttpClient aClient, MyInterceptor aInterceptor) {

    //Build a Retrofit object here
  }

  @Singleton
  @Provides
  OkHttpClient providesOkHTTPClient(MyInterceptor aInterceptor) {
   //Setup OKHTTP here
  }
}

And finally in MyApplication.Java in the onCreate method I just call the AppInjector like so: AppInjector.init(this);

All of this works and anything I put in my AppComponent's moduels, I can inject into Activities, Fragments and ViewModels.

However, I have cases where I would need a utility class, that depends on Application, for contex - and I use the utility class in various places. Or I will have a Manager class, that depends on Application, or needs something from AppModule. However, since I use these classes outside of Activities, Fragments and ViewModels I cannot just inject. How would I provide my utility classes with their dependencies and any other type of class - like a manager class?

My first thought was to create a UtilityComponent and a ManagerCompoent of sorts, however I have no idea how I would get them to work with anything in AppModuel or through my AppComponent.

回答1:

Please don't just use component.inject(myObject) for everything. Always prefer constructor injection or provide it from a module where you can do additional setup steps. .inject(myObject) is intended for Framework components where you don't have access to the constructor.

My first thought was to create a UtilityComponent and a ManagerCompoent of sorts, however I have no idea how I would get them to work with anything in AppModuel or through my AppComponent.

You don't need a separate component for that. See below.

However, since I use these classes outside of Activities, Fragments and ViewModels I cannot just inject.

That has nothing to do with injection. You're talking about scopes, and it sound like your utilities are a @Singleton. Your AppComponent is a @Singleton scoped component, hence it can be used to provide your utils, too.

However, I have cases where I would need a utility class, that depends on Application, for context

If they are part of the @Singleton component, which has access to your Application, they can also be provided anywhere else. No need for more components or anything. Just declare your dependencies and don't overthink it.


Just declare your util, annotate it with @Singleton and mark the constructor with @Inject for constructor injection. @Singleton ensures that it will be provided by your AppComponent and can access the Application on which it depends.

@Singleton public class MyUtil {

  private Application application;

  @Inject public MyUtil(Application application) {
    this.application = application;
  }

}

And then you can just inject it in your Activities, Fragments, or even into other Utilities....

@Singleton public class MyUtilWrapper {

  private MyUtil myUtil;

  @Inject public MyUtilWrapper(MyUtil myUtil) {
    this.myUtil = myUtil;
  }

}

And you can inject either or both into your activity or fragment...

@Inject MyUtil myUtil;
@Inject MyUtilWrapper myUtilWrapper;

void onCreate(..) {
  AndroidInjection.inject(this);
}

You do not need any modules, provides methods, or components to provide simple classes. Just make sure to add the right scope!