RoboGuice getInjector alternate in HILT (Dagger2) - android

I am in process of migrating to HILT from RoboGuice in my current app, there are alot of placs where injection in RoboGuice is done using below way:
IHelper helper = RoboGuice.getInjector(getApplicationContext()).getInstance(IHelper.class);
Dose HILT support this kind of injection, as per my knowledge HILT only does field and method injection. And correct me if I am wrong, field injection only works if I have #AndroidEntryPoint annotation declared (that means that class has to be one that aligns with #AndroidEntryPoint approved classes)

I got it sorted out thanks to #ADM's suggestion, here is how its working for me. Happy to see if any one has any improvement suggestions.
public class MyDialogFragment extends DialogFragment {
//create interface inside the class where you want to inject
#EntryPoint
#InstallIn(ApplicationComponent.class)
interface MyDialogFragmentEntryPoint {
public IHelper helper();
}
protected IHelper mHelper
public void anyMethod() {
//declare object of the Entry Point we declare
MyDialogFragmentEntryPoint dialogFragmentEntryPoint = EntryPointAccessors.fromApplication(this._context.getApplication(), MyDialogFragmentEntryPoint.class);
//You can get the instance of helper
mHelper = dialogFragmentEntryPoint.helper();
}
}

Related

Difference between void inject(Activity activity) and SomeComponent getSomeComponent()

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.

Dagger2 Inject a lot of activities/fragments/services (possible get a lot of NPE)

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

Dagger 2 - How does marking a class constructor with #Inject without component registration work

i have dagger already set up with two components. One component is a subcomponent of another, big deal. Everything works. But then i randomly wanted to try constructor injection so i created a random class and marked its constructor with the Inject annotation and to my surprise when i wnated to inject this class it works ? my Component(s) know nothing about this. I have no written in my components interface about this class. its just a random class that has a constructor annotated with #Inject. How is this working ? Here is the random class:
public class Knife {
#Inject
public Knife(){
System.out.println("a spreading knife has been created");
};
}
and here is how call inject my class if that matters:
public class MainActivity extends AppCompatActivity {
private final String TAG = getClass().getSimpleName();
//#Inject
//AlmondButter someAlmondButter;
#Inject
CashewSandwich sandwich;
#Inject
CashewSandwich sandwich2;
/*some how this is getting injected but its not in any component, how ?No ones
providing it in a module either*/
#Inject
Knife mKnife;
SandwichComponent sandwichComponent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*create the dependent butter for the sandwich here*/
ButterComponent butterComponent=DaggerButterComponent.builder().
butterModule(new ButterModule()).build();
/*create a scope sandwichcomponent here */
sandwichComponent=DaggerSandwichComponent.builder().sandwichModule(new SandwichModule()).
butterComponent(butterComponent)
.build();
//finally we have a sandwichComponent, lets inject our dependencies
sandwichComponent.inject(this);
Log.v(TAG," first:"+sandwich.toString());
Log.v(TAG,"second:"+sandwich2.toString());
Log.v(TAG,mKnife.toString()); //this actually works !
}
}
UPDATE: I wrote a blog on this feature incase anyone needs help:
http://j2emanue.blogspot.ca/
Placing #Inject on a constructor makes it detectable to Dagger. You can read more about it in JSR 330.

Dagger 2 - how to inject only to base activity/fragment

