Sreehari
Sreehari

Reputation: 5655

Dagger 2 interdependency issue

In my application , I am trying to create Dagger 2 components for

  1. Context (To use in different classes) Which is already done
  2. AppUtil (requires context for network check method) Need to get this done!

I have created components and initiated from Application class.

    public class MyApp extends Application{

      private ContextComponent mContextComponent;
      private AppUtilComponent mAppUtilComponent;
      private NetworkComponent mNetworkComponent;

      @Override
        public void onCreate() {
            super.onCreate();
            mNetworkComponent = createNetworkComponent();
            mContextComponent =  createContextComponent();
            mAppUtilComponent = createAppUtilComponent();
        }

        private AppUtilComponent createAppUtilComponent() {
                return DaggerAppUtilComponent.builder().appUtilModule(new AppUtilModule(this)).build();
        }

        public AppUtilComponent getAppUtilComponent() {
           return mAppUtilComponent;
        }

        private NetworkComponent createNetworkComponent() {
             return DaggerNetworkComponent.builder().networkModule(new NetworkModule()).build();
        }

        public NetworkComponent getNetworkComponent() {
            return mNetworkComponent;
        }

        private ContextComponent createContextComponent() {

            return DaggerContextComponent.builder().contextModule(new ContextModule(this)).build();
        }

        public ContextComponent getContextComponent(){
            return mContextComponent;
        }
    }

The ContextModule class is as follows

@Module
public class ContextModule {

    private Context mContext;

    public ContextModule(Context context){
        mContext = context;
    }

    @Provides
    @Singleton
    Context getContext(){
        return mContext;
    }
}

Context component will be

@Singleton
@Component (modules = ContextModule.class)
public interface ContextComponent {

    void inject(AppUtils appUtils);

}

AppUtilModule is like

@Singleton
@Component (modules = AppUtilModule.class)

public interface AppUtilComponent {

   void inject(SplashActivity splashActivity);
}

With this AppUtilModule have modified as

@Module (includes = ContextModule.class)
public class AppUtilModule {

    private AppUtils mAppUtils;
    private Context context;

    @Inject
    public AppUtilModule(Context context) {
        this.context = context;
    }

    @Provides
    public AppUtils getAppUtil(){
        mAppUtils = new AppUtils(context);
        return mAppUtils;
    }
}

Now my AppUtils is getting context injected , which is perfectly alright.

public class AppUtils {

    public Context mContext;

    @Inject
    public AppUtils(Context _context){
        mContext = _context;
    }

    public boolean isNetworkConnected(){
     //Using mContext to determine Network
    }
    ... Other Utility methods which will be used throughout my application
}

But now how can I make AppUtil as a separate Dagger component (which is internally having Context as a Dependency) and inject in other classes?


EDIT 1: After making constructor injection of context into AppUtils and using AppUtil component in SplashActivity which already had Network component

After making the AppUtil as Dagger dependency, now Project is giving compile time error. Before this changes, NetworkProcessor in SplashActivity used to work fine, as it was only the dependency SplashActivity had. What is that I am missing/ doing wrong!

public class SplashActivity ....
 {

    @Inject
    public NetworkProcessor mNetworkProcessor;
    .....
    @Inject
    public AppUtils mAppUtils;

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

        MyApp.getInstance().getAppUtilComponent().inject(this);

        MyApp.getInstance().getNetworkComponent().inject(this);
    }

}

Error:

rror: [Dagger/MissingBinding] com.dev.myapp.networking.NetworkProcessor cannot be provided without an @Inject constructor or an @Provides-annotated method.
com.dev.myapp.networking.NetworkProcessor is injected at
com.dev.myapp.ui.activities.landing.SplashActivity.mNetworkProcessor
com.dev.myapp.ui.activities.landing.SplashActivity is injected at
com.dev.myapp.components.AppUtilComponent.inject(com.dev.myapp.ui.activities.landing.SplashActivity)

Upvotes: 1

Views: 168

Answers (1)

nosyjoe
nosyjoe

Reputation: 561

So a component can create multiple dependencies at once, which are usually grouped by lifetime scope (Application = whole app lifetime, Activity = activity lifetime).

So if your AppUtils class has the same scope as your ContextComponent, you can just use it to inject the AppUtils into classes that should use it, e.g. an activity:

public class MainActivity extens Activity {

    @Inject AppUtils appUtils;

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

        ((MyApp) getApplication()).getContextComponent().inject(this);
    }
}

after extending the ContextComponent definition to

@Singleton
@Component (modules = ContextModule.class)
public interface ContextComponent {

    void inject(AppUtils appUtils);

    void inject(MainActivity activity);
}

and you need to change AppUtils to use conastructor injection:

public class AppUtils {
    private Context mContext;

    @Inject 
    public AppUtils(Context context){
        this.mContext = context;
    }
}

After Question Edit 1

Dagger does not know how to create the NetworkProcessor-class, hence the compiler error. To make it work, you should change NetworkProcessor to have a constructor that is annotated with @Inject like you did with AppUtils (the second option would be creating a @Provides method in a dagger module, but the first solution is easier).

You did not supply the source code of NetworkProcessor so I'm assuming here it only needs a Context as a dependency.

Now that both AppUtils and NetworkProcessor have an injectible constructor that only has Context as an argument, Dagger can create the missing links by itself.

You don't need the NetworkComponent and the AppUtilComponent, just one Component, so delete them. Also delete the NetworkModule and the AppUtilModule. ContextComponent now is sufficient to inject all the dependencies into SplashActivity:

public class SplashActivity .... {

    @Inject public NetworkProcessor mNetworkProcessor;
    .....
    @Inject public AppUtils mAppUtils;

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

        MyApp.getInstance().getContextComponent().inject(this);
    }
    //...
}

This works because Dagger can create the wiring code itself, since it knows how to instantiate both AppUtils and NetworkProcessor.

Upvotes: 1

Related Questions