I want to use injecting in classes like Adapter or custom object, which don't have access to MyApplication class which extends Application.
How can I get access to component?
Is it good approach to use static methods like below?
public class MyApplication extends Application {
private static MyComponent component;
#Override
public void onCreate() {
super.onCreate();
component = DaggerMyComponent.builder()
.appModule(new AppModule(this))
.helperModule(new HelperModule())
.build();
}
public static MyComponent getComponent(){return component;}
}
I think using static objects in this case would cause memory leaks.
What you might follow is "Inject everything" pattern shown in this great article.
What is it's essence? Try to inject the object that needs Component inside the object that is already injected. In this article Adapter and ViewHolder (any object) is injected into activity (and objects inside adapter too). This is what you are trying to achieve.
I also advise you to read all his articles about Dagger 2, as I haven't seen such a great and advanced tutorial on Dagger 2 features
Related
This is the Nth question about how to store #Singleton scoped Dagger 2 Components whose lifetime should equal the application's lifetime.
In Android apps using Dagger 2 there is usually at least one Component which is #Singleton scoped and should last for all the application's lifetime: because of these requirements it is usually initialised and stored inside a custom Application class.
Since the instance of this Component must be reachable in all parts of our Application I've seen code like this:
1. Store the component in a public static variable inside the application class.
public class App extends Application {
public static AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this)).build();
}
}
This way it can be accessed anywhere else with:
App.appComponent.inject(this);
2. Store the component in a private variable inside the application instance and create a static accessor for it.
public class App extends Application {
private static AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this)).build();
}
public static AppComponent getAppComponent() {
return appComponent;
}
}
This way it can be accessed anywhere else with:
App.getAppComponent().inject(this);
3. Store the component in a private variable inside the application instance and create a non static accessor for it.
public class App extends Application {
private AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this)).build();
}
public AppComponent getAppComponent() {
return appComponent;
}
}
This way it can be accessed only from class instances which hold a reference to a Context:
// From within an Activity.
((App) getApplication()).getAppComponent().inject(this);
// From within a Fragment.
((App) getActivity().getApplication()).getAppComponent().inject(this);
// From within any other class which holds a reference to a Context.
((App) context.getApplicationContext()).getAppComponent().inject(this);
This last way makes it pretty much compulsory to pass a Context reference to any class willing to access the Component (even if that Context isn't needed by that class for any other purposes).
IMHO having to "manually inject" a Context instance only to access the injector itself sounds a bit counter intuitive.
On the other side many advise against using static variables but: why? If an object must stay in memory for the application's lifetime (which means for the whole lifetime of the JVM instance) what's the problem if it's stored in a static variable?
Others say that static stuff can't be mocked in tests and it's true, though I'm not sure I totally get this because it is the DI pattern which enables easy mocking/testing and not the injector itself, so why would we want to mock the injector itself?
What are the pros and cons of these alternatives? Are there any other possible alternatives besides the ones already mentioned here?
With 1 and 2 you are using static references. This is a good thread about why to avoid them
Why are static variables considered evil?
So the only option left is the 3rd. That is what I am using on my projects.
About if you should pass the context as argument or not, depends on the architecture of your project and how you designed the Dagger dependencies. Personally I don't have that problem because I am only injecting in Activities/Fragments. Can you give me an example where you need to pass the context to inject dependencies?
I use method #2. The main problem with method #1 is that you're exposing a mutable field. If your module doesn't require a Context to construct, you could make the field final. But as a matter of style, I still prefer not to expose fields.
You should normally avoid global state, especially in Android because of the complex and sometimes unintuitive lifecycles of components and the VM itself. But Application is the exception to this rule. Exactly one instance of it exists per VM, and its onCreate() method is called exactly once, before any other component is created. This makes it an acceptable place to create and store a static singleton.
I am trying to make my app better and code more maintainable using Dagger2 I caught general idea, but still cannot figure out how scopes are managed by Dagger2
I injected dagger into my project (sounds funny).
I created ApplicationComonent component and it works perfectly in my project.
Here is my code.
#Singleton
#Component(modules = {
ApplicationModule.class,
ThreadingModule.class,
NetworkModule.class,
DatabaseModule.class,
ServiceModule.class,
ParseModule.class,
PreferencesSessionModule.class})
public interface ApplicationComponent {
ActivityComponent activityComponent(ActivityModule activityModule);
void inject(BaseActivity baseActivity);
void inject(MainAppActivity mainAppActivity);
void inject(MyApplication application);
void inject(BaseFragment baseFragment);
void inject(MyService service);
void inject(RegistrationIntentService service);
}
I create my component instance in MyApplication class like this
private void initializeAndInjectComponent() {
mApplicationComponent =
DaggerApplicationComponent
.builder()
.threadingModule(new ThreadingModule(1))
.applicationModule(new ApplicationModule(this))
.networkModule(new NetworkModule(
MyService.API_SERVER_BASE_URL,
MyService.TIMEOUT))
.build();
mApplicationComponent.inject(this);
}
And I can obtain component in order to inject in in my Activities
MyApplication application = MyApplication.get(this);
application.getApplicationComponent().inject(this);
Everything works perfectly.
To add each method as well as module class is annotated with #Singleton scope, all modules related to the ApplicationComponent
Now I want to make dependencies better and I have seen a lot of examples with custom scopes like #PerActivity, #PerFragment. I have a lot of questions, but about this later.
So I created ActivityComponent
#PerActivity
#Subcomponent(
modules = {
NetworkServiceModule.class,
ActivityModule.class,
PermissionModule.class
})
public interface ActivityComponent {
Activity activity();
void inject(BaseActivity baseActivity);
}
All modules looks like this
#PerActivity
#Module
public class ActivityModule {
private Activity mActivity;
public ActivityModule(Activity activity) {
this.mActivity = activity;
}
#Provides
#PerActivity
Activity provideActivity() {
return this.mActivity;
}
}
I have following dependencies in my BaseActivity
// Dependencies from ApplicationComponent
#Inject
protected ApplicationSettingsManager mApplicationSettingsManager;
#Inject
protected ScheduledThreadPoolExecutor mPoolExecutor;
// Dependencies from ActivityComponent
#Inject
protected SpiceManager mSpiceManager;
#Inject
protected PermissionController mPermissionController;
And in my onCreate() method I am injecting as following
MyApplication application = MyApplication.get(this);
application.getApplicationComponent().activityComponent(new ActivityModule(this)).inject(this);
Before creating subcomponent ActivityComponent it was
MyApplication application = MyApplication.get(this);
application.getApplicationComponent().inject(this);
Now I got an error
Error:(34, 10) error: com.octo.android.robospice.SpiceManager cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method.
BaseActivity.mSpiceManager
[injected field of type: com.octo.android.robospice.SpiceManager mSpiceManager]
I cannot figure out where is problem, what I missed.
My questions about scopes in dagger2.
Everything but #Singleton is ignored by Dagger 2, am I right ?
I don't understand how life of component is managed ? I have only one idea
When you use #Singleton annotation dagger is creating object in some static pool that will exist during whole application lifecycle, and will be destroyed when JVM process (dalvik VM,ART) instance will be destroyed.
When you use any other annotation is just for you as developer to better maintain code, #PerActivity, #PerFragment is just custom annotation nothing more. And in case you place #PerFragment component in Application class it will live as long as Application lives. Am I right ?
So I understand this like this, if dagger finds #Singleton annotation it will add static reference to component when it is created first time and in case of any other annotation it won't hold reference to component.
I would be very grateful for any help with problems described above.
UPDATE
Thank you David Medenjak for great answer, I got much better understanding of Dagger2.
I have also just found the problem, as far as I am using separate Activity component now, I forgot about two lines in ApplicationComponent and change inejction in my MainActivity to ActivityComponent instead of ApplicationComponent, so for sure it couldn't resolve dependencies from subcomponent.
void inject(BaseActivity baseActivity);
void inject(MainAppActivity mainAppActivity);
Now everything works perfectly, I like Dagger2 and separated architecture.
A bit radical, but to simplify things:
All Scope annotations are nothing but syntactic sugar—including #Singleton.
Scopes mostly just provide compile time checks. Cyclic dependencies, or errors about things that you might have missed. #Singleton is just like any other scope, the only difference is that it is an already existing annotation and you don't have to create it yourself. You could just use #MySingleton instead.
[...] dagger is creating object in some static pool that will exists during whole application lifecycle
No. Dagger does nothing static. You have component objects. Those components hold your objects created by modules. If an object in a component has the scope of the component, it will only be created once in that exact component. If you decide to create 2 AppComponent objects, you will have 2 objects of each #Singleton annotated object, each within its component. This is why you should keep the reference to the component. Most implementations that I have seen or used hence keep their AppComponent within their Application. If you do this, you can use it like a singleton—it is still just a POJO.
[...]you place #PerFragment component in Application class it will live as long as Application lives.
Yes. As already covered by the paragraph above, it is just an object. Keep the reference, you keep the objects. Throw it away or create a new one and you have new objects (defined within in this component / scope). You should although not keep activity or fragment scoped components any place besides in activities or fragments respectively, since keeping them e.g. in your app component will most likely lead to a memory leak. (If it doesn't, you probably would not have needed the activity or fragment scope.)
if dagger finds #Singleton annotation it will add static reference to component when it is created first time and in case of any other annotation it won't hold reference to component.
Again, no. Nothing static. Plain old java objects. You can have multiple #Singleton components with their own objects, but you probably shouldn't (Although this is what makes instrumentation testing possible / easy—just swap components.)
Your mentioned error
SpiceManager cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method.
This means that the component you are trying to inject your object with can not find any way to produce or provide a SpiceManager. Make sure you provide it from your AppComponent or some other place, are not missing any annotations, etc.
I need to create tests for some class. This class in main project (src/main/java/..) is injected easily into another classes, since I have custom ResourceConfig class which declares which packages have to be scanned to seek for service classes.
Now I created test directories (in src/test/java/..) and created a class, something like:
public class TheMentionedClassIntegrationTest {
#Inject
private TheMentionedClass theMentionedClass ;
#Test
public void testProcessMethod() {
assertNotNull(theMentionedClass);
}
}
But the problem is that whatever I do the class is always null. In another tests in the project I was using JerseyTest class. So I tried to do the same here, extend TheMentionedClassIntegrationTest with JerseyTest, override configure method, create my private ResourceConfig class which registers Binder (default for whole project) and register TheMentionedClassIntegrationTest as well.
It didnt work. I did many different attempts but none of them were successfull. I think working with HK2 is extremly difficult, there is no good documentation or so..
Do you guys have an idea how to inject TheMentionedClass into the test class? Maybe my approach is wrong?
Thanks!
The easiest thing to do is to just create the ServiceLocator and use it to inject the test class, as see here. For example
public class TheMentionedClassIntegrationTest {
#Inject
private TheMentionedClass theMentionedClass;
#Before
public void setUp() {
ServiceLocator locator = ServiceLocatorUtilities.bind(new YourBinder());
locator.inject(this);
}
#Test
public void testProcessMethod() {
assertNotNull(theMentionedClass);
}
}
You could alternatively use (make) a JUnit runner, as seen here.
For some other ideas, you might want to check out the tests for the hk2-testing, and all of its containing projects for some use case examples.
I'm just trying dagger instead of roboguice, so far butterknife was awesome and simple, point for it :)
But dagger on the other hand I found it less configurable than roboguice, I have to benchmark if its worth the change but in this case I'm looking on how to inject stuff in lets say Adapters, this is what I made and it works:
public class PeopleAdapter extends BaseAdapter {
private static final String TAG = PeopleAdapter.class.getName();
#Inject
TempoSharedPreferences prefs;
private LinkedList<People> elements;
public PeopleAdapter (LinkedList<People> elements, TempoApplication app) {
this.elements = elements;
app.inject(this);
Log.d(TAG, "registered: " + prefs.isRegistered());
} ....
But on the Activity that creates this instance I have to get an Application that allows to inject, also I have to add to the module every time the classes that use that dependency, roboguice did all that for me and had only one entry point where to modify the stuff.
I'm I doing something wrong? is there any better way to perform this injections? Avoid the declaration of each class on the module?
#Module(injects = {
MainActivity.class,
PeopleAdapter.class
},
library = true)
public class AndroidModule { ....
I'll appreciate any comment or best practice or experience on this.
Thanks!
You're doing everything right. Dagger needs some more configuration than Roboguice, but is also more powerful (configurable). Did you try scoped graphs or lazy injection already?
Every single class that uses injection must be listed in the injects param of the module. There's no way to avoid declaration of each class.
I have found one answer that appears to say I should create a separate class and make a static MyApplication object and make a get method. Then any class can call MyApplication.get() to retrieve the context.
Is there any other cleaner way? This is my situation:
I have a class A and a class B. Class A contains an object from class B (let's call the object b). In class A I call, "b.play()". However, I get a null pointer exception because class B needs to pass a context to the MediaPlayer.create() method.
Until now I threw together a hack and from class A I called.... "b.play(this)" and simply passed the context to B. However that is pretty ugly and looks like a bad use of OOP.
Any thoughts?
This problem seem to arise a lot in Android development. One solution to obtaining a reference to a specific Context is subclassing the Application and grab a reference to the Context which you want.
public class MyApplication extends Application {
private Context context;
#Override
public onCreate() {
super.onCreate();
this.context = getApplicationContext() // Grab the Context you want.
}
public static Context getApplicationContext() { return this.context; }
}
This solution however requires that you specify the name of your subclass in your manifest.
<application
android:name=".MyApplication"
</application>
You can then use this anywhere in your application like this in non-activity classes.
MyApplication.getContext(); // Do something with the context! :)
If class B requires a Context to operate, then I don't see any problem having class A provide that to it (through a parameter on the play method, a parameter in a constructor, etc).
I don't think you are doing any poor OOP by providing class B the dependencies that it needs to do it's job.
Passing this around is a viable way of doing things, especially if this is the activity that creates the object in need of a Context. Sometimes, I'll put the Context into the constructor (like public MyObject(Context context){this.context = context;}), so that you don't need to send it every time. However, if your object is shared across multiple Activities, you should probably update the context it is looking at with the new Activity, though I haven't tested what happens if you use the old activity.
I've answered also here.
You can do that using ContextWrapper, as described here.
For example:
public class MyContextWrapper extends ContextWrapper {
public MyContextWrapper(Context base) {
super(base);
}
public void someMethod() {
// MediaPlayer.create(this, ...)
}
}