Dagger 2 base class injections - android

In Dagger 1 I had a base class setup such that it would handle creating a scoped graph and injecting dependencies into the current object. For example...
public abstract class MyBaseActivity extends Activity {
private ObjectGraph graph;
protected void onCreate(Bundle savedInstanceState) {
graph = ((MyApp) getApplication()).plus(getModules());
graph.inject(this);
}
protected Object[] getModules();
}
public class MyClass extends MyBaseActivity {
#Inject SomeDep someDep;
#Override
protected Object[] getModules() {
return new Object[/* Contains a module that provides SomeDep */];
}
}
This allowed for each subclass to supplement their own set of modules in addition to a standard application module.
After playing around with Dagger 2, it doesn't seem possible to handle a similar scenario...
public abstract class MyBaseActivity extends Activity {
private MyBaseActivityComponent component;
protected void onCreate(Bundle savedInstanceState) {
component = ((MyApp) getApplication()).component().plus(/* Can not accept an array */);
component.inject(this);
}
}
I worked around the above by modifying MyBaseActivityComponent such that it would list all possible modules it may use...
#Subcomponent(modules = {
Module1.class,
Module2.class
})
public interface MyBaseActivityComponent {
public void inject(MyBaseActivity activity);
}
So now I can do something like this...
public abstract class MyBaseActivity extends Activity {
private MyBaseActivityComponent component;
protected void onCreate(Bundle savedInstanceState) {
component = ((MyApp) getApplication()).component().plus(new Module1(), new Module2());
component.inject(this);
}
}
But now I have a problem where the injection will inject dependencies for MyBaseActivity but not it's subclasses. Suggestions?

Theoretically, you can do it like this.
1.) Specify a child scope
#Scope
#Retention(RUNTIME)
public #interface PerActivity {
}
2.) Specify the parent component
#Singleton
#Component(modules={Module1.class, Module2.class)
public interface MyApplicationComponent {
Dependency1 providesDependency1();
Dependency2 providesDependency2();
}
3.) Specify the child component
#PerActivity
#Component(dependencies={MyApplicationComponent.class}, modules={Module3.class})
public interface MyBaseActivityComponent extends MyApplicationComponent {
void inject(BaseActivity baseActivity);
Dependency3 providesDependency3();
}
4.) Create your module
#Module
public class Module3 {
#Provides
#PerActivity
public Dependency3 providesDependency3() {
return new Dependency3();
}
}
5.) Create Activity-level scoped component
public class BaseActivity extends AppCompatActivity {
private MyBaseActivityComponent baseComponent;
#Override
public void onCreate(Bundle saveState) {
super.onCreate(saveState);
baseComponent = DaggerBaseActivityComponent.builder()
.applicationComponent(((MyApp)getApplication()).component())
.build();
}
public MyBaseActivityComponent baseComponent() {
return baseComponent;
}
#Override
public void onDestroy() {
component = null;
super.onDestroy();
}
}
Please reply if it worked, previously I forgot to specify the dependencies in my Component and got compile errors, but it should work like this.
Also, if you need to specify a subcomponent for each Activity, then you can just specify the dependencies with provision methods in the BaseActivityComponent component...
#PerActivity
#Component(dependencies={MyBaseActivityComponent.class}, modules={Module4.class})
public interface MyActivityComponent extends MyBaseActivityComponent {
public void inject(MyActivity myActivity);
Dependency4 providesDependency4();
}
#Module
public class Module4 {
#PerActivity
#Provides
public Dependency4 providesDependency4(Dependency3 dependency3) {
return new Dependency4(dependency3);
}
}
public class MyActivity extends MyBaseActivity {
private MyActivityComponent component;
#Override
public void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
component = DaggerMyActivityComponent.builder()
.applicationComponent(((MyApp)getApplication()).component())
.myBaseActivityComponent(baseComponent())
.build();
}
#Override
public void onDestroy() {
component = null;
super.onDestroy();
}
}
EDIT: #Subcomponent works to replace component dependencies with subcomponent factory methods according to the docs only if you use the following pattern (aka, embedding the subcomponent within the parent component using a provision/factory method definition):
#Singleton #Component
interface ApplicationComponent {
// component methods...
RequestComponent newRequestComponent(RequestModule requestModule);
}
Where
#Subcomponent(modules={RequestModule.class})
interface RequestComponent {
RequestSomething requestSomething();
}

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 a Activity into a Fragment using Dagger2

