Ahmed
Ahmed

Reputation: 113

Inject activity using dagger

I try to create sample application with dagger 2 using mvp & RXAndroid, every thing work correctly but I cannot able to inject Activity the following is my AppComponent

@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
    void inject(App app);

    void inject(MainActivity activity);

    void inject(ResponseService service);

    void inject(MainPresenter presenter);
}

and the following is my Module

@Module
public class AppModule {

    private App app;
    public AppModule(App app) {
        this.app = app;
    }
    private static final String API_ENDPOINT = "url here";

    @Provides
    @Singleton
    public ApiService apiService() {
        OkHttpClient client = new OkHttpClient();


        Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ClassTypeAdapterFactory())
                .registerTypeAdapter(Class.class, new ClassTypeAdapter()).create();


        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(API_ENDPOINT)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(client)
                .build();

        return retrofit.create(ApiService.class);
    }

    @Provides
    @Singleton
    ResponseService responseService() {
        return new ResponseService(app.getComponent());
    }

    @Provides
    @Singleton
    MainPresenter mainPresenter() {
        return new MainPresenter(app.getComponent());
    }

    @Provides
    @Singleton
    EventBus eventBus() {
        return EventBus.getDefault();
    }
}

I inject all things correctly and can work with them except ManiActivity when try to use it give me null pointer the following how i inject it

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ((App) getApplicationContext())
            .getComponent()
            .inject(this);

    setContentView(R.layout.activity_main);
    ButterKnife.bind(this);

    initRecyclerView();
    presenter.setView(this);
}

but when try to use it as Context in My adapter and pass it to Picasso library give me the following exception java.lang.IllegalArgumentException: Context must not be null. the following is how i use it

@Inject
MainActivity activity;

and use it in onBindViewHolder as following

Picasso.with(activity).load(response).fit().into(holder.ivCover);

My adapter code

public class ReAdapter extends RecyclerView.Adapter<ReAdapter.RViewHolder> {
    private List<Response> responseList;

    @Inject
    MainActivity appContext;


    public ReAdapter() {
    }

    public void setResponseList(List<Response> responseList) {
        this.responseList = responseList;
        notifyDataSetChanged();
    }

    @Override
    public RViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.response_item, parent, false);
        return new RViewHolder(view);
    }

    @Override
    public void onBindViewHolder(RViewHolder holder, int position) {
        final Response response = responseList.get(position);
            Picasso.with(appContext).load(response.getValue().toLowerCase()).fit().into(holder.ivd);
    }

    @Override
    public int getItemCount() {
        return responseList != null ? responseList.size() : 0;
    }


    public class RViewHolder extends RecyclerView.ViewHolder {
        @BindView(R.id.ivd)
        ImageView ivd;

        public RViewHolder(View view) {
            super(view);
            ButterKnife.bind(this, itemView);
        }
    }
}

Can anyone help me to solve this issue ?

Upvotes: 4

Views: 9010

Answers (1)

LordRaydenMK
LordRaydenMK

Reputation: 13321

  • Your activity: MainActivity doesn't have an @Inject annotated constructor (and can't have, because it's created by the system).
  • In your AppModule there is no @Provides annotated method that returns MainActivity.
  • You are injecting the fields in your activity not in your adapter.

The result is Dagger has no idea how to create an object of type MainActivity.

The solution for this specific problem is to use the application context for Picasso or even better create a Picasso object with Dagger.

Update your AppModule with:

@Provides
@Singleton
Picasso providePicasso(App app) {
    return Picasso.with(app);
}

In MainActivity add a field:

@Inject
ReAdapter adapter;

In ReAdapter modify the constructor and add a field for picasso:

private final Picasso picasso;

@Inject
public ReAdapter(Picasso picasso) {
    this.picasso = picasso;
}

This way Dagger can create a singleton Picasso instance. The ReAdapter is annotated with @Inject (using constructor injection) so Dagger knows how to create it. By adding a ReAdapter field in MainActivity when you call component.inject(this) in MainActivity the adapter field will be initialized.

Upvotes: 5

Related Questions