sector11 sector11 - 1 month ago 34
Java Question

Dagger 2 not injecting in interface type

I an trying to inject a field in an interface type which is implemented by a class.

Here is what i have done so far.

These are view interface:

public interface PostView extends View, ListView<Post>, EmptyView<String> {
}
public interface View {

public void showProgressIndicator();

public void hideProgressIndicator();

public void onSuccess();

public void onFailure();

public void onFailure(String message);
}

public interface ListView<E> {

public void onListItems(List<E> items,
int pageNum,
int pageSize,
boolean next);

}

public interface EmptyView<E> {

public void onEmpty(E e);

public void onEmpty(String message);
}


Components:

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

Api provideApi();

}

@UserScope
@Component(dependencies = ApiComponent.class, modules = PostModule.class)
public interface PostComponent {

PostPresenter providePostPresenter();

void inject(NetworkTest networkTest);
}


Modules:

@Module
public class ApiModule {

private static final Logger logger = Logger.getLogger(ApiModule.class.getSimpleName());

private final String baseUrl;

public ApiModule(String baseUrl) {
this.baseUrl = baseUrl;
}

@Provides
@Singleton
boolean provideIsLoggerEnabled() {
logger.info("proviedIsLoggerEnabled()");
return true;
}

@Provides
@Singleton
OkHttpClient provideOkHttpClient(boolean logEnabled) {
logger.info(" provideOkHttpClient(logEnabled)");
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
Interceptor requestInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
return chain.proceed(chain.request());
}
};
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.addInterceptor(requestInterceptor)
.addNetworkInterceptor(interceptor);
return builder.build();
}

@Provides
@Singleton
Api provideApi(OkHttpClient okHttpClient) {
logger.info("provideApi");
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit.create(Api.class);
}

}

@Module
public class PostModule {

private static final Logger logger = Logger.getLogger(PostModule.class.getSimpleName());
private final PostView postView;

public PostModule(PostView postView) {
this.postView = postView;
}

@Provides
@UserScope
PostService providePostService(Api api) {
logger.info("Provider post with api now");
return new PostService(api);
}

@Provides
@UserScope
PostPresenter providePostPresenter(PostService service) {
logger.info("Providing presenter with service now");
return new PostPresenter(postView, service);
}
}


Presenter:

public class PostPresenter extends AbstractPresenter {

private static final Logger logger = Logger.getLogger(PostPresenter.class.getSimpleName());
private PostView postView;
private PostService postService;

public PostPresenter(PostView postView, PostService postService) {
this.postView = postView;
this.postService = postService;
}

@Override
protected View getView() {
logger.info("Getting view");
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}

public void getPosts() {
logger.info("Getting posts ");
Call<List<Post>> posts = this.postService.getPosts();
postView.showProgressIndicator();
posts.enqueue(new Callback<List<Post>>() {

@Override
public void onResponse(Call<List<Post>> call, Response<List<Post>> rspns) {
postView.onListItems(rspns.body(), 1, 25, true);
postView.hideProgressIndicator();
postView.onSuccess();
}

@Override
public void onFailure(Call<List<Post>> call, Throwable thrwbl) {
onApiCallError(thrwbl);
postView.hideProgressIndicator();
}
});
}
}

public abstract class AbstractPresenter {

private static final Logger logger = Logger.getLogger(AbstractPresenter.class.getSimpleName());

protected abstract View getView();

/*
* General indication whether api call stated or not.
*/
protected void onApiCallStart() {
logger.info("Api call started");
View v = getView();
if (v != null) {
v.showProgressIndicator();
}
}

protected void onApiCallEnd() {
logger.info("Api call finished");
View v = getView();
if (v != null) {
v.hideProgressIndicator();
}
}

/*
* General error handling
*/
protected void onApiCallError(Throwable e) {
logger.info("Api call terminated with error");
View v = getView();
if (v != null && e != null) {
v.onFailure(e.getMessage());
}
}
}


NetworkTest:

public class NetworkTest implements PostView {

private static final Logger logger = Logger.getLogger(NetworkTest.class.getSimpleName());

private PostComponent component;
@Inject
PostPresenter presenter;

public NetworkTest(ApiComponent apiComponent) {
component = DaggerPostComponent.builder()
.apiComponent(apiComponent)
.postModule(new PostModule(this))
.build();
}

public void init() {
component.inject(this);
}

void showPosts() {
if (presenter != null) {
logger.info("Hurray it worked");
presenter.getPosts();
} else {
logger.warning("Alas it failed");
}
}

@Override
public void showProgressIndicator() {
logger.info("Show progress indicator here");
}

@Override
public void hideProgressIndicator() {
logger.info("Hide progress indicator here");
}

@Override
public void onSuccess() {
logger.info("Api calls successfull");
System.exit(0);
}

@Override
public void onFailure() {
logger.warning("Api call failure");
System.exit(0);
}

@Override
public void onFailure(String message) {
logger.warning(message);
System.exit(0);
}

@Override
public void onListItems(List<Post> items, int pageNum, int pageSize, boolean next) {
logger.info("List received is: " + new Gson().toJson(items));
}

@Override
public void onEmpty(String e) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}

public static void main(String[] args) {
ApiComponent apiComponent = DaggerApiComponent.builder()
.apiModule(new ApiModule("https://jsonplaceholder.typicode.com/"))
.build();

NetworkTest networkTest = new NetworkTest(apiComponent);
networkTest.init();
networkTest.showPosts();
}

}


My Problem is when i try to use

void inject(NetworkTest networkTest); //It works
void inject(PostView postView); //Doesn't work


I want that
PostPresenter
should get Injected in any class who is implementing
PostView
.

But when i do this
@Inject
field return null.
Does anyone have any clue about this.

Answer

NetworkTest has an @Inject field that Dagger can detect at compile time. PostView does not. Dagger 2 can perform injection on both NetworkTest and PostView, but because PostView has no @Inject-annotated methods, there's nothing for Dagger 2 to inject.

If you want to express that arbitrary implementors of PostView can be injected, you should add an @Inject-annotated initialize or injectPresenter method (et al); otherwise, just get/inject concrete types from Dagger so all of their dependencies can be injected at once.


As stated in the Dagger 2 user's guide (emphasis mine), "Dagger is a fully static, compile-time dependency injection framework for both Java and Android." Unlike with Guice or Spring, Dagger 2 performs no runtime reflection, so (for instance) a generated Component method inject(PostView) can only inject fields and methods defined on PostView or its supertypes, and not anything defined on a subtype.

In a general sense, I don't think it's reasonable for you to expect (or constrain) your PostView interface implementors to require the injection of a Presenter a certain way; if you want to make an explicit presenter-providing lifecycle method, you can do that on PostView without involving Dagger, and that way your classes can be more specific with their dependencies rather than mixing the necessary deps with the "unnecessary-but-included" deps you prescribe.

Comments