Hi I've read through other posts but I am not being able to fix it. Basically my issue is that I call .inject and when I want to use the field it's still null.
I have this class:
public class Application extends Game implements IApplication {
#Inject IApplication app;
#Inject IRouter router;
public Application(IPlatformCode platformCode) {
}
#Override
public void create() {
initDagger();
System.out.println(app); //NULL
System.out.println(router); //NULL
router.showPage(Page.MenuPage); //NULL EXCEPTION
}
#Override
public void render() {
Gdx.gl.glClearColor(1, 0.5f, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
super.render();
}
#Override
public void setPage(IPage page) {
setScreen(page);
}
protected void initDagger() {
ApplicationComponent.Initializer.init(this).inject(this);
RouterComponent.Initializer.init().inject(this);
}
}
I won't show the router because I'm doing the same in app.
My Application component looks like this:
#Singleton
#Component ( modules = {ApplicationModule.class })
public interface ApplicationComponent {
void inject(IApplication application);
IApplication getApplication();
static final class Initializer {
private Initializer(){}
public static ApplicationComponent init(IApplication app) {
return DaggerApplicationComponent
.builder()
.applicationModule(new ApplicationModule(app))
.build();
}
}
}
And this is the module:
#Module
public class ApplicationModule {
private IApplication app;
public ApplicationModule(IApplication app) {
this.app = app;
}
#Provides
#Singleton
public IApplication providesApplication(){
return app;
}
}
As far as I read after calling inject(IApplication) the #Inject IApplication should be injected and have a value but right now it's null.
The generated code looks like this:
public final class DaggerApplicationComponent implements ApplicationComponent {
private Provider<IApplication> providesApplicationProvider;
private DaggerApplicationComponent(DaggerApplicationComponent.Builder builder) {
assert builder != null;
this.initialize(builder);
}
public static DaggerApplicationComponent.Builder builder() {
return new DaggerApplicationComponent.Builder();
}
private void initialize(DaggerApplicationComponent.Builder builder) {
this.providesApplicationProvider = DoubleCheck.provider(ApplicationModule_ProvidesApplicationFactory.create(builder.applicationModule));
}
public void inject(IApplication application) {
MembersInjectors.noOp().injectMembers(application);
}
public IApplication getApplication() {
return (IApplication)this.providesApplicationProvider.get();
}
public static final class Builder {
private ApplicationModule applicationModule;
private Builder() {
}
public ApplicationComponent build() {
if(this.applicationModule == null) {
throw new IllegalStateException(ApplicationModule.class.getCanonicalName() + " must be set");
} else {
return new DaggerApplicationComponent(this);
}
}
public DaggerApplicationComponent.Builder applicationModule(ApplicationModule applicationModule) {
this.applicationModule = (ApplicationModule)Preconditions.checkNotNull(applicationModule);
return this;
}
}
}
Thanks in advance.
Your inject method
void inject(IApplication application);
needs to change to
void inject(Application application);
Note the change from IApplication to just Application. You can't use interfaces for inject methods, you need to use a class.
Typically, Dagger 2 component creation is done in a class extending one of Android's Application classes. This is primarily done to ensure that these components (and the dependencies they house) are only instantiated once. (see Dagger docs for more details: https://google.github.io/dagger/users-guide)
While I haven't seen anything that says you can't wire it up differently, I haven't found an example of it, either. I'm wondering if trying to wire the components on-the-fly with the Initializer.init() calls you're making is somehow bypassing Dagger's ability to setup Components for injection correctly. Would it be possible to refactor the instantiation of the Component classes into an Application implementation instead?
The code above looks like it should work (outside of getting a new DaggerApplicationComponent instance with every init() call instead of a Singleton), I can't really explain why it doesn't.
Related
i have a problem with Singleton with the Dagger2 library for Android.
My problem is im using the #Singleton but getting two different objects =[
i Have 2 Components and 2 Modules:
DispatcherComponent which includes the DispatcherModule class that provides a Dispatcher.
the Dispatcher needs instance UserStore which is provided by StoreModule.
#Singleton
#Component(modules={AppModule.class, StoreModule.class, DispatcherModule.class})
public interface DispatcherComponent {
void inject(SomeClass someClass);
}
and the DispatcherModule.class is as follows
#Module
public class DispatcherModule {
#Provides
#Singleton
public Dispatcher provideDispatcher(UserStore store) {
Log.d("DEBUG", "userStore : " + store.toString());
return new Dispatcher(store);
}
and now the StoreComponent.class
#Singleton
#Component(modules={AppModule.class, StoreModule.class})
public interface StoreComponent {
void inject(SomeOtherClass otherClass);
}
and StoreModule.class
#Module
public class StoreModule {
#Provides
#Singleton
public UserStore provideUserStore() {
return new UserStore();
}
now when im trying to inject UserStore im getting two different objects =/
public class SomeOtherClass extends Acitivity {
#Inject UserStore mStore;
public void onCreate(Bundle savedInstance) {
StoreComponent comp = ((MyApp) getApplication).getStoreComponent();
comp.inject(this);
Log.d("DEBUG", "userStore2 :" + mStore.toString());
}
}
public class SomeClass {
#Inject Dispatcher mDispatcher;
public SomeClass (Application application) {
((MyApp) application).getDispatcherComponent().inject(this);
}
and last, this is how i create the components:
public class MyApp extends Application {
public void onCreate() {
StoreModule store = new StoreModule();
StoreComponent storeComponent = DaggerStoreComponent.builder().appModule(new AppModule(this)).storeModule(storeModule).build();
DispatcherComponent disComp = DaggerDispatcherComponent.builder().appModule(new AppModule(this)).storeModule(storeModule).dispatcherModule(new DispatcherModule()).build();
}
now, when im running the Application, i get 2 different objects ! can someone help me ? how should i fix it? i dont want to have a god component..
THanks!
Note that #Singleton dost not make the object to be singleton actually, instead, it just use the DoubleCheck class to cache which is hold by the component impl generated by dagger. Check DaggerDispatcherComponent for more detail.
For this case, you can change StoreModule like below:
#Module
public class StoreModule {
private UserStore mStore;
#Provides
#Singleton
public UserStore provideUserStore() {
return getStore();
}
private UserStore getStore() {
//lazy initialized and make sure thread safe
if (null == mStore) {
#synchronized(StoreModule.class) {
if (null == mStore) {
mStore = new UserStore();
}
}
}
return mStore;
}
}
I have a module as follows.
#Module
public class AppModule {
private final Application app;
public AppModule(Application app) {
this.app = app;
}
#Provides
#Architecture.ApplicationContext
Context provideContext() {
return app;
}
#Provides //scope is not necessary for parameters stored within the module
public Context context() {
return provideContext();
}
#Singleton
#Provides
Application provideApp() {
return app;
}
#Singleton
#Provides
SoundsRepository provideSoundsRepository(Context context, SoundsDAO soundsDAO) {
return new SoundsRepository(context, soundsDAO);
}
}
A component like this.
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
void inject(Global global);
void inject(MainActivity mainActivity);
#Architecture.ApplicationContext
Context getContext();
Application getApplication();
void inject(PostView postView);
void inject(MediaPlayerService mediaPlayerService);
}
In activity, fragment or service, I do this
#Inject
SoundsRepository soundsRepository;
#Override
protected void onCreate(...) {
//....
((Global) getApplication()).getComponent().inject(this);
}
In SoundsRepository
#Singleton
public class SoundsRepository {
#Inject
public SoundsRepository(Context context, SoundsDAO soundsDAO) {
this.context = context;
this.soundsDAO = soundsDAO;
System.out.println(TAG + "INIT");
}
// ....
}
So, now, every time I start to access an activity or service where SoundsRepository is injected, I get a new instance, I mean, the constructor of "SoundsRepository" fires again.
What am I doing wrong?
EDIT : Inject in Application Class
public class Global extends MultiDexApplication {
protected AppComponent appComponent;
private boolean calledAlready = false;
#Override
public void onCreate() {
super.onCreate();
//if (LeakCanary.isInAnalyzerProcess(this)) return;
//LeakCanary.install(this);
initFirebasePersistance();
appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
appComponent.inject(this);
FrescoUtil.init(getApplicationContext());
}
public AppComponent getComponent() {
return appComponent;
}
}
In your module you have a method that provides an instance of SoundsRepository - good
In your AppComponent you are missing a:
SoundsRepository soundsRepository();
In your Global which extends Application/MultidexApplication you create your DaggerAppComponent - good
In your other activities/fragments/services just call:
Global application = (Global) getApplication();
SoundsRepository sr = application.getComponent().soundsRepository()
Android guarantees you have only one instance of your Application (Global) class for all other actvities/services (its somewhat like a singleton).
So keep your component in that application class, and whenever you need your class, call: (YourApplication) getApplication().getComponent().yourSingleInstanceSomething();
I created and tested sample code for you: https://github.com/zakrzak/StackDaggerTest
Dagger's #Singleton is just a scope, and does not guarantee returning a singular instance of a class.
In my understanding, if you:
void inject(PostView postView);
you tell Dagger to make everything you annotated with #Provided in AppModule accessible in your PostView as soon as you request it with:
#Inject
SoundsRepository soundsRepository;
then dagger just calls the #provided method which in your case returns a new SoundRepository instance:
#Singleton
#Provides
SoundsRepository provideSoundsRepository(Context ........) {
return new SoundsRepository(...);
}
which causes your problem
I'm implimenting Dagger 2 in my Android app. I have it setup in the following way:
AppComponent.java
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AndroidSupportInjectionModule.class,
ActivityBuilder.class,
AppModule.class,
DataBaseDaoModule.class
})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application aApplication);
AppComponent build();
}
Application application();
void inject(MyApplication aApplication);
}
AppInjector.java
ublic class AppInjector {
public static void init(MyApplication aApplication) {
//Initialize dagger and inject the aApplication
DaggerAppComponent.builder().application(aApplication).build().inject(aApplication);
aApplication.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(Activity aActivity, Bundle aBundle) {
handleActivity(aActivity);
}
#Override
public void onActivityStarted(Activity aActivity) {
}
#Override
public void onActivityResumed(Activity aActivity) {
}
#Override
public void onActivityPaused(Activity aActivity) {
}
#Override
public void onActivityStopped(Activity aActivity) {
}
#Override
public void onActivitySaveInstanceState(Activity aActivity, Bundle aBundle) {
}
#Override
public void onActivityDestroyed(Activity aActivity) {
}
});
}
private static void handleActivity(Activity aActivity) {
if (aActivity instanceof HasActivityInjector) {
AndroidInjection.inject(aActivity);
Timber.d("injected Activity");
}
if (aActivity instanceof FragmentActivity) {
((FragmentActivity) aActivity).getSupportFragmentManager()
.registerFragmentLifecycleCallbacks(
new FragmentManager.FragmentLifecycleCallbacks() {
#Override
public void onFragmentCreated(FragmentManager fm, Fragment f,
Bundle savedInstanceState) {
if (f instanceof Injectable) {
Timber.d("injected Fragment");
AndroidSupportInjection.inject(f);
}
}
}, true);
}
}
}
AppModule.java
Module(includes = ViewModelModule.class)
class AppModule {
#Singleton
#Provides
ApiService providesApiService(OkHttpClient aClient, MyInterceptor aInterceptor) {
//Build a Retrofit object here
}
#Singleton
#Provides
OkHttpClient providesOkHTTPClient(MyInterceptor aInterceptor) {
//Setup OKHTTP here
}
}
And finally in MyApplication.Java in the onCreate method I just call the AppInjector like so: AppInjector.init(this);
All of this works and anything I put in my AppComponent's moduels, I can inject into Activities, Fragments and ViewModels.
However, I have cases where I would need a utility class, that depends on Application, for contex - and I use the utility class in various places. Or I will have a Manager class, that depends on Application, or needs something from AppModule. However, since I use these classes outside of Activities, Fragments and ViewModels I cannot just inject. How would I provide my utility classes with their dependencies and any other type of class - like a manager class?
My first thought was to create a UtilityComponent and a ManagerCompoent of sorts, however I have no idea how I would get them to work with anything in AppModuel or through my AppComponent.
Please don't just use component.inject(myObject) for everything. Always prefer constructor injection or provide it from a module where you can do additional setup steps. .inject(myObject) is intended for Framework components where you don't have access to the constructor.
My first thought was to create a UtilityComponent and a ManagerCompoent of sorts, however I have no idea how I would get them to work with anything in AppModuel or through my AppComponent.
You don't need a separate component for that. See below.
However, since I use these classes outside of Activities, Fragments and ViewModels I cannot just inject.
That has nothing to do with injection. You're talking about scopes, and it sound like your utilities are a #Singleton. Your AppComponent is a #Singleton scoped component, hence it can be used to provide your utils, too.
However, I have cases where I would need a utility class, that depends on Application, for context
If they are part of the #Singleton component, which has access to your Application, they can also be provided anywhere else. No need for more components or anything. Just declare your dependencies and don't overthink it.
Just declare your util, annotate it with #Singleton and mark the constructor with #Inject for constructor injection. #Singleton ensures that it will be provided by your AppComponent and can access the Application on which it depends.
#Singleton public class MyUtil {
private Application application;
#Inject public MyUtil(Application application) {
this.application = application;
}
}
And then you can just inject it in your Activities, Fragments, or even into other Utilities....
#Singleton public class MyUtilWrapper {
private MyUtil myUtil;
#Inject public MyUtilWrapper(MyUtil myUtil) {
this.myUtil = myUtil;
}
}
And you can inject either or both into your activity or fragment...
#Inject MyUtil myUtil;
#Inject MyUtilWrapper myUtilWrapper;
void onCreate(..) {
AndroidInjection.inject(this);
}
You do not need any modules, provides methods, or components to provide simple classes. Just make sure to add the right scope!
I am trying field injection with dagger although constructor injection is working absolutely fine but i don't know what is wrong with field injection. May be I am doing wrong. I am adding the code snippets. I am getting null pointer exception on engine.start() because the engine dependency is not fed. It is similar to A->B->C dependencies where A->B is fed but B->C is not. Its been long I am unable to resolve.
package com.raghav.java.car;
import javax.inject.Inject;
public class App
{
#Inject
Car car;
App() {
DaggerCarComponent.create().inject(this);
}
public static void main( String[] args )
{
App app = new App();
app.perform();
}
private void perform() {
car.run();
}
}
public interface Engine {
void start();
}
class FordEngine implements Engine {
public void start() {
System.out.println("Engine started -- Vroom Vroom");
}
}
public interface Car {
void run();
}
class MarutiCar implements Car {
#Inject
Engine engine;
public void run() {
engine.start();
System.out.println("WOW!! Maruti Running ");
}
}
#Singleton
#Component(modules = {CarModule.class})
public interface CarComponent {
void inject(App app);
}
#Module
class CarModule {
#Singleton
#Provides
public Car provideCar() {
return new MarutiCar();
}
#Singleton
#Provides
public Engine provideEngine() {
return new FordEngine();
}
}
if you want to do nested injection you need to use constructor injection otherwise that wont happen automatically because when you provide a dependency out of Dagger style (through constructor injection or argument injection in module) dagger has no idea about that. change your classes like below:
change your MarutiCar like this:
class MarutiCar implements Car {
Engine engine;
#Inject
public MarutiCar(Engine engine)
{
this.engine = engine;
}
public void run() {
engine.start();
System.out.println("WOW!! Maruti Running ");
}
}
and your FordEngine class like this:
class FordEngine implements Engine {
#inject
public FordEngine()
{
}
public void start() {
System.out.println("Engine started -- Vroom Vroom");
}
}
then change your CarModule like below:
#Module
class CarModule {
#Singleton
#Provides
public Car provideCar(MarutiCar marutiCar) {
return marutiCar;
}
#Singleton
#Provides
public Engine provideEngine(FordEngine fordEngine) {
return fordEngine;
}
}
UPDATE: DAGGER WONT INJECT FIELDS OF A CLASS THAT IS NOT CREATED BY ITSELF.
you are creating the MarutiCar instance by your self how you expect dagger to know what it needs ? you can start a new chain of dagger creation in that class to achieve this which is a weird thing to do.
you need to tell dagger what you need by showing dependencies through constructor or module method argument so that dagger instantiate them for you to know what they need. dagger doesnt provide your nested dependencies injected fields because it hasnt created them so it has no idea about their dependencies, unless you start another chain of dagger creation like what you did in your App class.
there is another way if you dont want to use constructor injection which i only show this for you Car and MarutiCar class:
#Module
class CarModule {
#Singleton
#Provides
public Car provideCar(Engine engine) {
MarutiCar marutiCar = new MarutiCar(engine);
}
#Singleton
#Provides
public Engine provideEngine(FordEngine fordEngine) {
return fordEngine;
}
}
and the MarutiCar class would be like this (no need for #inject)
class MarutiCar implements Car {
Engine engine;
public MarutiCar(Engine engine)
{
this.engine = engine;
}
public void run() {
engine.start();
System.out.println("WOW!! Maruti Running ");
}
}
I have 2 modules: AndroidModule for providing Context-related objects and PhotosModule providing PhotoManager.
I want to use PhotoManager in PhotosFragment. PhotoManager depends on DbManager, which is provided by AndroidModule.
How do I connect all the dots?
The code compiles, but a NPE is thrown on this line:
mDbManager.readDatabase();
meaning that the injection of DbManager into PhotoManager did not occur, even though I set the
injects = PhotoManager.class
in AndroidModule.
Also, mDbManager is not a private field, so I don't think I have to use injection on PhotoManager's constructor; field injection should be sufficient.
What's the reason and how do I satisfy the missing dependency? I assume there may be more than one problem with my code. Note: I just started with Dagger, please keep that in mind.
Application:
public class TestApp extends Application {
private ObjectGraph mObjectGraph;
#Override
public void onCreate() {
super.onCreate();
mObjectGraph = ObjectGraph.create(getModules().toArray());
}
protected List<Object> getModules() {
return Arrays.asList(new AndroidModule(this), new PhotosModule());
}
public void inject(Object object) {
mObjectGraph.inject(object);
}
}
AndroidModule:
#Module(library = true, injects = PhotoManager.class)
public class AndroidModule {
private final TestApp mApplication;
public AndroidModule(TestApp application) {
mApplication = application;
}
#Provides
#Singleton
#ForApplication
Context provideApplicationContext() {
return mApplication;
}
#Provides
#Singleton
DbManager provideDbManager() {
return new DbManager(mApplication);
}
}
PhotosModule:
#Module(injects = PhotosFragment.class)
public class PhotosModule {
#Provides
#Singleton
PhotoManager providePhotoManager() {
return new PhotoManager();
}
}
PhotoManager:
#Singleton
public class PhotoManager {
#Inject
DbManager mDbManager;
public void doSomething() {
mDbManager.readDatabase();
}
}
PhotosFragment:
public class PhotosFragment extends Fragment {
#Inject
PhotoManager mPhotoManager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((TestApp) getActivity().getApplication()).inject(this);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
///...
mPhotoManager.doSomething();
}
}
DbManager:
#Singleton
public class DbManager {
public DbManager(Context context) {
//...
}
public void readDatabase() {
//...
}
}
Here's the core of the problem: in PhotosModule, you have the provider method:
PhotoManager providePhotoManager() {
return new PhotoManager();
}
You are newing up the PhotoManager yourself, so Dagger doesn't have an opportunity to inject its dependency (the DbManager).
I think it's because you're misunderstanding the meaning of the injects property on the #Module(injects=...) annotation of the AndroidModule. You have:
#Module(library = true, injects = PhotoManager.class)
But that injects isn't needed, because you're never calling objectGraph.inject on the PhotoManager. Instead, you're only injecting the PhotoFragment, which is correct...
So one way to fix the NPE problem:
remove the injects=PhotoManager.class from AndroidModule.java:
#Module(library = true)
change PhotosModule to include AndroidModule:
#Module(injects = PhotosFragment.class, includes = AndroidModule.class)
I avoid field injection using the #Inject annotation (only use it for the top-level objects, i.e. where you do the objectgraph.inject, such as activity and fragment level), and use constructor injection instead for all the other dependencies. So I'd change PhotoManager to inject the DbManager like this:
private final DbManager mDbManager;
public PhotoManager(DbManager dbManager) {
mDbManager = dbManager;
}
I've put up the code here on GitHub.