Dagger2 inject everything? - android

I am reading documentation about Dagger2. The thing is I have clear how inject a class how has Context using this
But my doubt is with the following class for example:
public class SocialControler{
private ImageView twitterLogo;
private ImageView googleLogo;
public SocialControler(ImageView twitter, ImageView google){
twitterLogo = twitter;
googleLogo = google;
}
//Getters and Setters
}
So in my MainActivity I will have something like this
SocialControler mSocial = new SocialControler(mTwitterLogo, mGoogleLogo);
Should I inject this class using #Inject anotation instead doing new , so no more new on our Activity? If yes How?
I am stuck in the #Mudule . How I can provide a View inside de Module ?
#Module
public class AppModuleSocialMediaManager {
#Provides
public MainActivity provideActivity(){
return new MainActivity();
}
#Provides
public SocialMediaClickManager provideMediaManager(MainActivity mainActivity, View twitterLogo, View googleLogo) {
return new SocialMediaClickManager(mainActivity);
}
#Provides
public View provideTwitter(){
return ?
//from here how I can provide a View
}
}

I would not recommend the architecture you're using to build your app, i.e. make your controllers depend on exact views, but if you're fine with that then you can implement your module like this:
#Module
public class AppModuleSocialMediaManager {
private final MainActivity mainActivity;
public AppModuleSocialMediaManager(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
#Provides
#Named("twitter_view")
View provideTwitterView() {
return mainActivity.findViewById(..);
}
#Provides
#Named("google_view")
View provideGoogleView() {
return mainActivity.findViewById(..);
}
#Provides
SocialController provideSocialController(#Named("twitter_view") View twitterView, #Named("google_view") View googleView) {
return new SocialController(twitterView, googleView);
}
}
Then you need to declare another component. Let's say it's MainActivityComponent:
#Component(modules = AppModuleSocialMediaManager.class)
public interface MainActivityComponent {
void inject(MainActivity activity);
}
After that in your activity you can do something like this:
public class MainActivity extends Activity {
#Inject
SocialController socialController;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerMainActivityComponent.Builder()
.appModuleSocialMediaManager(new AppModuleSocialMediaManager(this))
.build()
.inject(this);
socialController.doSomething();
}
}

Related

Android Java Dagger Inject Problems

