Search code examples
androiddagger-2

Dagger2 is not generating classes in android


I am using Dagger2 in my android application, I have two component in my application. First is global for entire application and second is specific to activity instance.

NetComponent.java

@Singleton
@Component(modules = {AppModule.class, NetModule.class})
public interface NetComponent {
    void inject(AuthenticationActivity authenticationActivity);

    void inject(PaymentActivity paymentActivity);
}

ValidationComponent.java

@Singleton
@Component(modules = {ValidatorModule.class})
public interface ValidationComponent {
    void inject(Activity activity);
}

AppModule.java

@Module
public class AppModule {

    private Application application;

    public AppModule(Application application) {
        this.application = application;
    }

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

NetModule.java

@Module
public class NetModule {

    @Provides
    @Singleton
    SharedPreferences providesSharedPreferences(Application application) {
        return PreferenceManager.getDefaultSharedPreferences(application);
    }

    @Provides
    @Singleton
    Cache provideOkHttpCache(Application application) {
        int cacheSize = 10 * 1024 * 1024; // 10 MiB
        Cache cache = new Cache(application.getCacheDir(), cacheSize);
        return cache;
    }

    @Provides
    @Singleton
    Gson provideGson() {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
        return gsonBuilder.create();
    }

    @Provides
    @Singleton
    OkHttpClient provideOkHttpClient(Cache cache) {
        OkHttpClient okHttpClient = new OkHttpClient();
        okHttpClient.newBuilder()
                //.addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)
                .cache(cache)
                .build();
        return okHttpClient;
    }

    @Provides
    @Singleton
    @Named("authRetrofit")
    Retrofit provideAuthRetrofit(Gson gson, OkHttpClient okHttpClient) {
        Retrofit retrofit = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create(gson))
                .baseUrl(PAYMENT_SERVICE)
                .client(okHttpClient)
                .build();
        return retrofit;
    }

    @Provides
    @Singleton
    @Named("paymentRetrofit")
    Retrofit providePaymentRetrofit(Gson gson, OkHttpClient okHttpClient) {
        Retrofit retrofit = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create(gson))
                .baseUrl(LOGIN_SERVICE)
                .client(okHttpClient)
                .build();
        return retrofit;
    }


}

ValidatorModule.java

@Module
public class ValidatorModule {

    private final Activity activity;


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

    @Provides
    com.mobsandgeeks.saripaar.Validator providesValidator() {
        return new com.mobsandgeeks.saripaar.Validator(activity);
    }
}

AppApplication.java

public class AppApplication extends Application {

    private NetComponent mNetComponent;

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

        mNetComponent = DaggerNetComponent.builder()
                .appModule(new AppModule(this))
                .build();
    }

    public NetComponent getmNetComponent() {
        return mNetComponent;
    }

}

AuthenticationActivity.java

public class AuthenticationActivity extends BaseActivity implements View.OnClickListener, Validator.ValidationListener {

    @Inject
    Validator validator;

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

        ((AppApplication) getApplication()).getmNetComponent().inject(this);

        ValidationComponent validationComponent = DaggerValidationComponent.builder()
                .validatorModule(getValidatorModule())
                .build();

        validationComponent.inject(this);

        ...

        // apply click and validation listeners
        validator.setValidationListener(this);
    }
}   

protected ValidatorModule getValidatorModule() {
    return new ValidatorModule(this);
}

It is generating ValidationComponent but not DaggerNetComponent

enter image description here

Above are my module, components of my application. When I am compiling and building a application then I am getting below errors -

Error

Information:Gradle tasks [clean, :app:generateDebugSources, :app:mockableAndroidJar, :app:prepareDebugUnitTestDependencies, :app:generateDebugAndroidTestSources, :app:compileDebugSources, :app:compileDebugUnitTestSources, :app:compileDebugAndroidTestSources]
C:\Users\xyz\AndroidStudioProjects\ICICIAppathon\app\src\main\java\com\icici\iciciappathon\AppApplication.java
Error:(24, 48) error: cannot find symbol class DaggerNetComponent
C:\Users\xyz\AndroidStudioProjects\ICICIAppathon\app\src\main\java\com\icici\iciciappathon\login\AuthenticationActivity.java
Error:(35, 48) error: cannot find symbol class DaggerValidationComponent
Error:(39, 43) error: package com.icici.iciciappathon.databinding does not exist
C:\Users\xyz\AndroidStudioProjects\ICICIAppathon\app\src\main\java\com\icici\iciciappathon\checkout\PaymentActivity.java
Error:(28, 43) error: package com.icici.iciciappathon.databinding does not exist
C:\Users\xyz\AndroidStudioProjects\ICICIAppathon\app\src\main\java\com\icici\iciciappathon\dashboard\DashboardActivity.java
Error:(32, 43) error: package com.icici.iciciappathon.databinding does not exist
C:\Users\xyz\AndroidStudioProjects\ICICIAppathon\app\src\main\java\com\icici\iciciappathon\shopping\ScanBarcodeActivity.java
Error:(41, 43) error: package com.icici.iciciappathon.databinding does not exist
C:\Users\xyz\AndroidStudioProjects\ICICIAppathon\app\src\main\java\com\icici\iciciappathon\ui\GetStartedActivity.java
Error:(27, 43) error: package com.icici.iciciappathon.databinding does not exist
C:\Users\xyz\AndroidStudioProjects\ICICIAppathon\app\src\main\java\com\icici\iciciappathon\dagger\component\NetComponent.java
Error:(34, 10) error: com.mobsandgeeks.saripaar.Validator cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.
com.icici.iciciappathon.login.AuthenticationActivity.validator
[injected field of type: com.mobsandgeeks.saripaar.Validator validator]
Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
> Compilation failed; see the compiler error output for details.
Information:BUILD FAILED
Information:Total time: 11.373 secs
Information:9 errors
Information:0 warnings
Information:See complete output in console