I am studying a Dagger 2 from many sources such as this one: http://fernandocejas.com/2015/04/11/tasting-dagger-2-on-android/
but I still haven't found an answer to my question.
I work on quite complex application with tens of fragments and several activities in which I want to use DI (dagger 2). For all of those fragments and activities I have one BaseActivity and one BaseFragment. However, as far as I read and tried, in order to use #Inject in my let's say MainActivity, I have to specify it in Component interface and also invoke getApplicationComponent().inject(this) in onCreate method. When I do this for BaseActivity only, #Inject annotated fields in MainActivity is never injected. And what is even worse, I do not find out about that until that specific part of code is executed and NPE is thrown.
So far it is a deal breaker for me, because this can be source of many crashes. I would need to specify tens of fragments and activities in Component interface and not forget to call inject in each onCreate method.
I would be very glad to hear any solution to this since I would really like to use DI..
code example:
#Singleton
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
void inject(BaseActivity baseActivity);
Analytics analytics();
}
public class BaseActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.getApplicationComponent().inject(this);
}
}
public class MainActivity extends BaseActivity {
#Inject
Analytics analytics;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
analytics.log("event1"); // THROWS NPE!
}
}
You can not inject properties in your subclass by injecting the super (since dagger2 works at compile time and there is no way to dynamically check subclasses for annotated properties.)
You can move analytics up to the super, then it will be injected there. To inject annotated fields in your subclass you will have to call the injection there again.
You can make an abstract method in your baseclass e.g. inject(App app)where you just handle the injection. That way you can't 'miss' it.
As stated in the official documentation:
While a members-injection method for a type will accept instances of its subtypes, only Inject-annotated members of the parameter type and its supertypes will be injected; members of subtypes will not.
move the
#Inject
Analytics analytics;
to your BaseActivity class, the Analytics object is initialized in the superclass and is inherited by sub-classes automatically, therefor u wouldn't get null any more.
public class MainActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
analytics.log("event1");
} }

How can I inject parameter through constructor in Roboguice? [android]

This question is probably exact duplicate of this one
Pass parameter to constructor with Guice
Difference is that I use roboguice for android, not just Guice, so answers there does not work for me.
Question is - how can I pass initialize parameters into created object? I.e. I have injected interface which should be initialize with some parameter which roboguice does not know.
What I see in link I provide, I should create factory interface and register it like this
void configure(Binder binder) {
binder.install(new FactoryModuleBuilder()
.implement(FooInterface.class, Foo.class)
.build(FooFactory.class));
}
But I can't find FactoryModuleBuilder class. I use Intellij IDEA, it can show me every class which I can access at current place and I can be 100% sure that there is no classes which starts with 'Factory' word.
How can I create my factory using roboguice?
UPDATED
I forgot to download guice-assistedinject. But still I can't figure out where should I register this factory.
UPDATE 2
Why I need that? Because there should be situation where some abstraction has dependency which could not be resolved by Roboguice. This dependency could be any type, even simple string or number.
In my case I have NumberPicker control on UI and I want to move all UI specific tasks in MyNumberPickerWrapper class. And when I create this wrapper I inject its dependency (this control) through constructor.
It's not the point if I am right with such approach, but that there could be a plenty of another more applicable example where constructor injection needed and this injected classes could not be created by Roboguice
I followed the steps of the answer given in Pass parameter to constructor with Guice and did slight modifications to run it under roboguice. Works completely fine for me.
add guice-assistinject library to gradle script
dependencies { compile 'com.google.inject.extensions:guice-assistedinject:4.+' }
Create Factory interface that with create method that accepts parameters the object constructor requires and returns object's interface
public interface ICustomObjectFactory {
ICustomObject create(String queueName);
}
Add #Inject annotation to constructor of the object and #Assisted annotation to each parameter coming from factory.
public class CustomObject implements ICustomObject {
protected String name;
#Inject
public CustomObject(#Assisted String name){
this.name = name;
}
}
Add binding into the Module that you are using
public class SomeModule extends AbstractModule {
#Override
protected void configure() {
install(new FactoryModuleBuilder()
.implement(ICustomObject.class, CustomObject.class)
.build(ICustomObjectFactory.class));
}
}
Inject factory and create instances of your object
public class SomeClass {
#Inject ICustomObjectFactory factory;
public SomeClass () {
ICustomObject first = this.factory.create("first");
ICustomObject second = this.factory.create("second");
}
}
I faced this same problem and I succeed thanks to Pavel's answer. I only have had to struggle with some errors, and I don't know if it's due to the versions of the libraries used, but for me didn't work without modifying the annotation of the constructor, replacing #Inject by #AssistedInject. With that, the code of the class that implements the interface looks like this.
public class CustomObject implements ICustomObject {
protected String name;
#AssistedInject
public CustomObject(#Assisted String name){
this.name = name;
}
}

Categories

Resources