I am trying to inject my MainActivity into the Fragment. I have an Interface that is implemented in my MainActivity that will listen to events from the Fragment. So I want to Inject the MainActivity and call the event listener on it.
I have tried doing the following but has failed to do so. Just displaying the code snippets.
interface
public interface RecipeItemListener {
void onRecipeItem();
}
MainActivity that implements the interface
public class MainActivity extends AppCompatActivity implements RecipeItemListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState == null) {
getSupportFragmentManager()
.beginTransaction()
.add(R.id.main_fragment_container, RecipeListView.newInstance(), RecipeListView.TAG)
.commit();
}
}
#Override
public void onRecipeItem() {
Timber.d("onRecipeItem");
}
}
My Module that provides the MainActivity
#Module
public class RecipeListModule {
private MainActivity mainActivity;
public RecipeListModule(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
#RecipeListScope
#Provides
public RecipeItemListener providesRecipeListMainActivity() {
return mainActivity;
}
}
My main Component
#Singleton
#Component(modules = {
AndroidModule.class,
NetworkModule.class})
public interface BusbyBakingComponent {
RecipeListComponent add(RecipeListModule recipeListModule);
}
My SubComponent
#RecipeListScope
#Subcomponent(modules = {RecipeListModule.class})
public interface RecipeListComponent {
void inject(RecipeListView target);
}
My Application class
public class BusbyBakingApplication extends Application {
private BusbyBakingComponent applicationComponent;
private RecipeListComponent recipeListComponent;
#Override
public void onCreate() {
super.onCreate();
applicationComponent = createApplicationComponent();
}
public BusbyBakingComponent createApplicationComponent() {
return DaggerBusbyBakingComponent.builder()
.networkModule(new NetworkModule())
.androidModule(new AndroidModule(BusbyBakingApplication.this))
.build();
}
public BusbyBakingComponent getApplicationComponent() {
return applicationComponent;
}
public RecipeListComponent createRecipeListComponent(MainActivity activity) {
recipeListComponent = applicationComponent.add(new RecipeListModule(activity));
return recipeListComponent;
}
public void releaseRecipeListComponent() {
recipeListComponent = null;
}
}
And in My fragment this is how I am trying to inject:
#Inject MainActivity mainActivity;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((BusbyBakingApplication)getActivity().getApplication())
.createRecipeListComponent((MainActivity)getActivity())
.inject(RecipeListView.this);
}
I keep getting the following error:
Error:(14, 8) error: [me.androidbox.busbybaking.di.RecipeListComponent.inject(me.androidbox.busbybaking.recipieslist.RecipeListView)] me.androidbox.busbybaking.recipieslist.MainActivity cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method.
me.androidbox.busbybaking.recipieslist.MainActivity is injected at
me.androidbox.busbybaking.recipieslist.RecipeListView.mainActivity
me.androidbox.busbybaking.recipieslist.RecipeListView is injected at
me.androidbox.busbybaking.di.RecipeListComponent.inject(target)
Many thanks for any suggestions.
If you have a look at your module
#RecipeListScope
#Provides
public RecipeItemListener providesRecipeListMainActivity() {
return mainActivity;
}
You provide the interface (which is good) and not MainActivity (the implementation).
Since you request MainActivity
#Inject MainActivity mainActivity;
You receive this error:
MainActivity cannot be provided [...]
because you only provide RecipeItemListener.
Switch your code from requiring MainActivity in RecipeListView to
#Inject RecipeItemListener recipeListener;
and it should work, if the rest of your setup is correct.
You can access activity in Fragment using getActivity() and cast it to your interface listener like this
((RecipeItemListener)getActivity()).doSomethingOnListener()
much simpler, without any unnecessary injections

Dagger2 - "Unused" Modules in Generated Component Class