My AppComponent
#Singleton
#Component(modules = {SplashModule.class})
public interface AppComponent {
void inject(SplashActivity splashActivity);//this is inject activity
}
Splash Module
#Module
public class SplashModule {
#Provides
#Singleton
static SplashInteract provideSplashInteract(){
return new SplashInteract();//instance interact
};
#Provides
#Singleton
SplashPresenter provideSplashPresenter(SplashInteract splashInteract){
return new SplashPresenter(splashInteract);//instance SplashPresenter
};
}
Splash Presenter
public class SplashPresenter implements ISplashContract.Presenter {
ISplashContract.View mView;
SplashInteract splashInteract;
public SplashPresenter(SplashInteract splashInteract) {
this.splashInteract =splashInteract;
}
public void bindView(ISplashContract.View mView) {
this.mView = mView;
}
#Override
public void attach() {
this.mView.startAnimation();//start splash animation
}
#Override
public void start(Activity activity) {
this.splashInteract.SplashScreenAnimation(activity);// add interact methods
}
}
Splash Activity
public class SplashActivity extends AppCompatActivity implements ISplashContract.View{
#Inject SplashPresenter splashPresenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
getAppComponent().inject(this);//call BaseApp DaggerAppComponent
splashPresenter.attach();
splashPresenter.bindView(this);
}
#Override
public void startAnimation() {
this.splashPresenter.start(this);
}
}
Base App
public class BaseApp extends Application {
private static AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
setUp();
}
private void setUp() {
appComponent = DaggerAppComponent.builder().build();//Call Component in BaseApp
}
public static AppComponent getAppComponent() {
return appComponent;
}
}
Hi Everyone
I am writing a project, I want to use dagger, but I am inexperienced at this. This code gives a NullPointerException error. I could not find what I am doing wrong.
I need help and I will be glad if those who know better than the dagger guide me
In your case, dagger will not know how to provide splashPresenter, either do a constructor Injection or define in the module how SplashPresenter should be created, and remove SpashPresenter from the Component. The module approach should be like so,
#Module
public class SplashModule {
ISplashContract.View mView;
public SplashModule(ISplashContract.View mView) {
this.mView = mView;//add SplashModule view
}
#Provides
#Singleton
public ISplashContract.View provideSplashPresenter(){
return mView;//set this view
}
#Provides
static SplashInteract provideSplashInteract(){
return new SplashInteract();//instance interact
};
#Provides
SplashPresenter provideSplashPresenter(ISplashContract.View mView, SplashInteract splashInteract){
return new SplashPresenter(mView, splashInteract);//instance SplashPresenter
};
}
And remove inject annotations from SplashPresenter and you will also have to change the signature for its constructor. You can optionally remove Singleton annotation from the code, if the presenter is not supposed to be singleton.
Updates based on comments
public class SplashModule {
#Provides
static SplashInteract provideSplashInteract(){
return new SplashInteract();//instance interact
};
#Provides
SplashPresenter provideSplashPresenter(SplashInteract splashInteract){
return new SplashPresenter(splashInteract);//instance SplashPresenter
};
}
public class SplashPresenter implements ISplashContract.Presenter {
ISplashContract.View mView;
SplashInteract splashInteract;
public SplashPresenter(SplashInteract splashInteract) {
this.splashInteract = splashInteract;
}
public void bindView(ISplashContract.View mView) {
this.mView = mView;
}
#Override
public void attach() {
this.mView.startAnimation();//start splash animation
}
#Override
public void start(Activity activity) {
this.splashInteract.SplashScreenAnimation(activity);// add interact methods
}
}
public class SplashActivity extends AppCompatActivity implements ISplashContract.View{
#Inject SplashPresenter splashPresenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
getAppComponent().inject(this);//call BaseApp DaggerAppComponent
splashPresenter.bind("the view you want to bind")
splashPresenter.attach();
}
#Override
public void startAnimation() {
this.splashPresenter.start(this);
}
}```
Make sure that you declare your BaseApp in AndroidManifest :
<application
android:name=".fullPathTo.BaseApp"
...

Dagger 2 returns null after injection

I am trying to make an injection using Dagger 2, but it always returns null. I think I am doing all right, but anyway it does not work.
Here is the application class:
public class ApplicationSA extends Application {
private static AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.create();
}
public static AppComponent getComponent() {
return appComponent;
}
}
The component interface:
#Component(modules = {
SnoreDetectorClass.class,
AudioRecorderClass.class
})
public interface AppComponent {
void injectsMainFunctionalityActivity(Activity activity);
}
An the main class where I am trying to get the object:
public class MainFunctionalityActivity extends AppCompatActivity {
#Inject
AudioRecorderClass audioRecorderClass;
#Inject
SnoreDetectorClass snoreDetectorClass;
#Override
protected void onCreate(Bundle savedInstanceState) {
ApplicationSA.getComponent().injectsMainFunctionalityActivity(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("TESTING","audioRecorderClass= "+audioRecorderClass); // always null
Log.d("TESTING","snoreDetectorClass= "+snoreDetectorClass); // always null
...
}
And here are the module classes:
#Module
public class AudioRecorderClass {
public interface AudioRecorderInterface {
void AudioRecorder_hasUpdate(double amplitude_in_dB);
}
public AudioRecorderInterface delegate = null;
#Provides
AudioRecorderClass provideAudioRecorderClass(Activity activity) {
delegate = (AudioRecorderInterface)activity;
return new AudioRecorderClass();
}
...
#Module
public class SnoreDetectorClass {
#Provides
SnoreDetectorClass provideSnoreDetectorClass() {
return new SnoreDetectorClass();
}
...
What am I doing wrong ? Why the objects are always null ?
Ah, I see what is going on here. You cannot inject into a subclass. So in your AppComponent you cannot have
void injectsMainFunctionalityActivity(Activity activity);
you must inject with
void injectsMainFunctionalityActivity(MainFunctionalityActivity activity);
As a side note I would suggest not combining your injector and your model class. Better to have separation of concerns. Keep them separate
You have to specifically tell dagger which activity will be injected here, not use the super class Activity but rather your own implementation of the Activity class :
void injectsMainFunctionalityActivity(Activity activity);
change to:
void injectsMainFunctionalityActivity(MainFunctionalityActivity activity);

How to inject an Activity into an Adapter using dagger2

Android Studio 3.0 Canary 8
I am trying to inject my MainActivity into my Adapter. However, my solution works ok, but I think its a code smell and not the right way to do it.
My adapter snippet looks like this the but I don't like about this is that I have to cast the Activity to MainActivity:
public class RecipeAdapter extends RecyclerView.Adapter<RecipeListViewHolder> {
private List<Recipe> recipeList = Collections.emptyList();
private Map<Integer, RecipeListViewHolderFactory> viewHolderFactories;
private MainActivity mainActivity;
public RecipeAdapter(Activity activity, Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
this.recipeList = new ArrayList<>();
this.viewHolderFactories = viewHolderFactories;
this.mainActivity = (MainActivity)activity;
}
#Override
public RecipeListViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
/* Inject the viewholder */
final RecipeListViewHolder recipeListViewHolder = viewHolderFactories.get(Constants.RECIPE_LIST).createViewHolder(viewGroup);
recipeListViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
/* Using the MainActivity to call a callback listener */
mainActivity.onRecipeItemClick(getRecipe(recipeListViewHolder.getAdapterPosition()));
}
});
return recipeListViewHolder;
}
}
In my Module, I pass the Activity in the module's constructor and pass it to the Adapter.
#Module
public class RecipeListModule {
private Activity activity;
public RecipeListModule() {}
public RecipeListModule(Activity activity) {
this.activity = activity;
}
#RecipeListScope
#Provides
RecipeAdapter providesRecipeAdapter(Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
return new RecipeAdapter(activity, viewHolderFactories);
}
}
In My Application class I create the components and I am using a SubComponent for the adapter. Here I have to pass the Activity which I am not sure is a good idea.
#Override
public void onCreate() {
super.onCreate();
applicationComponent = createApplicationComponent();
recipeListComponent = createRecipeListComponent();
}
public BusbyBakingComponent createApplicationComponent() {
return DaggerBusbyBakingComponent.builder()
.networkModule(new NetworkModule())
.androidModule(new AndroidModule(BusbyBakingApplication.this))
.exoPlayerModule(new ExoPlayerModule())
.build();
}
public RecipeListComponent createRecipeListComponent(Activity activity) {
return recipeListComponent = applicationComponent.add(new RecipeListModule(activity));
}
My Fragment I inject like this:
#Inject RecipeAdapter recipeAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((BusbyBakingApplication)getActivity().getApplication())
.createRecipeListComponent(getActivity())
.inject(this);
}
Even though the above design works, I think it's a code smell as I have to cast the Activity to the MainActivity. The reason I use the Activity as I want to make this module more generic.
Just wondering if there is a better way
=============== UPDATE USING INTERFACE
Interface
public interface RecipeItemClickListener {
void onRecipeItemClick(Recipe recipe);
}
Implementation
public class RecipeItemClickListenerImp implements RecipeItemClickListener {
#Override
public void onRecipeItemClick(Recipe recipe, Context context) {
final Intent intent = Henson.with(context)
.gotoRecipeDetailActivity()
.recipe(recipe)
.build();
context.startActivity(intent);
}
}
In my module, I have the following providers
#Module
public class RecipeListModule {
#RecipeListScope
#Provides
RecipeItemClickListener providesRecipeItemClickListenerImp() {
return new RecipeItemClickListenerImp();
}
#RecipeListScope
#Provides
RecipeAdapter providesRecipeAdapter(RecipeItemClickListener recipeItemClickListener, Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
return new RecipeAdapter(recipeItemClickListener, viewHolderFactories);
}
}
Then I use it through constructor injection in the RecipeAdapter
public class RecipeAdapter extends RecyclerView.Adapter<RecipeListViewHolder> {
private List<Recipe> recipeList = Collections.emptyList();
private Map<Integer, RecipeListViewHolderFactory> viewHolderFactories;
private RecipeItemClickListener recipeItemClickListener;
#Inject /* IS THIS NESSESSARY - AS IT WORKS WITH AND WITHOUT THE #Inject annotation */
public RecipeAdapter(RecipeItemClickListener recipeItemClickListener, Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
this.recipeList = new ArrayList<>();
this.viewHolderFactories = viewHolderFactories;
this.recipeItemClickListener = recipeItemClickListener;
}
#Override
public RecipeListViewHolder onCreateViewHolder(final ViewGroup viewGroup, int i) {
/* Inject the viewholder */
final RecipeListViewHolder recipeListViewHolder = viewHolderFactories.get(Constants.RECIPE_LIST).createViewHolder(viewGroup);
recipeListViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
recipeItemClickListener.onRecipeItemClick(getRecipe(recipeListViewHolder.getAdapterPosition()), viewGroup.getContext());
}
});
return recipeListViewHolder;
}
}
Just one question, is the #Inject annotation need for the constructor in the RecipeAdapter. As it works with or without the #Inject.
Do not pass Activities into Adapters - This is a really bad practice.
Inject only the fields you care about.
In your example: Pass an interface into the adapter to track the item click.
If you need a MainActivity then you should also provide it. Instead of Activity declare MainActivity for your module.
#Module
public class RecipeListModule {
private MainActivity activity;
public RecipeListModule(MainActivity activity) {
this.activity = activity;
}
}
And your Adapter should just request it (Constructor Injection for non Android Framework types!)
#RecipeListScope
class RecipeAdapter {
#Inject
RecipeAdapter(MainActivity activity,
Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
// ...
}
}
If you want your module to use Activity and not MainActivity then you will need to declare an interface as already mentioned. You adapter would then declare the interface as its dependency.
But in some module you will still have to bind that interface to your MainActivity and one module needs to know how to provide the dependency.
// in some abstract module
#Binds MyAdapterInterface(MainActivity activity) // bind the activity to the interface
Addressing the updated part of the question
Just one question, is the #Inject annotation need for the constructor in the RecipeAdapter. As it works with or without the #Inject.
It works without it because you're still not using constructor injection. You're still calling the constructor yourself in providesRecipeAdapter(). As a general rule of thumb—if you want to use Dagger properly—don't ever call new yourself. If you want to use new ask yourself if you could be using constructor injection instead.
The same module you show could be written as follows, making use of #Binds to bind an implementation to the interface, and actually using constructor injection to create the adapter (which is why we don't have to write any method for it! Less code to maintain, less errors, more readable classes)
As you see I don't need to use new myself—Dagger will create the objects for me.
public abstract class RecipeListModule {
#RecipeListScope
#Binds
RecipeItemClickListener providesRecipeClickListener(RecipeItemClickListenerImp listener);
}
Personally I would do the following trick
public class MainActivity extends AppCompatActivity {
private static final String TAG = "__ACTIVITY__";
public static MainActivity get(Context context) {
// noinspection ResourceType
return (MainActivity)context.getSystemService(TAG);
}
#Override
protected Object getSystemService(String name) {
if(TAG.equals(name)) {
return this;
}
return super.getSystemService(name);
}
}
public class RecipeAdapter extends RecyclerView.Adapter<RecipeListViewHolder> {
private List<Recipe> recipeList = Collections.emptyList();
private Map<Integer, RecipeListViewHolderFactory> viewHolderFactories;
public RecipeAdapter(Map<Integer, RecipeListViewHolderFactory> viewHolderFactories) {
this.recipeList = new ArrayList<>();
this.viewHolderFactories = viewHolderFactories;
}
#Override
public RecipeListViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
/* Inject the viewholder */
final RecipeListViewHolder recipeListViewHolder = viewHolderFactories.get(Constants.RECIPE_LIST).createViewHolder(viewGroup);
recipeListViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MainActivity mainActivity = MainActivity.get(v.getContext());
if(recipeListViewHolder.getAdapterPosition() != -1) {
mainActivity.onRecipeItemClick(
getRecipe(recipeListViewHolder.getAdapterPosition()));
}
}
});
return recipeListViewHolder;
}
}

cannot be provided without an #Inject constructor or from an #Provides-annotated method

I am using Android Dagger2 but I am getting the error below.
My AppModule class is:
#Module
public class AppModule {
#Provides
public DownloadFilePresenterImp provideDownloadfilePresenterImp(DownloadFileView downloadFileView) {
return new DownloadFilePresenterImp(downloadFileView);
}
}
My AppComponent interface is:
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
void inject(DownloadFileView target);
}
My DaggerInject class
public class DaggerInjector {
private static AppComponent mAppComponent = DaggerAppComponent
.builder()
.appModule(new AppModule())
.build();
public static AppComponent getAppComponent() {
return mAppComponent;
}
}
I am trying to inject into my Fragment (DownloadFileView)
#Inject DownloadFilePresenterImp mDownloadFilePresenterImp;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.download_file_view, container, false);
/* Initialize presenter */
DaggerInjector.getAppComponent().inject(DownloadFileView.this);
/* Use mDownloadFilePresenterImp here */
return view;
}
And my DownloadFilePresenterImp constructor part
public class DownloadFilePresenterImp implements DownloadFilePresenterContact {
public DownloadFilePresenterImp(DownloadFileView downloadFileView) {
mDownloadFileContract = downloadFileView;
}
}
This is the error I am getting:
Error:(17, 10) error: com.sunsystem.downloadfilechatapp.downloader.DownloadFileView cannot be provided without an #Inject constructor or from an #Provides-annotated method. This type supports members injection but cannot be implicitly provided.
com.sunsystem.downloadfilechatapp.downloader.DownloadFileView is injected at
com.sunsystem.downloadfilechatapp.downloader.dagger.AppModule.provideDownloadfilePresenterImp(downloadFileView)
com.sunsystem.downloadfilechatapp.downloader.DownloadFilePresenterImp is injected at
com.sunsystem.downloadfilechatapp.downloader.DownloadFileView.mDownloadFilePresenterImp
com.sunsystem.downloadfilechatapp.downloader.DownloadFileView is injected at
com.sunsystem.downloadfilechatapp.downloader.dagger.AppComponent.inject(target)
Many thanks for any suggestions,
The error just states that dagger doesn't have a way to provide said dependency. You will have to add it to your component in some way—and since it is a Fragment—you will have to use a #Module.
I am assuming that your AppComponent is created by your Application on start. Your AppComponent has a lifecycle that is longer than both your activities and fragments lifecycles are. It is therefore reasonable, that it does not know how to provide an activity, or a fragment in your case.
Your DownloadFilePresenterImp depends on your DownloadFileView.
You want to inject DownloadFilePresenterImp into your DownloadFileView
To inject the view and presenter you are using your AppComponent which knows nothing about the activity, and obviously nothing about the fragment. It has a different scope and lifecycle.
To not further cause confusion I will be talking about fragments, since their and the activities lifecycle are what you have to keep in mind. You can just use DownloadFileView with the module, but those long names would get confusing.
To provide a fragment or activity you have to use a module. e.g.
#Module FragmentModule {
Fragment mFragment;
FragmentModule(Fragment fragment) { mFragment = fragment; }
#Provides Fragment provideFragment() { return mFragment; }
// here you could also provide your view implementation...
#Provides DownloadFileView provideDownloadFileView() {
return (DownloadFileView) mFragment;
}
}
Since this module should only live along with fragments lifecycle, you have to use either subcomponents, or components with a dependency on your AppComponent.
#Component(dependencies=AppComponent.class, modules=FragmentModule.class)
interface FragmentComponent {
// inject your fragment
void inject(DownloadFileView fragment);
}
In your fragment you'd have to properly create your component...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.download_file_view, container, false);
// properly create a component that knows about the fragment
DaggerFragmentComponent.builder()
.appComponent(DaggerInjector.getAppComponent())
.fragmentModule(new FragmentModule(this))
.build()
.inject(DownloadFileView.this);
return view;
}
Also, I highly recommend having a look at and using constructor injection
public class DownloadFilePresenterImp implements DownloadFilePresenterContact {
#Inject
public DownloadFilePresenterImp(DownloadFileView downloadFileView) {
mDownloadFileContract = downloadFileView;
}
}
Alternatively you can move the provideDownloadfilePresenterImp(View) method to the FragmentModule to have the same effect if you like redundant code.
Done.
Ok...this should work
#Module
public class AppModule {
#Provides
public DownloadFilePresenterImp provideDownloadfilePresenterImp() {
return new DownloadFilePresenterImp();
}
}
and
public class DownloadFilePresenterImp implements DownloadFilePresenterContact {
private WeakReference<DownloadFileView> weak;
public DownloadFilePresenterImp() {
}
public void setDownloadFileView(DownloadFileView view) {
weak = new WeakReference<>(view);
}
public void doSomething() {
if (weak != null) {
DownloadFileView view = weak.get();
if (view != null) {
[...]
}
}
}
}
Basically, all objects provided by the graph (the component) must be built with objects belonging or built by the graph itself. So I removed the DownloadFilePresenterImp(DownloadFileView downloadFileView) constructor that is going to cause a leak and replaced it with a setter that creates a WeakReference to the View.
EDIT
If you want to inject an object that you build at runtime, the only way to go is to pass that object to the AppComponent. In your case it could be:
AppComponent.java
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
void inject(DownloadFileView target);
}
AppModule.java
#Module
public class AppModule {
private final DownloadFileView downloadFileView;
public AppModule(DownloadFileView downloadFileView) {
this.downloadFileView = downloadFileView;
}
#Provides
public DownloadFilePresenterImp provideDownloadfilePresenterImp() {
return new DownloadFilePresenterImp(downloadFileView);
}
}
DownloadFilePresenterImp.java
public class DownloadFilePresenterImp {
private final DownloadFileView downloadFileView;
public DownloadFilePresenterImp(DownloadFileView downloadFileView) {
this.downloadFileView = downloadFileView;
}
}
DownloadFileView.java
public class DownloadFileView extends Fragment {
private AppComponent mAppComponent;
#Inject
DownloadFilePresenterImp impl;
public DownloadFileView() {
// Required empty public constructor
mAppComponent = DaggerAppComponent
.builder()
.appModule(new AppModule(this))
.build();
mAppComponent.inject(this);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_download_file_view, container, false);
}
}
In my case, I had a presenter class in Kotlin, while using Dagger2. I forgot
#Inject constructor
as the constructor of the presenter.
The dagger errors was not very useful
In my case, I was using a mismatching version between my main :app module and other modules. Make sure you're using the right versions and that they are the same between modules.
As a permanent fix, in my android level build.gradle, I have the following:
buildscript {
ext.dagger_version = "2.30.1"
}
and all my modules build.gradle files refer to the same version:
// Dependency Injection
implementation "com.google.dagger:dagger:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version"
For me the problem was that another component had an unused inject method with the same class as input parameter. Removed that inject method and it compiled.
I highly recommend you to checkout this complete implementation of dragger :
https://github.com/Spentas/securechat/blob/master/app/src/main/java/com/spentas/javad/securechat/app/AppComponent.java
Or follow following:
In your AppModule:
#Singleton
#Provides
public DownloadFilePresenterImp provideDownloadfilePresenterImp(DownloadFileView downloadFileView) {
return new DownloadFilePresenterImp(downloadFileView);
}
your AppComponent:
#Singleton
#Component(modules = {AppModule.class})
public interface AppComponent {
void inject(DownloadFileView target);
}
then you need to initialize your dagger in your Application Class :
public class App extends Application {
private AppComponent component;
#Override
public void onCreate() {
super.onCreate();
mContext = this;
component = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
}
public AppComponent getComponent(){
return component;
}
In wherever u want to use:
((App) App.getContext()).getComponent().inject(this);

Unable to inject class with Dagger 2

I am attempting to add Dagger 2 to my Android Project. I think I understand the concepts up to the point of where I build the graph. At that point I'm shooting in the dark and that is where I'm going wrong.
Everything compiles, but the injected field is null at Runtime.
I am attempting to start simply by injecting the Presenter into my MainActivity. I have written the following code and would appreciate some help figuring out where I have gone wrong.
My PresenterModule.java:
#Module
public class PresenterModule {
#Provides MainActivityPresenter providesMainActivityPresenter() {
return new DefaultMainActivityPresenter();
}
}
My Application class which also includes my Component following the Dagger2 example code:
public class App extends Application {
private PresenterComponent component;
#Singleton
#Component(modules = PresenterModule.class)
public interface PresenterComponent {
void inject(App app);
void inject(MainActivity activity);
}
#Override public void onCreate() {
Log.d("App.java", "Starting Application");
super.onCreate();
component = DaggerApp_PresenterComponent.builder()
.presenterModule(new PresenterModule())
.build();
component.inject(this);
}
public PresenterComponent component() {
return component;
}
}
And finally my MainActivity.
public class DefaultMainActivity
extends ActionBarActivity
implements MainActivity
{
#Inject MainActivityPresenter mPresenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((App)getApplication()).component().inject(this);
mPresenter.getCurrentDetailLineNumber();
setContentView(R.layout.base_layout);
getSupportActionBar();
mContainer = (Container) findViewById(R.id.container);
mPresenter.requestCurrentScreen();
}
The actual object to be injected is an implementation of an interface but is other wise a POJO object:
public class DefaultMainActivityPresenter implements MainActivityPresenter {
private static final int SCREEN_BROWSER = 0;
private static final int SCREEN_DETAIL = 1;
LineNumber mCurrentDetailLineNumber;
int mCurrentScreen;
#Inject
public DefaultMainActivityPresenter() {
}
...
}
Changing PresenterComponent to following will fix your problem:
#Singleton
#Component(modules = PresenterModule.class)
public interface PresenterComponent {
void inject(App app);
void inject(DefaultMainActivity activity);
}
This is due to covariance:
While a members-injection method for a type will accept instances of its subtypes, only Inject-annotated members of the parameter type and its supertypes will be injected; members of subtypes will not. For example, given the following types, only a and b will be injected into an instance of Child when it is passed to the members-injection method injectSelf(Self instance):
class Parent {
#Inject A a;
}
class Self extends Parent {
#Inject B b;
}
class Child extends Self {
#Inject C c;
}

Categories

Resources