I'm trying to use Dagger for my android project but I encounter a Problem: Is it possible to mix constructor and field injection?
I have a module and two classes:
#Module(injects = fooActivity.class)
public class Module {
private fooActivity activity;
public Module(fooActivity activity) {
this.activity = activity;
}
#Provides
public bar provideBar() {
return new bar(activity);
}
}
public class fooActivity extends Activity {
#Inject Baz baz;
public void onCreate(Bundle savedInstanceState) {
...
ObjectGraph graph = ObjectGraph.create(new BasicModule()).plus(new Module(this));
graph.inject(this);
}
}
public class bar {
#Inject Baz baz;
#Inject FooActivity activity;
public bar(FooActivity activity) {
this.activity = activity;
}
}
The problem is that 'baz' in the class 'Bar' is always null after the injection. It works though, if I don't use a provide method but only use field injection and handle all dependencies in 'Module'.
So from my point of view there are three possibilities:
1) Do field injection only and handle all dependencies in module classes.
2) Do constructor injection only and have all dependencies as constructor parameters.
3) Mix both but call .inject(this) on a ObjectGraph-object in the constructor to satisfy fields with #Inject annotation.
Is this correct?
EDIT:
I cannot use complete constructor injection because 'baz' is declared in another module. Editing the example.
#Module(includes = Module.class)
public class BasicModule {
#Provides
public baz provideBaz() {
return new baz();
}
}
I think what you are trying to do is possible. I think you want to add your activity graph to your main application graph in dagger.
Check out https://github.com/square/dagger/tree/master/examples/android-activity-graphs.
Also your class bar, you should probably do complete constructor injection. But if you MUST do it the way you are doing, you need to call inject and register bar in a module. Here is the way I would create Bar.
public class Bar {
private final Activity activity;
private final Baz baz;
#Inject public bar(FooActivity activity, Baz baz) {
this.activity = activity;
this.baz = baz;
}
}
Related
I'm learning how to use Dagger2 and MVP, so i created project, contains one main activity with viewPager and two fragments.
Code is working, but i'm think i do something wrong when i create my Dagger classes. I think if i contunue, a create a monster =)
I'm asking you help to comment my code architecture, associated with Dagger2. Maybe this question should be on CodeReview: if so, i del and move it.
Ok, i show it step by step. Main suspicions of bad code on step 3.
At the first, main activity called HomeActivity, and fragment(i show one) called HomePacksFragment. App about animals, and fragment show packs of them, it's why it called so.
1) I have two modules: PresentersModule, provide presenters for HomeActivity and HomePacksFragment:
#Module
public class PresentersModule {
#Provides
HomePresenter provideHomePresenter(AnimalDatabase animalDatabase) {
return new HomePresenter(animalDatabase);
}
#Provides
HomePacksFragmentPresenter provideHomePacksFragmentPresenter(AnimalDatabase animalDatabase) {
return new HomePacksFragmentPresenter(animalDatabase);
}
}
and RoomModule prodive access to database and DAO:
#Module
public class RoomModule {
private AnimalDatabase db;
public RoomModule(Application mApplication) {
db = Room.databaseBuilder(mApplication,
AnimalDatabase.class, AnimalDatabase.DATABASE_NAME)
.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.build();
}
#Singleton
#Provides
AnimalDatabase providesRoomDatabase() {
return db;
}
#Singleton
#Provides
DAO providesProductDao(AnimalDatabase db) {
return db.daoAccess();
}
}
2) Then i have component AppComponent who knows all my modules and can inject dependences in concrete view:
#Singleton
#Component(modules = {PresentersModule.class, RoomModule.class})
public interface AppComponent {
void injectsHomeActivity(HomeActivity homeActivity);
void injectsHomePacksFragment(HomePacksFragment homePacksFragment);
DAO animalDao();
AnimalDatabase animalDatabase();
}
3) Then i have class AnimalsLibraryApp extended Application, and this is the place i make wrong in my opinion.
public class AnimalsLibraryApp extends Application {
private static AnimalsLibraryApp instance;
private static AppComponent homeActivityComponent;
private static AppComponent homeFragmentPacksComponent;
#Override
public void onCreate() {
super.onCreate();
instance = this;
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
}
createHomeActivityComponent();
createHomePacksComponent();
}
public static AnimalsLibraryApp getInstance() {
return instance;
}
public static AppComponent getHomeActivityComponent() {
return homeActivityComponent;
}
public static AppComponent getHomeFragmentPacksComponent() {
return homeFragmentPacksComponent;
}
private void createHomeActivityComponent() {
homeActivityComponent = DaggerAppComponent.builder()
.presentersModule(new PresentersModule())
.roomModule(new RoomModule(instance))
.build();
}
private void createHomePacksComponent() {
homeFragmentPacksComponent = DaggerAppComponent.builder()
.presentersModule(new PresentersModule())
.roomModule(new RoomModule(instance))
.build();
}
}
Idea of this class was provide singletons of AppComponent. Technicaly it works, but what if i have 10 or 20 views? Class, extended Application is basic class, it will not contain only AppComponent create logic, so much more will be here. Do i write it correct?
4) It's end. I create my HomeActivity and inject dependences with simple(is't it?) line.
public class HomeActivity extends AppCompatActivity implements HomeContract.View {
#Inject
HomePresenter presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
AnimalsLibraryApp.getHomeActivityComponent().injectsHomeActivity(this);
presenter.attachView(this);
presenter.viewIsReady();
DatabaseUtils.copyDatabase(this, AnimalDatabase.DATABASE_NAME);
ViewPager viewPager = findViewById(R.id.pager);
HomeMenuAdapter myPagerAdapter = new HomeMenuAdapter(getSupportFragmentManager());
viewPager.setAdapter(myPagerAdapter);
TabLayout tabLayout = findViewById(R.id.tablayout);
tabLayout.setupWithViewPager(viewPager);
tabLayout.getTabAt(0).setIcon(R.drawable.help);
tabLayout.getTabAt(1).setIcon(R.drawable.help);
}
}
I do right things or i must to change something until it's not too late?
UPD:
Ok, i google to simple Dagger 2 examples and found this App class version:
public class MyApp extends Application {
private static MyApp app;
private AppModule appModule;
private BasicComponent basicComponent;
#Override
public void onCreate() {
super.onCreate();
app = this;
appModule = new AppModule(this);
basicComponent = DaggerBasicComponent.builder()
.appModule(appModule)
.build();
}
public static MyApp app() {
return app;
}
public AppModule appModule() {
return appModule;
}
public BasicComponent basicComponent() {
return basicComponent;
}
It's look like better and cleaner that my, but i have a question. In this example we always retutn basicComponent, so it means that it must included all modules? It's normal practice to create "god" component? =)
If i have 3 modules, i need to write(for my example):
appComponent = DaggerAppComponent
.builder()
.appModule(appModule)
.presentersModule(new PresentersModule())
.roomModule(new RoomModule(this))
.build();
According to my idea and the reference as you said you are using AnimalsLibraryApp in a bad way.
In the application class, you just need to declare one dagger component then in every view, you will inject the module with that component in the view.
If you google a dagger2 sample and check the class that extended the application you will understand.
I have a class
public class DialogUtils
{
private Context context;
#Inject
public DialogUtils(Context context)
{
this.context = context;
}
}
In my activity class i have did but i'm getting null pointer exception on dialogUtils instance.
public class LoginActivity extends Activity{
#Inject DialogUtils dialogUtils;
}
I know how to inject dependency via module and component but not sure how to with construction injection. Any help is much appreciated.
If you don't retain the activity-level component and you aren't inheriting from a superscope (application-level component) using component dependency or subcomponent, then it's the following
// unscoped
public class DialogUtils {
private final Context context;
#Inject
public DialogUtils(Context context) {
this.context = context;
}
}
then
#Module
public class ActivityModule {
private final Context context;
public ActivityModule (Context context) {
this.context = context;
}
#Provides //scope is not necessary for parameters stored within the module
public Context context() {
return context;
}
}
#Component(modules={ActivityModule.class})
#Singleton
public interface ActivityComponent {
Context context();
DialogUtils dialogUtils();
void inject(MainActivity mainActivity);
}
And then
#Inject
DialogUtils dialogUtils;
...
ActivityComponent activityComponent = DaggerMainActivityComponent.builder()
.activityModule(new ActivityModule(MainActivity.this))
.build();
activityComponent.inject(this); // activityComponent.dialogUtils() also works
On the one hand you are registering DialogUtils for Constructor Injection, so any component could provide it.
On the other hand an activity and other parts of the Android Framework still need to be field injected. Dagger can't call their constructor, and #Inject DialogUtils dialogUtils; will not just magically appear.
Here you have to use a component, and register a method that takes your components type as an argument. Dagger will then create the method to inject your activities fields.
#Component MyComponent {
inject(LoginActivity activity);
}
To inject the fields you still have to create your component, and call the inject(loginActivity) method.
void onCreate(...) {
MyComponent component = // create the component
// dagger does some heavy lifting here
component.inject(this);
dialogUtils.doSomething(); // not null, we just injected our fields
}
I'm using dagger:1.2.2. There're two modules. BaseModule serves an EventBus for an Activity and ActivityModule serves NavigationController for the same Activity.
#Module(injects = { `does NOT have MainActivity.class registered`},
library = true
)
public class BaseModule {
#Provides #Singleton public EventBus provideEventBus() {
return DefaultEventBus.create();
}
#Provides #Singleton public Application provideApplication() {
return mApp;
}
#Provides #IsTablet public boolean isTablet(Application app) {
return app.getResources().getBoolean(R.bool.is_tablet);
}
}
#Module(
addsTo = BaseModule.class,
injects = MainActivity.class
)
public class ActivityModule {
#Provides
NavigationController provideNavigationController(Application app,#IsTablet boolean isTablet) {
return new NavigationController(app, isTablet);
}
}
Activity's fields:
#Inject
EventBus mEventBus;
#Inject
NavigationController mNavigationController;
NavigationController's ugly ctor:
public NavigationController(Context context, FragmentManager fm, List<Behaviour> behaviourList, #Nullable Class lastRoot,
#IsTablet boolean isTablet, Application application) {
This code passes static validation and performs well with
protected void onCreate(Bundle savedInstanceState) {
ObjectGraph activityGraph = getBaseModuleGraph(this).plus(new ActivityModule(this));
activityGraph.inject(this);
}
It crushes as expected due to unsatisfied dependency provided by ActivityModule if i just apply:
getBaseModuleGraph(this).inject(this);
The thing that bugs me is that BaseModule's inject doesn't contain MainActivity while it provides EventBus dependency for it. If I add this inject it gonna complain about unresolved NavigationController that is provided by ActivityModule. Setting complete=false on the BaseModule results in runtime exception
NavigationController has no injectable members. Do you want to add an
injectable constructor? required by class
MainActivity
I don't quite get it.
Ultimately I wanted BaseModule's injects to be more explicit by including MainActivity because it provides EventBus as I mentioned.
Could you help me out understanding it?
I want to replace our component registry (with dexfile class loader magic) with an dependency injection framework for Android.
The first try is dagger.
When trying I get the following error:
11-06 13:05:41.040 16269-16269/com.daggertoolkitexample E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{daggertoolkitexample/com.dagger.MyActivity}: java.lang.IllegalArgumentException: No inject registered for members/com.dagger.MyActivity. You must explicitly add it to the 'injects' option in one of your modules.
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2295)
...
Caused by: java.lang.IllegalArgumentException: No inject registered for members/com.dagger.MyActivity. You must explicitly add it to the 'injects' option in one of your modules.
at dagger.ObjectGraph$DaggerObjectGraph.getInjectableTypeBinding(ObjectGraph.java:302)
at dagger.ObjectGraph$DaggerObjectGraph.inject(ObjectGraph.java:279)
at com.dagger.MyApplication.inject(MyApplication.java:39)
at com.dagger.MyBaseActivity.onCreate(MyBaseActivity.java:18)
at com.dagger.MyActivity.onCreate(MyActivity.java:22)
at android.app.Activity.performCreate(Activity.java:5372)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1104)
...
I can fix it if i inject my activity in the #Module. Its work without exception.
#Module(
library = true,
injects = MyActivity.class)
public class AuthManagementModul {...}`
But this is not that i want.
I don´t can and want to know all users of my component.
Has everyone an idea what's wrong?
Here is my example code:
public class MyBaseActivity extends ActionBarActivity {
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((MyApplication) getApplication()).inject(this);
}
}
...
public class MyActivity extends MyBaseActivity {
#Inject AuthManagement authManagement;
...
}
...
public class MyApplication extends Application{
private ObjectGraph graph;
#Override
public void onCreate() {
super.onCreate();
graph = ObjectGraph.create(new AuthManagementModul(this));
}
public void inject(Object object) {
graph.inject(object);
}
}
...
#Module(
library = true
)
public class AuthManagementModul {
private final Application application;
public AuthManagementModul(Application application) {
this.application = application;
}
#Provides
#Singleton
AuthManagement provideAuthManagement() {
return new AuthManagementImpl(application);
}
}
In this case, you don't want to add injects= to your AuthManagementModul, but rather to an activity-specific module which includes it.
Dagger 1.x uses injects= as a signal for what graph-roots to analyze, so they must be present -but they need not be present on leaf-node library modules - just on a module the activity uses. Consider breaking up your modules on more partitioned lines like so:
#Module(
injects = {
... all your activities
},
includes = {
AuthManagementModul.class,
ApplicationModule.class
}
)
class EntryPointsModule {}
#Module(library = true, complete = false)
class AuthManagementModul {
#Provides
#Singleton
AuthManagement provideAuthManagement(Application application) {
return new AuthManagementImpl(application);
}
}
#Module(library = true)
class ApplicationModule {
private final Application application;
public ApplicationModule(Application application) {
this.application = application;
}
#Provides
#Singleton
Application application() {
return application;
}
}
Then create your graph like so:
public class MyApplication extends Application{
private ObjectGraph graph;
#Override
public void onCreate() {
super.onCreate();
// AuthManagementModul is automatically included because it has a default
// constructor and is included by EntryPointsModule
graph = ObjectGraph.create(new EntryPointsModule(), new ApplicationModule(this));
}
public void inject(Object object) {
graph.inject(object);
}
}
There are other ways to structure this - you could just have ApplicationModule include the AuthModule and declare injects, so you only have two modules, etc. I suggested this way because ApplicationModule is then a separate concern whose only role is to hoist the Application instance into the graph, AuthManagementModul is exclusively there to support the auth function, and EntryPointsModule is there to be the front of the whole graph.
If you migrate to Dagger2, this structure is also convenient in that EntryPointsModule naturally converts to a #Component.
I'm starting using the dagger, like it pretty much, but now facing some difficulties. My scenario is as follows: there's an activity and a dependency for it. Dependency is injected to the activity, and requires a reference to that activity. Just like this:
public class MainActivity extends BaseActivity {
#Inject ScbeHelper scbeHelper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
}
}
public class ScbeHelper {
protected static final String TAG = "scbe_helper";
private BaseActivity activityContext;
#Inject
public ScbeHelper(BaseActivity context) {
this.activityContext = context;
}
}
I'm following dagger's example from the github for activity's graphs. So I've created a similar structure in my project. First, the BaseActivity class, from which MainActivity is inherited:
public abstract class BaseActivity extends Activity {
private ObjectGraph activityGraph;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
protoApp application = (protoApp) getApplication();
// Exception happens in next line, inside plus() method
activityGraph = application.getApplicationGraph().plus(getModules().toArray());
// Inject ourselves so subclasses will have dependencies fulfilled when this method returns.
activityGraph.inject(this);
((protoApp)getApplication()).inject(this);
}
protected List<Object> getModules() {
return Arrays.<Object>asList(new ActivityModule(this));
}
public void inject(Object object) {
activityGraph.inject(object);
}
}
And the module:
#Module(injects={MainActivity.class})
public class ActivityModule {
private final BaseActivity activity;
public ActivityModule(BaseActivity activity) {
this.activity = activity;
}
#Provides #Singleton BaseActivity provideActivity() {
return activity;
}
}
Now, the problem: No injectable members on com.example.proto.BaseActivity. Do you want to add an injectable constructor? required by public com.example.proto.ScbeHelper(com.example.proto.BaseActivity)
In other words, provider method ActivityModule.provideActivity() doesn't really provide the instance of BaseActivity for some reason, though in my understanding it's set up correctly. Does anyone see an error in my setup? Am I missing something in dagger's logic?
Thanks in advance!
I'm no Dagger expert, but you have 2 issues:
you have a cyclical dependency: you helper want to have the activity injected, your activity wants to have the helper injected. I don't think Dagger can resolve this
your activity tries to get injected twice, once with the activity-level graph, once with the application-level graph
Here's what I did to get it to work:
in ScbeHelper: remove the #Inject annotation
in BaseActivity: remove ((protoApp)getApplication()).inject(this);
in ActivityModule: remove your provideActivity method (it's not going to be used anymore), and add the following method:
#Provides #Singleton ScbeHelper provideScbeHelper() {
return new ScbeHelper(activity);
}
What this does is it provides your ScbeHelper with the context it needs, but leaves only 1 annotation-driven injection so dagger can resolve it. Hope this helps.