Son
Son

Reputation: 21

How to inject different dependencies for parent/child activities from multiple independent components using Dagger 2?

I'm relatively new to Dagger 2, and I kept running into compiler error from Dagger 2 says missing @Provide or @Produces. What I'm trying to do is inject common components in base class, then child class also has its own component. I checked out several examples online and saw people using sub-component; but I still don't get why can we just have multiple independent components for each class? I don't see why my ImageComponent has to be sub-component of ApiComponent, it just doesn't make any sense. So I'm wondering what's the right approach regarding this issue, or is there any alternative approach that I'm not aware of? Thanks.

Api Component

public interface UserApi {

    int getUser();
}

public interface SearchApi {

    int search();
}

@Singleton
@Module
public class ApiModule {

    public ApiModule() {
    }

    @Singleton
    @Provides
    public SearchApi provideSearchApi() {
        return new SearchApi() {
            @Override
            public int search() {
                return 0;
            }
        };
    }

    @Singleton
    @Provides
    public UserApi provideUserApi() {
        return new UserApi() {
            @Override
            public int getUser() {
                return 0;
            }
        };
    }
}

@Singleton
@Component(modules = {
    ApiModule.class
})
public interface ApiComponent {

    void inject(BaseActivity activity);
}

Image Component

public interface ImageLoader {

    boolean load(String url);
}

@ActivityScope
@Module
public class ImageModule {

    @ActivityScope
    @Provides
    public ImageLoader provideImageResource() {
        return new ImageLoader() {
            @Override
            public boolean load(String url) {
                return false;
            }
        };
    }
}

@ActivityScope
@Component(
    modules = {
        ImageModule.class
    }
)
public interface ImageComponent {
    // Dagger doesn't allow this? Not sure why?
    void inject(MainActivity activity);
}

Activity and Components Singleton

public class Components {

    private ApiComponent apiComponent;

    private ImageComponent imageComponent;

    private static Components singleton;

    public static void initialize(Context context) {
        if (singleton != null) {
            throw new RuntimeException("Attempted to initialize components twice");
        }
        singleton = new Components(context);
    }

    public static Components get() {
        return singleton;
    }

    private Components(Context context) {
        apiComponent = ApiComponent
            .builder()
            .dataModule(new ApiModule())
            .build();

        // Can't generate image component yet
    }

    public ApiComponent api() {
        return apiComponent;
    }

    public ImageComponent image() {
        return imageComponent;
    }
}
public abstract class BaseActivity extends AppCompatActivity {

    @Inject
    protected UserApi userApi;

    @Inject
    protected SearchApi searchApi;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Components.get().data().inject(this);
    }
}

public class MainActivity extends BaseActivity {

    @Inject
    protected ImageLoader imageLoader;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

stacktrace

Error:(15, 10) error: android.com.dagger.data.UserApi cannot be provided without an @Provides- or @Produces-annotated method.
android.com.dagger.BaseActivity.userApi
[injected field of type: android.com.dagger.data.UserApi userApi]

Upvotes: 2

Views: 1154

Answers (1)

Bhargav
Bhargav

Reputation: 8277

base classes are not sufficient as injection targets. Dagger 2 relies on strongly typed classes, so you must specify explicitly which ones should be defined

You can't do this Components.get().data().inject(this); this (i.e BaseActivity is abstract)

Upvotes: 4

Related Questions