Solution

  • The general statement here is that Dagger doesn't generate classes when it encounters an error, usually when you're doing something impossible in your binding graph. That's the case here.

    C:\Users\xyz\AndroidStudioProjects\ICICIAppathon\app\src\main\java\com\icici\iciciappathon\dagger\component\NetComponent.java Error:(34, 10) error: com.mobsandgeeks.saripaar.Validator cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method. com.icici.iciciappathon.login.AuthenticationActivity.validator [injected field of type: com.mobsandgeeks.saripaar.Validator validator]

    I agree with azizbekian that the problem is that you are trying to inject the Validator from within AuthenticationActivity, and telling NetComponent to try to do so without instructing it how to do so (in ValidationModule).

    In the code you've posted, there isn't a reason for NetComponent to inject AuthenticationActivity; there's nothing for it to provide. So you can delete these two lines and be done:

    // In NetComponent
    void inject(AuthenticationActivity authenticationActivity);
    
    // In AuthenticationActivity
    ((AppApplication) getApplication()).getmNetComponent().inject(this);
    

    However, it's also possible that you've simply not shown us the NetComponent dependencies you need in AuthenticationActivity. In that case, you're going to need to combine the object graphs, either through subcomponents or component dependencies. You'll still need to delete the two lines listed above, because under no circumstance does NetComponent have the dependencies it needs to inject AuthenticationActivity.

    Subcomponents

    To do so with subcomponents, simply make a builder on NetComponent through which you get your ValidationActivity. You're welcome to provide a scope, but you don't necessarily need one, and I think it might be confusing to add that into the mix. At that point, your ValidationComponent will have access to all the bindings in your NetComponent AND all of the bindings listed in ValidationComponent. This is effectively identical to azizbekian's answer, though notably this is through subcomponents, not component dependencies. I'll talk about component dependencies in a moment.

    // After removing the inject(AuthenticationActivity) call, add this to NetComponent
    ValidatorComponent validatorComponent(ValidatorModule validatorModule);
    
    // And call the implementation from AuthenticationActivity
    ((AppApplication) getApplication()).getmNetComponent()
        .validatorModule(new ValidatorModule(this))
        .inject();
    

    You'll also need to switch ValidationComponent from @Component to @Subcomponent, and remove the @Singleton instance (which makes sense because you're creating a new one for every AuthenticationActivity instance Android creates.

    Effectively, this turns NetComponent into a factory for ValidationComponent instances. Dagger generates the code for both at once, so ValidationComponent can access anything it needs from NetComponent automatically. However, this way it is impossible to generate code for each one separately, or to get a ValidationComponent without a NetComponent instance (except through including it in another component).

    Component dependencies

    Component dependencies work a little bit differently. They're freestanding components, so they can be generated separately and independently. Instead, you can pass in an interface (often but not necessarily another Dagger component like NetComponent), and every single no-argument method becomes an automatic provider into the other graph. This means, for example, that you could list specific dependencies available to ValidationComponent from NetComponent, add a dependencies={NetComponent.class} to your ValidationComponent definition, and then change your DaggerValidationComponent builder to call .netComponent(((AppApplication getApplication()).getmNetComponent()) to hook the exact instances up. (Similarly, you could do so with any implementation of that interface, whether Dagger generates it or not; this can help with testing.) You'll also need to remove the use of @Singleton, because Dagger would be very confused to have two separate components both claim to exist in singleton scope—one has to be created first, after all.

    You haven't listed out the exact dependencies you're consuming out of NetComponent, so I don't have example changes I can show. However, for cases like this, I would recommend using subcomponents anyway; the bindings are more automatic, you likely won't benefit from the loose coupling you'd get from component dependencies, and you're closer to the design of Dagger's official Android support package so it's easier to migrate to that later.