Dagger 2 - Singleton class created twice - android

I have singleton class that 2 other classes depend on: another singleton and a service. The problem is that in practice each of them receives a different instance of what's supposed to be a singleton. I'm trying to figure out why. Here's the gist of it:
The singleton dependency class:
#Singleton
public class SingletonDependency {
#Inject
public SingletonDependency() {
// ...
}
}
The other singleton which depends on it:
#Singleton
public class DependentClass {
#Inject
public DependentClass(SingletonDependency singletonDependency) {
// ...
}
}
The service which depends on it:
public class DependentService extends Service {
#Inject SingletonDependency mSingletonDependency;
#Override
public void onCreate() {
CoreInjector.INSTANCE.getCoreComponent().inject(this);
}
}
I do not provide SingletonDependency explicitly since it has a #Inject constructor and Dagger2 takes care of it for me. Is this the root of it?
Edit:
Here's my injector:
public enum CoreInjector {
INSTANCE;
private CoreComponent mCoreComponent;
public void init(Context context) { // called by the Application's onCreate()
mCoreComponent = DaggerCoreComponent.builder()
.coreModule(new CoreModule(context))
.build();
}
public CoreComponent getCoreComponent() {
return mCoreComponent;
}
}
The Component itself:
#Singleton
#Component(modules = {CoreModule.class})
public interface CoreComponent {
void inject(DependentService dependentService);
}
The Module itself doesn't provide the dependency explicitly, but once I do, everything works as expected - same instance.
Edit 2
Conclusions posted as an answer.

I figured it out. My singleton class implements an interface, which is what my dependent classes depends on - not the implementation. I guess Dagger2 can't make that leap even though the implementation has a injected constructor.
Explicit #Provides method in a module was the only way to go.

Another possible cause of a singleton created many times is that #Singleton is enforced only by #Component. If a singleton is called by another #Component (or another instance of the same component), it will be re-created.

Related

Dagger 2 how to access same component everywhere

How do I create a singleton object that I can access anywhere from any place in my code?
Objects(1) from which I want to inject my signleton component can't be created withing dagger.
Those objects don't have common dependencies which I can access (like getApplication()
Quick to an example:
InjectedClass:
public class InjectedClass {
public InjectedClass() {
System.out.println("injected class created");
}
}
HolderClassA:
public class HolderClassA { // this is one of object I marked with (1)
#Inject InjectedClass b;
public HolderClassA() {
Injector build = DaggerInjector.builder().build();
build.inject(this);
System.out.println(b);
}
}
HolderClassB:
public class HolderClassB { // this is another object I marked with (1)
#Inject InjectedClass b;
public HolderClassB() {
Injector build = DaggerInjector.builder().build();
build.inject(this);
System.out.println(b);
}
}
Provider:
#Module
public class Provider {
#Provides
#Singleton
InjectedClass provideInjectedClass() {
return new InjectedClass();
}
}
Injector:
#Component(modules = {Provider.class})
#Singleton
public interface Injector {
void inject(HolderClassA a);
void inject(HolderClassB a);
}
Somewhere in code:
new HolderClassA();
Somewhere else in code that is NO WAY related to previous code, nor has the same parent or can access same objects (like in dagger guide with getApplication()):
new HolderClassB();
Actual result: two instances of InjectedClass are crated
Expected result: a single instance of InjectedClass is created.
The issue is DaggerInjector.builder().build(); creates different scopes which don't know about each other. For example, I can solve this issue by creating static variable DaggerInjector.builder().build() and call inject from it. But thus it wouldn't be dependency injection anymore, but rather not trivial singleton pattern.
From what I understand from comments, you want separate components for your holder classes, that would inject same instance? Pardon me if I am wrong.
You can try this.
#Component(modules = arrayOf(AppModule::class))
#Singleton
interface AppComponent {
//sub components
fun plusHolderClass1(holderModule: HolderModule1):HolderComponent1
fun plusHolderClass2(holderModule: HolderModule2):HolderComponent2
//provision methods
fun getInjectedClass():InjectedClass
}
This is your application component, or the top level component, that you initialise in your application, of course using your Module class that will provide the Injected class as a singleton.
#Subcomponent(modules = arrayOf(HolderModule1::class))
#ActivityScope
interface HolderComponent1{
fun inject(holder:Holder1)
}
And a similar one for Holder2 class. You can define your local scope dependencies in the modules.
But of course even in this case you have to store the instance of appComponent in Application class.
While injecting
appComponent.plusHolderComponent1(HolderModule1()).inject(yourObject)
The InjectedClass object will be injected to yourObject by fetching it from provision methods