My Dagger2 Component class contains 3 modules which I'm trying to use to inject field dependencies into an Android Activity class. The generated Component file has comments saying all the modules are unused, linking this page for more info.
My Activity class is calling the Component's inject(Activity) method and has fields annotated for injection that are provided by the modules, so I am not sure why the generated Component file does not have any Providers to do this injection.
My code is below, thanks for the help!
Generated Component Class:
public final class DaggerMainComponent implements MainComponent {
private DaggerMainComponent(Builder builder) {
assert builder != null;
}
public static Builder builder() {
return new Builder();
}
public static MainComponent create() {
return builder().build();
}
#Override
public void inject(Activity activity) {
MembersInjectors.<Activity>noOp().injectMembers(activity);
}
public static final class Builder {
private Builder() {}
public MainComponent build() {
return new DaggerMainComponent(this);
}
/**
* #deprecated This module is declared, but an instance is not used in the component. This method is a no-op. For more, see https://google.github.io/dagger/unused-modules.
*/
#Deprecated
public Builder daoModule(DaoModule daoModule) {
Preconditions.checkNotNull(daoModule);
return this;
}
/**
* #deprecated This module is declared, but an instance is not used in the component. This method is a no-op. For more, see https://google.github.io/dagger/unused-modules.
*/
#Deprecated
public Builder repositoryModule(RepositoryModule repositoryModule) {
Preconditions.checkNotNull(repositoryModule);
return this;
}
/**
* #deprecated This module is declared, but an instance is not used in the component. This method is a no-op. For more, see https://google.github.io/dagger/unused-modules.
*/
#Deprecated
public Builder portableModule(PortableModule portableModule) {
Preconditions.checkNotNull(portableModule);
return this;
}
}
}
Non-Generated Component Class:
#Component(modules={DaoModule.class,RepositoryModule.class,PortableModule.class})
public interface MainComponent {
void inject(Activity activity);
}
Module Classes:
Is there any issue with having one module provide an object with a dependency on another object provided by another module belonging to the same Component?
#Module
public class DaoModule {
private DatabaseHelper databaseHelper;
public DaoModule(DatabaseHelper databaseHelper){
this.databaseHelper = databaseHelper;
}
#Provides
public Dao<Player,Integer> providePlayerDao(){
return databaseHelper.getPlayerDao();
}
#Provides
public Dao<GamePlayed,Integer> provideGamePlayedDao() {
try {
return databaseHelper.getDao(GamePlayed.class);
} catch (SQLException e) {
return null;
}
}
#Provides
public Dao<GamePlayer,Integer> provideGamePlayerDao() {
try {
return databaseHelper.getDao(GamePlayer.class);
} catch (SQLException e) {
return null;
}
}
}
...
#Module
public class RepositoryModule {
#Provides
public IGameResultRepository provideGameResultRepository(
Dao<Player,Integer> playerDao,
Dao<GamePlayed,Integer> gameDao,
Dao<GamePlayer, Integer> gamePlayerDao)
{
return new OrmliteGameResultRepository(playerDao,gameDao,gamePlayerDao);
}
}
#Module
public class PortableModule {
#Provides
public GameResultListener provideGameResultListener(IGameResultRepository gameResultRepository){
return new GameResultListener(gameResultRepository);
}
}
Application Class:
public class AppStart extends Application {
private MainComponent mainComponent;
#Override
public void onCreate() {
super.onCreate();
DatabaseHelper databaseHelper = new DatabaseHelper(getApplicationContext());
mainComponent = DaggerMainComponent.builder()
.daoModule(new DaoModule(databaseHelper))
.build();
}
public MainComponent getMainComponent(){
return mainComponent;
}
}
Activity Class:
public class MyActivity extends Activity {
#Inject GameResultListener gameResultListener;
#Inject Dao<Player,Integer> dao;
#Inject IGameResultRepository repository;
#Override
protected void onCreate(Bundle state) {
super.onCreate(state);
((AppStart)this.getApplication()).getMainComponent().inject(this);
Question 1: Why are my modules being marked as "unused"?
You have not supplied the correct injection site! As it stands, your component interface is one with the sole injection site of android.app.Activity. Since android.app.Activity has no #Inject annotations on its fields then you get a no-op members injector. Similarly, your modules are marked as unused because none of them are actually being used as sources of dependencies for android.app.Activity. To fix this, in your component change:
void inject(Activity activity);
to:
void inject(MyActivity myActivity);
Question 2:
Is there any issue with having one module provide an object with a dependency on another object provided by another module belonging to the same Component?
No, this is perfectly fine. To illustrate, let's take a simple object graph:
public class Foo {
public Foo(FooDependency fooDependency) {}
}
public class FooDependency {
FooDependency(String name) {}
}
We want to inject it inside the following class using Dagger:
public class FooConsumer {
#Inject Foo foo;
private FooConsumer() {}
}
We would like to reuse a module binding FooDependency so we'll write two separate modules:
#Module
public class FooModule {
#Provides
Foo foo(FooDependency fooDependency) {
return new Foo(fooDependency);
}
}
#Module
public class FooDependencyModule {
#Provides
FooDependency fooDependency() {
return new FooDependency("name");
}
}
And the following component interface:
#Component(modules = {FooModule.class, FooDependencyModule.class})
public interface FooComponent {
void inject(FooConsumer fooConsumer);
}
The generated component DaggerFooComponent contains the following code that will correctly use the FooDependency from the separate module FooDependencyModule to inject Foo:
#SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.fooDependencyProvider =
FooDependencyModule_FooDependencyFactory.create(builder.fooDependencyModule);
this.fooProvider = FooModule_FooFactory.create(builder.fooModule, fooDependencyProvider);
this.fooConsumerMembersInjector = FooConsumer_MembersInjector.create(fooProvider);
}

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