I am new to using roboguice and i am having a hard time configuring injection in my application.
Say that i have the following interface
public interface IAPICall{
void doSomething();
}
and i have two different implementations of my interface
public class MyApiCall implements IAPICall{
public void doSomething(){
}
}
public class MyMockApicall implements IAPICall{
public void doSomething(){
}
}
Now my requirement is that i want to inject the interface into my activity. How do i configure which concrete class gets injected. During testing i want to inject my mock class while during production i want to inject the actual class. How can i configure this ?
Kind Regards
In your guice configuration module :
public class GuiceConfigurationModule extends AbstractModule {
...
#Override
protected void configure() {
...
bind(IAPICall.class).to(MyApiCall.class);
...
}
...
}
In your activity :
#Inject
IAPICall someApiCall;
The best way to play with a mocked interface during test is to create a test module where the binding are pointing to mockup classes. There is a tutorial on Robolectric
on how to do that.
http://pivotal.github.com/robolectric/roboguice.html
To add your module to your application, add a roboguice.xml file in the values ressources folder :
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="roboguice_modules">
<item>com.stackoverflow.test.GuiceConfigurationModule</item>
</string-array>
</resources>
This is described here :
http://code.google.com/p/roboguice/wiki/UpgradingTo20
Related
I have an application with let's say 15 screens.
There are 3 main, completely separate Activities:
LoginActivity - quite obvious one, some login things
MainActivity - THE big one, most importat
TheOtherOneNotRelevantAtTheMoment
I decided to use Conductor, because I've found Fragment's lifecycle too complicated, and for me Conductor is now "hot shit"
I've got many Conductor's controllers. Most of them are like XXXListController with corresponding XXXDetailController. All of these Controllers live only inside MainActivity. It's something like "single-activity-application". LoginActivity was introduced basically due to callbacks from third party OAuths like FbLogin & Google Login. I am just trying to make MainActivity completely mine - without any other strange behaviours.
To organize the dependencies a little bit, I've decided to use Dagger2. I've got quite good experience with Spring and JavaEE so I though it would be easy.
I recognized few Modules without any problems:
AppModule - with my App-related things like server address etc
AndroidModule - with things like SharedPreferences, Application
Then I've pretty much "improvised" with how to organize my views. This is what I've got:
3 additional Scopes: ActivityScope, ControllerScope, and ServiceScope - not relevant.
each Controller has it's own corresponding Module & Component. I've read that this could not be a very good idea, but I am ok with it - each Controller is pretty independend and has it's own unique set of dependencies.
ApplicationComponent is of course root of the hierarchy.
MainActivityComponent is a #Subcomponent of the ApplicationComponent
XXXControllerComponent is a #Subcomponent of the MainActivityComponent
To inject dependencies inside MainActivity I am using and I found this code pretty common:
protected void injectDependencies(ApplicationComponent component) {
component.plus(new MainActivityModule(this)).inject(this);
}
The problem appears when I want to create & inject dependencies to my Controllers.
The MainActivity looks as follows:
#ActivityScope
#Subcomponent(modules = {
MainActivityModule.class
})
public interface MainActivityComponent {
XXXListComponent newXXXListComponent(XXXListModule xxxListModule);
void inject(MainActivity activity);
}
At the moment, the typical Controller looks like this:
#ControllerScope
#Subcomponent(modules = {
XXXListModule.class
})
public interface XXXListComponent {
void inject(XXXListController controller);
}
and the corresponding Module:
#Module
public class XXXListModule {
private XXXListController listController;
public XXXListModule(XXXListController listController) {
this.listController = listController;
}
#Provides
#ControllerScope
public XXXListController getMainView() {
return ListController;
}
// other not important
}
Basically every Controller should be singleton - I don't want to have two instances inside MainActivity - but this not a 'must-have'.
The problem is how to create Controller the right way. At the moment, MainActivity do it as follows:
router.pushController(RouterTransaction.with(new XXXListController()));
I am not sure about this, why to create Controller by hand?
Inside Controller in onCreateView() I've injecting all needed dependencies - in my opinion in very ugly way:
((MainActivity) getActivity()).getMainActivityComponent()
.newXXXListComponent(new XXXListModule(this))
.inject(this);
This long question helps me organize my knowledge about Dagger - maybe someone find it helpful. But! For those Stackoverflowers who reach this line, is this a good way or is there any other, better way to do it?
I'm not sure I fully understand your question, but since you have multiple Activities you probably want to do your injecting inside your Application rather than MainActivity.
Otherwise they will no longer be singletons as the activities get re-created when you move between them. For example if you make a class called ConductorApplication:
public class ConductorApplication extends Application {
static AppComponent app_component;
static ClockComponent component;
#Override
public void onCreate() {
super.onCreate();
app_component = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
component = createComponent();
}
protected ClockComponent createComponent() {
return DaggerClockComponent.builder().build();
}
public static ClockComponent getClockComponent() {
return component;
}
public ClockComponent component() {
return component;
}
public static AppComponent GetAppComponent() {
return app_component;
}
}
Then inside when your controller is created eg inside your inflateView() within HomeController
public class HomeController extends BaseController {
private HomeViewModel homeViewModel;
private ControllerHomeBinding binding;
#Inject
Clock clock;
#NonNull
#Override
protected View inflateView(#NonNull LayoutInflater inflater, #NonNull ViewGroup container) {
ConductorApplication.getClockComponent().inject(this);
return inflater.inflate(R.layout.controller_home, container, false);
...
}
If you haven't already, you may need to add:
<application
android:name=".ConductorApplication"
...
<activity
android:name=".MainActivity"
...
</activity>
</application>
inside your AndroidMainifest.xml
Usually when using Dagger 2 and android I have the following:
#Singleton
#Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
void inject(MainActivity activity);
}
public class MainActivity extends Activity {
#Inject SharedPreferences mSharedPrefs;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((DemoApplication) getApplication())
.getComponent()
.inject(this);
}
}
But recently I have seen this:
#Singleton
#Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
SharedPreferences getSharedPreferences();
}
public class MainActivity extends Activity {
SharedPreferences mSharedPrefs;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSharedPrefs = ((DemoApplication) getApplication())
.getComponent().getSharedPreferences();
}
}
I have omitted the DemoApplication class and the Module class, they are standard.
What is the difference between these two approaches? Pro's and con's of either? Maybe a right or wrong way?
The dependency inversion principle of software engineering suggests that we should try and depend on abstractions rather than concretions.
For this reason, you should prefer the #Inject annotations (abstract) for field injection in your Activity rather than calling the provision method from the Dagger Component (concrete).
Why? You will notice that the #Inject annotations are part of the javax.inject package. This is a standard for dependency injection APIs introduced into Java as part of JSR330. There are other DI frameworks, such as Guice and Toothpick, that can use these annotations. If you have to switch DI frameworks in the future it will be easier if you use the #Inject annotations. Yes, it does happen that you have to change DI frameworks. Roboguice is an example of a popular DI framework that has recently been retired.
To illustrate, let's take your Activity, add a few dependencies, and extract a method for injection like this:
public class MainActivity extends Activity {
#Inject SharedPreferences mSharedPrefs;
#Inject Foo foo;
#Inject Bar bar;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#VisibleForTesting
protected void injectMembers() {
((DemoApplication) getApplication())
.getComponent()
.inject(this);
}
}
A wild (new) DI framework appears! You now have to change your app to use it quickly! You use JSR330. It's super-effective! (translation: you can re-use your JSR330 #Inject annotations because the new DI framework supports them). If you had changed to a new Guice-like framework, all you would need to do is rewrite your method:
#VisibleForTesting
protected void injectMembers() {
GuiceLikeInjector.getInjector(this).injectMembers();
}
In contrast, if you had injected those fields manually using .getSharedPreferences(), getFoo() it's not very effective - you have to change a lot of lines of code.
If you look at the generated code of the Component, you'll notice that it implements the inject(MainActivity) method by setting injected fields directly using the activity reference you're passing it.
So both options do the same thing.
I prefer the first approach for 2 main reasons. First it is a lot clearer that fields are injected when they are annotated as such, and second it keeps the code much cleaner. In the example you gave you inject a single field and it's harder to see the benefit, but I think it becomes much more apparent when you need to inject 10 fields, where you will have to assign all of them in the onCreate(), and declare getters for them in the component.
We used RoboGuice, but it's deprecated I start replace it with Dagger2.
// https://github.com/google/dagger
compile('com.google.dagger:dagger:2.7')
annotationProcessor 'com.google.dagger:dagger-compiler:2.7'
provided 'org.glassfish:javax.annotation:10.0-b28'
#Module
public class ApplicationModule {
Application mApp;
public ApplicationModule(#NonNull Application app) {
Preconditions.checkNotNull(app);
mApp = app;
}
#Provides
#Singleton
public SharedPreferences providesSharedPrefs() {
return PreferenceManager.getDefaultSharedPreferences(mApp);
}
#Provides
#Singleton
public DateHelper providesDateHelper() {
return new DateHelper(mApp);
}
#Provides
#Singleton
public PersistentConfig providesPersistentConfig() {
return new PersistentConfig(mApp);
}
#Provides
#Singleton
public OttoBus providesOttoBus() {
return new OttoBus();
}
}
public class Application extends MultiDexApplication {
private ApplicationComponent mApplicationComponent;
#Override
public void onCreate() {
super.onCreate();
mApplicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
mApplicationComponent.inject(this);
}
public static Application getApp(#NonNull Context context) {
return (Application) context.getApplicationContext();
}
public static ApplicationComponent getApplicationComponent(#NonNull Context context) {
return getApp(context).getApplicationComponent();
}
}
And after everywhere when I want to inject ApplicationComponent
For example MainActivity
public class MainActivity extends AppCompatActivity {
#Inject
PersistentConfig mPersistentConfig;
#Inject
OttoBus mOttoBus;
#Override
public void onCreate(Bundle savedInstanceState) {
Helper.manageRotation(this);
super.onCreate(null);
setContentView(R.layout.main_layout);
Application.getApplicationComponent(this).inject(this);
}
}
Application.getApplicationComponent(context).inject(this);
First question: I'm really confused about interface ApplicationComponent which must provide all activities/fragments/services (etc) where I want to use injection. But I can't use generic objects like Activity / Fragment. Or am I really out of reality and don't understand how Dagger2 works?
Because this is really crazy for project with about 50+ activities and a tons of fragments/services...
#Singleton
#Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
void inject(#NonNull Application app);
void inject(#NonNull MainActivity object);
void inject(#NonNull DispatcherActivity object);
void inject(#NonNull DateTimeHelper object);
void inject(#NonNull DatabaseHelper object);
void inject(#NonNull LandingPageActivityFragment object);
void inject(#NonNull RedirectActivity object);
void inject(#NonNull CategoryFragment object);
void inject(#NonNull BaseModuleFragment object);
void inject(#NonNull NotificationHelper object);
void inject(#NonNull RecordAdapter object);
void inject(#NonNull PagingProvider object);
void inject(#NonNull FilterDialog object);
... next 100+ injections?
}
Said me, that it can't be real...
Second question: How I can provide to inject generic classes, when I can't use it like void inject(#NonNull NotificationHelper<? extends GenericObject> object); because it require specific object. So I must write all this objects inside ApplicationComponent and not use ? notation?
It's a much more than just crazy :(. Maybe better stay with RoboGuice which is much more developer friendly and don't need make this overhead and manual check every injected objects? When I forgot add them to this list, I will get NPE in runtime (when I will not test it a lot it will crash customers).
It's much faster write it manually, than make a list of all object when it's not possible to use generic objects like Activity / Fragment / Service.
Is there a better solution, when I don't want use same generic BaseActivity which will inject every part of ApplicationModule and every activity will be extended by this huge BaseActivity?
This question has aspects of a complaint, but to attempt an answer:
I'm really confused about interface ApplicationComponent which must provide all activities/fragments/services (etc) where I want to use injection. But I can't use generic objects like Activity / Fragment. Or am I really out of reality and don't understand how Dagger2 works?
This is, indeed, how Dagger 2 works; it you must statically supply the type of the injection target inside the injector (component) and you cannot use 'generic' (covariant) types. Dagger 2 does this in order to maintain a DI framework that is 100% static.
Note that you are specifying RecordAdapter and DatabaseHelper as injection sites. You probably don't need to do that, you should try and only specify top level objects for which the constructor is not visible (Activity, Fragment, and Service) as injection sites. The rest of the objects should be able to be constructed through annotating their dependencies with #Inject and specifying their dependencies, if any, in a Module.
Maybe better stay with RoboGuice which is much more developer friendly and don't need make this overhead and manual check every injected objects
Yes Roboguice is more friendly in the sense that you don't have to worry about specifying the injection targets. However, consider the following in Roboguice: 1. The 'red stacktrace of death' you get when you set up your object graph incorrectly
2. The fact that you cannot get see which implementations of interfaces are actually being used in your project with Find Usages which can also be 'developer unfriendly'
Is there a better solution, when I don't want use same generic BaseActivity which will inject every part of ApplicationModule and every activity will be extended by this huge BaseActivity?
Well, it would depend which dependencies you are using and where. If you have a small list of dependencies that you want to inject everywhere, that may be the best solution i.e., make a BaseActivity that receives injection of these and makes this available to all of your subclasses. Alternatively, you can use sub-components and modules you can divide up your object graph so that you can group consumers/injection targets together with the correct modules. Then you don't need to have one 'god' component that lists all of the injection sites.
Second question: How I can provide to inject generic classes, when I can't use it like void inject(#NonNull NotificationHelper object); because it require specific object. So I must write all this objects inside ApplicationComponent and not use ? notation?
Yes, you must supply the invariant type of the injection target. I am not sure if your NotificationHelper<String> is a top level type. Why not inject it through the object graph when you inject in a Fragment, Activity or Service?
If it absolutely must be an injection target you will need to subclass: NotificationHelper<String> and Notification<Integer> become StringNotificationHelper extends NotificationHelper<String>, IntegerNotficationHelper extends NotificationHelper<Integer>. This is a practice recommended in the book Clean Code.
You don't need to write it all the injection sites inside the ApplicationComponent, you may create subcomponents that correspond with the consumption patterns of the dependencies in your project.
(disclosure: as someone who is currently trying to migrate a project from Roboguice to Dagger 2 I am sympathetic to your complaint)
Thanks, we solved it as you described a week ago. Using every objects as injected.
Better solution for it is don't use only inject but complex name. Why? Because it will help to resolve why some object is not injected (you know, base classes and so on).
#Singleton
#Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
void injectApplication(#NonNull Application app);
void injectMainActivity(#NonNull MainActivity object);
void injectDispatcherActivity(#NonNull DispatcherActivity object);
...
}
We finally use for genericity UtilityWrapper as is described here: https://medium.com/#patrykpoborca/dagger-2-and-base-classes-generics-and-presenter-injection-7d82053080c#.b58ykd4cm
TL;DR;
Is it acceptable for a class to depend on the ObjectGraph itself?
I need this because I need to inject dependencies on some objects that I load at runtime - at a time that is disconnected from the point at which the ObjectGraph is initialized. Here is an example that illustrates how I use ServiceLoader framework to load concrete implementation classes of a Service at runtime, and then inject dependencies into the loaded implementation classes.
interface Plugin {
void doSomething();
}
class AwesomePlugin implements plugin {
#Inject DependencyOne dependencyOne;
#Inject DependencyTwo dependencyTwo;
void doSomething(){
// ...some implementation...
}
}
class PluginEngine{
public void start(){
ServiceLoader<Plugin> pluginLoader = ServiceLoader.load(Plugin.class);
for(Plugin plugin: pluginLoader){
//TODO: Inject plugin dependencies here
}
}
}
Doing this would require the PluginEngine class to have access to the ObjectGraph instance:
class PluginEngine{
private final ObjectGraph objectGraph;
public PluginEngine(ObjectGraph graph){
this.objectGraph = graph;
}
public void start(){
ServiceLoader<Plugin> pluginLoader = ServiceLoader.load(Plugin.class);
for(Plugin plugin: pluginLoader){
objectGraph.inject(plugin);
}
}
}
Is this a code smell? Is this pointing to some problem elsewhere in my code, or in the way my dependencies are set up?
While composing this question, I began to see the role of Dagger as a means of replacing arbitrary dependencies with a dependency on the ObjectGraph itself. On Android, you use a reference to the custom Application sub-class and use it to perform injection - which is basically just a means to get access to the ObjectGraph itself. Is this reasoning flawed?
To answer my own question, it looks like this is acceptable. The u2020 sample app does something roughly similar. In fact it makes some very clever use of getSystemService() to achieve this but that is Android specific. In particular, look at Injector.java and how it is used from within custom views like TrendingView
So, conceptually, one could do something like this - which basically abstracts the concrete ObjectGraph dependency behind an Injector interface.
class PluginEngine{
private final Injector injector;
public PluginEngine(Injector injector){
this.injector = injector;
}
public void start(){
ServiceLoader<Plugin> pluginLoader = ServiceLoader.load(Plugin.class);
for(Plugin plugin: pluginLoader){
injector.inject(plugin);
}
}
}
This can be refined/adjusted in various ways such that the injector dependency is provided via a constructor or obtained in other ways.
I use abstract activity classes in my code to well, abstract away some features from the activity classes.
I'm trying to test the abstract activity classes using Robolectric and the gradle-android-test-plugin using subclasses that extend the abstract class. I can't seem to get it to work though.
Does anyone have any experience in this area and is it even possible ? Basic structure is :
#RunWith(RobolectricGradleTestRunner.class)
public class AbstractActivityTest {
private ActivityTest activity;
#Before
public void setUp() throws Exception {
activity = Robolectric.buildActivity(ActivityTest.class).create().get();
}
private class ActivityTest extends AbstractActivity {
// do something
}
}
Initially, I got the error message the sub class wasn't static so I made it static. Now I get the following two fails:
initializationError FAILED
java.lang.Exception: Test class should have exactly one public constructor
initializationError FAILED
java.lang.Exception: No runnable methods
Any obviously true tests I put in #Test methods succeed.
The first error saying that you added non-default constructor to your test class or changed access level for default one. But as it says junit Test class should have at least one public constructor.
The second one says that at least one method in test class should have #Test annotation (junit 4) or starts with test substring (junit 3).
Yo can doing exactly what you are trying to do: subclass the abstract activity and instance the concrete class.
However, you need to declare the class extending the abstract Activity in it's own public file. If it's a nested class Robolectric will fail to instance it.
I don't know why, though.
I test an abstract activity this way:
1. Creating the abstract avtivity:
public abstract class AbstractActivity extends AppCompatActivity {
public int getNumber() {
return 2;
}
}
2. Creating the test class:
You just need to declare a static nested subclass of your abstract class.
#RunWith(RobolectricTestRunner.class)
public class AbstractActivityTest {
#Test
public void checkNumberReturn() throws Exception {
TestAbstractActivity testAbstractActivity = Robolectric.setupActivity(TestAbstractActivity.class);
assertThat(testAbstractActivity.getNumber(), is(2));
}
public static class TestAbstractActivity extends AbstractActivity {
}
}