Dagger : Why does dagger require a #inject constructor for an object that does't depend on another object

I think I'm missing something. I get this error:
PostsVM cannot be provided without an #Inject constructor or from an
#Provides-annotated method.
Assume classes as follows :
#Module
public class AppModule {
private final Application mApplication;
#Singleton
#Provides
ViewModel provideListViewModel() {
return new PostsVM();
}
}
And a class PostVM
#Singleton
public class PostsVM extends ViewModel {
public boolean get(){
return true;
}
}
And a component :
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
void inject(Global global);
void inject(MainActivity mainActivity);
#Architecture.ApplicationContext
Context getContext();
Application getApplication();
}
And in activity :
#Inject
public ViewModelProvider.Factory factory;
#Override
protected void onCreate(Bundle savedInstanceState) {
InjectorClass.inject(this);
As you can see, the example given for PostVM class, does't depend on anything, why do I need an #inject constructor in it?
tl;dr To prevent errors and to follow convention.
From the JavaDoc of #Inject you can read:
Injectable constructors are annotated with #Inject and accept zero or more dependencies as arguments. #Inject can apply to at most one constructor per class.
And it's always good practice to follow the convention / documentation.
So #Inject marks an entry point for Dagger to signal it how and where to create your class. It is a clear sign of how you intend your class to be used.
What if you have multiple constructors?
What if you require additional setup and should use a #Module instead?
By just defaulting to a no-args constructor (if possible) things could start breaking very easily and you might not be able to pinpoint the source easily if you just assume Dagger does its job.
__ cannot be provided without an #Inject constructor or from an #Provides-annotated method.
This error on the other hand gives you a strong signal that you're missing something and cannot be ignored.

dagger2 constructor injection how to provide dependency without module

I've read that constructor injections don't require a module.
So I have this questions.
If I have this constructor injection:
private Model realmModel;
#Inject
public MainActivityPresenter(Model realmModel) {
this.realmModel = realmModel;
}
and this component:
#Singleton
#Component(modules = {AppModule.class})
public interface AppComponent {
Model realmModel();
void inject(MainActivity activity);
}
if in my MainActivity I do it:
((MyApp)getApplication()).createAppComponent().inject(this);
how could I pass the realmModel parameter to the presenter constructor injection?
EDIT: this is the model:
Presenter presenter;
#Inject
public RealmModel(Presenter presenter) {
this.presenter = presenter;
}
Thanks
Three ways to solve this problem
1) Give a module which does the provide of the Relam Model
#Provides
#Singleton
public Model providesRealmModel() {
return new Model();
}
2) Make your RelamModel class also constructor injected
class Model {
#Inject
public Model() {}
}
The Trick in construction injection is all its dependencies should also be constuctor injeceted then it would work fine.
(From experience your model would need application context. look at the 3 option for ways to implement external Dependencies
3) Provide Model as external dependency.
#Module
public class ModelModule {
private Model relamModel;
public ModelModule(Model relamModel) {
this.relamModel = relamModel
}
}
#Component(module={ModelModule.class})
public interface ApplicationComponent {
}
Take a look at the series of videos from twisted eqautions these were my first video tutorial on dagger2. I found it really helpful. Hope it helps you too
https://www.youtube.com/watch?v=Qwk7ESmaCq0
You have two choices:
use a module with a provide method
annotate the constructor of Model with #Inject, doing that when you pass realmModel in the presenter contructor, the model
constructor will be called.
I prefer to use modules, but that's just my opinion.

Dagger - Getting Same Instance On Different Component

