Dagger2 - "Unused" Modules in Generated Component Class - android

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);
}

Related

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);

android ObjectGraph in new version of Dagger2

i'm trying to update this below code to dagger2, but i get error for ObjectGraph:
import dagger.ObjectGraph;
public class App extends Application {
private static App instance;
private ObjectGraph objectGraph;
public App() {
instance = this;
}
#Override
public void onCreate() {
super.onCreate();
objectGraph = ObjectGraph.create(new AppModule());
}
public static void injectMembers(Object object) {
getInstance().objectGraph.inject(object);
}
public static <T>T get(Class<T> klass) {
return getInstance().objectGraph.get(klass);
}
public static App getInstance() {
return instance;
}
}
how can i update that to and which class must be use instead of ObjectGraph?
injectMembers used in this class
public class MyJobManager extends JobManager {
public MyJobManager(Context context) {
super(context, new Configuration.Builder(context)
.injector(new DependencyInjector() {
#Override
public void inject(BaseJob baseJob) {
App.injectMembers(baseJob);
}
})
.build());
}
}
now how can i inject with component?
my component:
#ActivitiesScope
#Component(dependencies = GithubApplicationComponent.class)
public interface ApplicationComponent {
void inject(ActivityRegister activityRegister);
void inject(ActivityStartUpApplication activityStartUpApplication);
void inject(GetLatestRepositories getLatestRepositories);
}
Dagger 2 doesn't use an ObjectGraph. It doesn't use anything as its replacement. Dagger1 did injection at runtime via reflection and used the ObjectGraph to provide that functionality. Dagger 2 does injection at compile time, thus it doesn't need a runtime object to represent the graph. Instead you'd want to build a component that links the modules you with to provide. You can then inject using that component.
See https://google.github.io/dagger/dagger-1-migration.html for more details.

How can "ask, don't look" be applied in Android?

This Google Testing Blog post lists some strategies for making code testable. One item says in part:
Ask for things, Don't look for things (aka Dependency Injection / Law of Demeter): OK, you got rid of your new operators in you application code. But how do I get a hold of the dependencies. Simple: Just ask for all of the collaborators you need in your constructor.
In other words, do this:
Foo(final Bar bar) {
mBar = bar;
}
Not this:
Foo() {
mBar = Bar.getBar(); // or new Bar();
}
The reason for this is obvious: it lets you test Foo by passing it a mock Bar. Since Android components require no-arg constructors, the equivalent is to pass their arguments via an extras Bundle.
How do you apply this principle in Android when the things the component needs are not Parcelable or Serializable?
What I use for that is Dagger2, where you only depend on the object graph (or one of its subscoped extended subgraphs) to receive all your dependencies.
Vaguely it works like this,
Singleton
.
#Component(modules={SingletonModule.class})
#Singleton
public interface SingletonComponent {
Foo foo();
Bar bar();
void inject(MainActivity mainActivity);
}
#Module
public class SingletonModule {
#Provides
#Singleton
public Bar bar() {
return new Bar();
}
#Provides
#Singleton
public Foo foo(Bar bar) {
return new Foo(bar);
}
}
public class CustomApplication extends Application {
SingletonComponent singletonComponent;
#Override
public void onCreate() {
super.onCreate();
singletonComponent = DaggerSingletonComponent.builder()
.singletonModule(new SingletonModule())
.build();
}
public SingletonComponent getSingletonComponent() {
return singletonComponent;
}
}
public class MainActivity extends Activity {
#Inject
Foo foo;
#Inject
Bar bar;
#Override
public void onCreate(Bundle saveinstanceState) {
super.onCreate(saveinstanceState);
((CustomApplication)getApplicationContext()).getSingletonComponent().inject(this);
bar.doSomething();
foo.doSomething();
}
}
Subscoping
.
#Component(modules=SingletonModule.class)
#Singleton
public interface SingletonComponent {
Foo foo();
}
#Component(dependencies={SingletonComponent.class}, modules={MainActivityModule.class})
#ActivityScope
public interface MainActivityCompoent extends SingletonComponent {
Bar bar();
void inject(MainActivity mainActivity);
}
#Module
public class SingletonModule {
#Provides
#Singleton
public Foo foo() {
return new Foo();
}
}
#Module
public class MainActivityModule {
#Provides
#ActivityScope
public Bar bar(Foo foo) {
return new Bar(foo);
}
}
#Scope
#Retention(RetentionPolicy.RUNTIME)
public #interface ActivityScope {
}
public class CustomApplication extends Application {
SingletonComponent singletonComponent;
#Override
public void onCreate() {
super.onCreate();
singletonComponent = DaggerSingletonComponent.builder()
.singletonModule(new SingletonModule())
.build();
}
public SingletonComponent getSingletonComponent() {
return singletonComponent;
}
}
public class MainActivity extends Activity {
#Inject
Foo foo;
#Inject
Bar bar;
private MainActivityComponent mainActivityComponent;
#Override
public void onCreate(Bundle saveinstanceState) {
super.onCreate(saveinstanceState);
mainActivityComponent = DaggerMainActivityComponent.builder()
.singletonComponent(((CustomApplication)getApplicationContext()).getSingletonComponent())
.mainActivityModule(new MainActivityModule())
.build();
mainActivityComponent.inject(this);
bar.doSomething();
foo.doSomething();
}
}