I'm having a similar problem like the one in this question.
While the accepted answer does help, but I'm missing final piece to solve the problem.
I have 2 android library modules: common and exp which depends on common.
Everything under common:
#Module
public class CommonModule {
#Singleton
#Provides
public Repository providesRepository() {
return new Repository();
}
}
#Singleton
#Component(modules={CommonModule.class})
public interface CommonComponent {
void inject(CommonClass commonClass);
/**
CommonClass needs instance of Repository
**/
}
public class CommonDIHolder {
public static CommonComponent sComponent;
public static void init() {
sComponent = DaggerCommonComponent.builder().build();
}
}
Everything under exp:
#Module(includes={CommonModule.class})
public class ExpModule {
#Singleton
#Provides
public ExpResource provideExpResource() {
return new ExpResource();
}
}
#Singleton
#Component(modules={ExpModule.class}, dependencies={CommonComponent.class})
public interface ExpComponent {
void inject(ExpClass expClass);
/**
ExpClass needs instance of Repository and ExpResource
**/
}
public class ExpDIHolder {
public static ExpComponent sComponent;
public static void init() {
sComponent = DaggerExpComponent.builder()
.commonComponent(CommonDIHolder.sComponent)
.build();
}
}
I need both CommonClass and ExpClass receive the same instance of Repository.
The problem with this approach is that #Singleton can't depends on #Singleton. So I have to change the scope of ExpComponent into self-defined scope called #ExpScope. Then I changed the provideExpResource into #ExpScope as well.
Then I encountered an error saying that ExpComponent may not reference bindings with different scopes. It refers to the provideRepository which has different scope (#Singleton) on it. If I changed the scope into ExpScope then the CommonComponent will have different scope with provideRepository.
If I changed all #Singleton into #ExpScope then I receive this error message: depends on scoped components in a non-hierarchical scope ordering
What should I do? Or I'm doing the wrong approach here?
Use one and only one #Singleton scoped component
You should have one and only one #Singleton scoped component like this:
#Singleton
#Component(modules={CommonModule.class, ExpModule.class})
public interface CommonComponent {
}
Only specify Activities, Fragments, and Services as explicit injection targets for Components
In an Android app, you should only list Activities, Fragments and Services as injection sites. You should configure Dagger 2 to inject the rest of your dependencies without having to resort to calling component.inject(this) inside them.
For example, if your CommonClass looks like this:
public class CommonClass {
#Inject Repository repository;
public class CommonClass() {
CommonComponentHolder.get().inject(this);
}
}
Refactor it like this:
public class CommonClass {
private final Repository repository;
#Inject
public class CommonClass(Repository repository) {
this.repository = repository;
}
}
Now when you have an Activity or Fragment that needs CommonClass and you are injecting with CommonComponent or one of its sub-components or dependent components, they can obtain instances of CommonClass wired with the correct dependencies:
public class MyActivity extends AppCompatActivity {
#Inject CommonClass commonClass;
public void onCreate(Bundle savedInstanceState) {
CommonComponentHolder.getComponent().inject(this);
}
}
Use subcomponents or dependent components to specify the injection targets
Now you have a #Singleton scoped component, you'll probably want to create a component for a narrower scope for your Activity or Fragment. You'll have to connect it to your CommonComponent, so use dependent components or subcomponents (subcomponents are preferred as of Dagger 2.10). Since you say you have already tried defining a #ExpScope, I think the missing piece is to make subcomponent or dependent component with the #ExpScope that injects your Activity or Fragment.
Something like the following for the top-level singleton component:
#Singleton
#Component(modules={CommonModule.class, ExpModule.class})
public interface CommonComponent {
ExpComponent.Builder exComponent();
}
And then for the subcomponent:
#ExpScope
#Subcomponent(modules = {NarrowerScopedModule.class})
public interface ExpComponent {
#Subcomponent.Builder
public interface Builder {
Builder narrowerScopedModule(NarrowerScopedModule narrowerScopedModule);
ExpComponent build();
}
}
There are good working examples of Android projects in the Google Android Architecture Blueprints Github repo

Dagger - nested injections, is it necessary to call inject()?

I'm new to Dagger and at the begininig I face some issues. I have simple structure so far in my project. My injection module:
#Module(
injects = {GameBoardFragment.class, GameManager.class},
complete = false,
library = true
)
public class GameObjectsProviderModule {
private final Application mApplication;
public GameObjectsProviderModule(Application application){
this.mApplication = application;
}
#Provides
#Singleton
public GameManager provideGameManager(){
return new GameManager();
}
#Provides
public Board getBoard(){
return new Board();
}
#Provides #Singleton #ForApplication Context provideAppContext() {
return mApplication;
}
My simplified custom app class looks like that:
public class MyApp extends Application {
private static ObjectGraph mApplicationGraph;
#Override public void onCreate() {
super.onCreate();
mApplicationGraph = ObjectGraph.create(new GameObjectsProviderModule(this));
}
public static ObjectGraph getObjectGraph(){
return mApplicationGraph;
}
}
And now, my fragment looks like that:
public class GameBoardFragment extends Fragment {
#Inject
GameManager mGameManager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
MyApp.getObjectGraph().inject(this);
View root = inflater.inflate(R.layout.fragment_game_board, container, false);
findViews(root);
confViews();
return root;
}
}
And finally my GameManager class
public class GameManager {
#Inject Board mBoard;
public GameManager(){
MyApp.getObjectGraph().inject(this);
}
}
Andy hey, it works! Great. But my question is why it doesn't work in case I comment out this line:
MyApp.getObjectGraph().inject(this);
Do we have always explicitly call inject() function to make all necessary injections take place event in nested objects?
It looks not, as shows coffe maker example:
https://github.com/square/dagger/tree/master/examples/simple/src/main/java/coffee
Why then I have to call inject() in GameManager class to get it working?
Edit:
The consturctor injection approach works just fine.
But for future use I tried to get field injection running, and so far I haven't succeed.
I commented out both #Provide methods from module and I made my GameManager look like this:
#Singleton
public class GameManager {
#Inject Board mBoard;
#Inject
public GameManager(){
}
}
and Board:
public class Board {
#Inject
public Board() {
}
}
However mBoard doesn't get instantiated. I will try more and I suppose I figure out the proper solution.
You should rather use constructor injection (like for example the Thermosiphon does), and avoid field injection unless necessary. For example, let your GameManager have the Board as a constructor argument:
#Singleton
public class GameManager {
private final Board mBoard;
#Inject
public GameManager(final Board board){
mBoard = board;
}
}
Dagger will use this constructor to create an instance of the GameManager (hence the #Inject annotation), and notice it needs a Board instance. Using the ObjectGraph, it will create a Board first, and use that instance to create the GameManager. You can remove the #Provides GameManager method if you do it this way.
In your case, you have a #Provides Board method in your module. If you add an #Inject annotation to your Board constructor, you can remove this provides-method from your module:
public class Board {
#Inject
public Board() {
}
}
If you don't want to use constructor injection, the problem is that you told Dagger that you want to create your GameManager instance yourself (because you have the #Provides GameManager method). If you remove this method, and let Dagger create it for you like above but without the Board parameter in the constructor, Dagger will also notice the #Inject Board field and inject that as well.
A final remark. Remove the library = true and complete = false statements! These are not necessary at all in this example. Only add them if you really know what you're doing. By not having them, Dagger will create compile-time errors to notify you that something is wrong. If you do include them, you're telling Dagger "Hey, I know what I'm doing, don't worry, it's all correct", when in fact it isn't.
Edit
A quote from the Dagger1 site:
If your class has #Inject-annotated fields but no #Inject-annotated
constructor, Dagger will use a no-argument constructor if it exists.
Classes that lack #Inject annotations cannot be constructed by Dagger.
I do not use this method very often, so I could be wrong. I think this means that you should remove the #Inject annotation from your constructor, like so:
#Singleton
public class GameManager {
#Inject Board mBoard;
public GameManager(){ // Or remove the constructor entirely since it's empty
}
}
Since there is an #Inject annotation on the Board field, Dagger will know to use the no-argument constructor.
I was struggling with the same issue as most of the dagger examples everywhere use a Module with Provides and I had a hard time finding a complete example that just does not use Provides.
I created this one. It uses field injection (not constructor injection) and works just fine through the hierarchy without requiring any call to inject. I am using Dagger 1.2.2.
Main.java
import javax.inject.*;
import dagger.*;
import dagger.ObjectGraph;
public class Main {
public static void main(String[] args) {
ObjectGraph objectGraph = ObjectGraph.create(new CarModule());
Car car = objectGraph.get(Car.class);
car.start();
}
}
CarModule.Java
import dagger.Module;
#Module(injects = Car.class)
public class CarModule {
}
Car.Java
import javax.inject.*;
public class Car {
#Inject public Engine engine;
#Inject Car() {
System.out.println("Car constructor");
}
public void start() {
engine.start();
}
}
Engine.Java
import javax.inject.*;
public class Engine {
#Inject WaterPump waterPump;
Engine() {
System.out.println("Engine Constructor");
}
void start() {
waterPump.run();
System.out.println("starting engine.");
}
}
WaterPump.Java
import javax.inject.*;
public class WaterPump {
#Inject WaterPump() {
System.out.println("WaterPump Constructor.");
}
public void run() {
System.out.println("WaterPump running.");
}
}
The output is:
Car constructor
Engine Constructor
WaterPump Constructor.
WaterPump running.
starting engine.
Without The CarModule that declares it injects Car.Class this does not work. You get:
Exception in thread "main" java.lang.IllegalArgumentException: No
inject registered for members/Car. You must explicitly add it to the
'injects' option in one of your modules.
But notice that CarModule does not #Provides anything. It is dagger that automatically creates all the dependencies using the object graph.
Also note that you don't have to place an #Inject annotation on the default constructor if you have a #Inject field in the class. For Car class I used it on both the constructor and the field and in Engine class I used it just for the field and not for the constructor and it works fine as documented.

Categories

Resources