Android IOC Dagger Framework - How to inject a nested field ?

I'm using Dagger for Android for dependency injections.
I have a UserService object in a Main Class:
public class Main implements Runnable {
#Inject
UserService service;
#Override
public void run() {
for (User f : service.getUserByName("toto")) {
System.out.print(f.getM_Nom());
}
}
public static void main(String[] args) {
ObjectGraph objectGraph = ObjectGraph.create(new UserModule());
Main m = objectGraph.get(Main.class);
m.run();
}
}
I managed to inject the "service" field and to call the method "getUserByName("")".
But in my "UserService", I have an other custom object ("RepositoryUser" class):
public class UserService implements IUserService {
#Inject
RepositoryUser m_Repository;
#Override
public List<User> getUserByName(String name) {
return m_Repository.getAll();
}
}
My problem is that this field is not inject: the "m_Repository" field is null and I get a null pointer exception when I try to use my RepositoryUser object.
Here is my Provider:
#Module(
injects = {UserService.class, Main.class, RepositoryUser.class}
)
public class UserModule {
#Provides
RepositoryUser provideRepositoryUser() {
return new RepositoryUser();
}
#Provides
UserService provideUserService() {
return new UserService();
}
}
Any idea ?
Thanks in advance !
It is preferrable to use Constructor Injection in this case. This can be achieved as follows:
Main:
public class Main implements Runnable {
private final IUserService service;
#Inject
public Main(IUserService service) {
this.service = service;
}
#Override
public void run() {
for (User f : service.getUserByName("toto")) {
System.out.print(f.getM_Nom());
}
}
public static void main(String[] args) {
ObjectGraph objectGraph = ObjectGraph.create(new UserModule());
Main m = objectGraph.get(Main.class);
m.run();
}
}
UserService:
public class UserService implements IUserService {
private final RepositoryUser m_Repository;
#Inject
public UserService(RepositoryUser repository) {
m_Repository = repository;
}
#Override
public List<User> getUserByName(String name) {
return m_Repository.getAll();
}
}
RepositoryUser:
public class RepositoryUser {
#Inject
public RepositoryUser() {
}
/* ... */
}
UserModule:
#Module(injects = Main.class)
public class UserModule {
#Provides
IUserService provideIUserService(UserService userService){
return userService;
}
}
Everywhere the #Inject annotation is present on a constructor, Dagger can automatically create an instance of that item. So when you request a RepositoryUser instance in the UserService constructor, Dagger will see the #Inject annotation on RepositoryUser's constructor, and use that to create a new instance. We do not need an #Provides method here.
The IUserService parameter on the Main constructor cannot be instantiated, since it is an interface. Using the provideIUserService method in the module, we tell Dagger that we want it to create a new UserService instance.
We do have an #Inject annotation on the Main constructor, but we request it using ObjectGraph.get(Class<T> clzz). Therefore, we need to add injects = Main.class to our module.

No injectable members-error when using more than one dagger module in project

I would really like to implement Dagger in my app but I have a hard time to understand how it should be configured correctly.
I have two classes that I would like to inject.
public class LoginController {
#Inject Bus bus;
#Inject Context context;
// more code
}
public class LoginFragment {
#Inject Bus bus;
// more code
}
If I put all my code in one module it works fine:
#Module(injects = {LoginController.class, LoginFragment.class})
public class BasicModule {
#Provides
#Singleton
public Bus busProvider() {
return new Bus();
}
#Provides
public Context provideContext() {
return RblMobileApp.getContext();
}
}
But when I put those methods in two different modules (BasicModule and TestModule) I get compiler errors:
#Module(injects = {LoginController.class, LoginFragment.class})
public class BasicModule {
#Provides
#Singleton
public Bus busProvider() {
return new Bus();
}
}
Error:(18, 8) java: No injectable members on android.content.Context. Do you want to add an injectable constructor? required by rblmobile.controllers.LoginController for rblmobile.injection.BasicModule
#Module(injects = {LoginController.class})
public class TestModule {
#Provides
public Context provideContext() {
return RblMobileApp.getContext();
}
}
Error:(13, 8) java: No injectable members on com.squareup.otto.Bus. Do you want to add an injectable constructor? required by rblmobile.controllers.LoginController for rblmobile.injection.TestModule
So basically I'm told that BasicModule doesn't deliever Context and TestModule doesn't deliever Bus. Why does the ObjectGraph not know that the respective Constructor is in the other module?
Thanks
EDIT:
That's the method where the ObjectGraph gets created.
public static void init(final Object... modules) {
Log.i(TAG, "initializing object graph");
if (objectGraph == null) {
objectGraph = ObjectGraph.create(modules);
} else {
objectGraph = objectGraph.plus(modules);
}
}
There is a #Module annotation attribute called complete. Try to set it to false:
#Module(complete = false, injects = {LoginController.class, LoginFragment.class})
public class BasicModule {
#Provides
#Singleton
public Bus busProvider() {
return new Bus();
}
}
#Module(complete = false, injects = {LoginController.class})
public class TestModule {
#Provides
public Context provideContext() {
return RblMobileApp.getContext();
}
}

Categories

Resources