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");
} }
Related
Given the following example:
class CustomView extends View {
#Inject
SomeObject mObject;
#Override
protected void onFinishInflate() {
super.onFinishInflate();
getApplicationComponent().inject(this);
}
}
class SecondaryCustomView extends CustomView {
#Inject
AnotherObject mAnotherObject;
#Override
protected void onFinishInflate() {
super.onFinishInflate();
getApplicationComponent().inject(this);
}
}
Both custom views may be used on a layout independently. The second is just a bit more specialized than the first one.
As you can see, both have fields to inject and both need to call inject(). The problem is that when SecondaryCustomView calls its inject(), Dagger injects an instance of AnotherObject and an instance of SomeObject. After the call to super.onFinishInflate(), it creates a second instance of SomeObject. This is not a problem per se, but we are at least creating unnecessary objects.
Is there a way to avoid this? Some way to tell Dagger that a child class has been injected, so ignore the parent injection?
As an example, the Component looks like this:
#Component(...)
public interface AppComponent {
void inject(CustomView);
void inject(SecondaryCustomView);
}
There's no way to do this in Dagger, but you can do it yourself.
To agree with and expand on your point in the comments:
yes it is needed. Dagger does not inject the child objects if we use injection on parent only. however it injects the parent objects if it is called from the child.
That's correct, and noted in "A note about covariance": Though inject(Foo) can accept an instance of Foo or any of its subclasses, Dagger's a compile-time framework; inject(Foo) will not be generated to inject the fields belonging to arbitrary subclasses of Foo because, well, that's impossible to know at compile time. This can be a little surprising, particularly if your Component has both inject(Foo) and inject(FooSubclass) as you have for CustomView and SecondaryCustomView here: With names injectCustomView and injectSecondaryCustomView it would be obvious that only the former is callable from within Foo.
Aside from simply setting an injectedAlready boolean field as a flag, one technique is to create an overridable method, which does not call its superclass implementation:
class CustomView extends View {
#Inject
SomeObject mObject;
#Override
protected void onFinishInflate() {
injectMe();
super.onFinishInflate();
}
protected void injectMe() {
getApplicationComponent().inject(this); // inject(CustomView);
}
}
class SecondaryCustomView extends CustomView {
#Inject
AnotherObject mAnotherObject;
#Override
protected void onFinishInflate() {
super.onFinishInflate();
// ...
}
/** Despite looking identical, the JVM can call the more-specific overload here. */
#Override protected void injectMe() {
getApplicationComponent().inject(this); // inject(SecondaryCustomView)
}
}
If you're looking for a similar solution for Activity and Fragment classes, you can use dagger.android; the built-in mechanism there uses the runtime type of the class to dynamically fetch the right AndroidInjector from a Map. However, that solution doesn't support View at the moment, so this is as close as you can get for your specific case.
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.
I'm trying to understand concept of MvP design pattern. I mean, I get it, its quite easy. The main problem is optimal implementation. I tried to make my own BaseActivity, BasePresenter and BaseView just to extract part of a joint from all of my activities, I've done this this way:
BaseActivity
public abstract class BaseActivity<T extends BasePresenter<? extends IBaseView>> extends FragmentActivity implements IBaseView {
protected T presenter;
private ActivityConfig activityConfig;
#Override
final protected void onCreate(Bundle savedInstanceState) {
activityConfig = getConfig();
super.onCreate(savedInstanceState);
presenter = createPresenter();
setContentView();
initLibraries();
prepareView(savedInstanceState);
addFragments();
}
protected abstract ActivityConfig getConfig();
protected abstract T createPresenter();
protected abstract void prepareView(Bundle savedInstanceState);
protected abstract void addFragments();
private void setContentView(){
View root = View.inflate(this, activityConfig.layoutId, null);
setContentView(root);
}
private void initLibraries() {
ButterKnife.bind(this);
Timber.plant(new Timber.DebugTree());
}
#Override
public BaseActivity getCurrentContext() {
return this;
}
#Override
public T getPresenter() {
return presenter;
}
}
BasePresenter
public abstract class BasePresenter<T extends IBaseView> {
public abstract void loadData(boolean refresh);
}
BaseView
public interface IBaseView {
BaseActivity getCurrentContext();
BasePresenter getPresenter();
}
It works fine but I feel like this is bad designed so I want to use Mosby instead. The problem is that all of the tutorials don't touch aspect of base classes, they just use Mosby's ones as base (with is bad I suppose? couse I have to duplicate my code (Butterknife.bind() for example). So can you guys give me some good designed quickstart classes for Mosby MVP or give me some tips how should I divide my project? Thanks!
So I see two possibilities:
You could extend from Mosby's MvpActivity as your base class and add your staff like initView(), initLibraries() etc. So that BaseActivity<P extends BasePresenter<? extends BaseView>> extends MvpActivity<P> implements BaseView. Then MyFooActivity extends BaseActivity<FooPresenter>. So you include Butterknife once in BaseActivity and it should work. However, you might have to duplicate that code like Butterknife.bind()` for Fragments, as Activity and Fragments obviously don't have the same super class. I will show you how to solve that above.
Do the other way around: Integrate Mosby's functionality into your BaseActivity. Mosby is build with the principle of "favor composition over inheritance". So what does this actually mean? Mosby offers a ActivityMvpDelegate. As the name already suggests this delegate does all the work of instantiating Presenter etc. But instead of inheriting from MvpActivity you use this delegate and invoke the corresponding delegate methods. Actually Mosby's MvpActivity is doing exactly that if you have a look at the source code. So instead of extending from Mosby'sMvpActivity you simply use MvpActivityDelegate in your BaseActivity.
So what about duplicating code like Butterknife.bind() i.e. in Activity and Fragment. Well, Mosby can share his code like instantiating Presenter etc. between Activity and Fragment because both use the mosby delegate.
So you could apply the same principle: You could put the shared code into a delegate and call the delegate from both, activity and fragments.
The question is: is it worth i.e. Butterknife.bind() is just one single call. You would also have to make one single call yourDelegate.doSomething() ...
But if you have to reuse "critical code" between activity and fragments then favor composition like Mosby does.
If you know that you are only working with Activites then extending from Mosby's MvpActivity would also be a good option as described in 1. solution.
I just wanted to add to sockeqwe's first answer.
It is perfectly fine to create your own base class where it makes sense. It's also pretty straightforward.
For example, I needed to create a base Fragment with some default behavior. All you need to do is duplicate the base generic type signature and pass it along to the base class.
For example:
public abstract class MyBaseFragment<V extends MvpView, P extends MvpPresenter<V>> extends MvpFragment<V, P>
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.
Is there any way in android to intercept activity method calls (just the standart ones, like "onStart. onCreate")?
I have a lot of functionality that must be present in every activity in my app, and (since it uses different types of activities (List, Preferences)) the only way to do it is to create my custom extensions for every activity class, which sucks :(
P.S. I use roboguice, but since Dalvik doesn't support code generation at runtime, I guess it doesn't help much.
P.S.S. I thought about using AspectJ, but it's too much of a hassle since it requires a lot of complications (ant's build.xml and all that junk)
The roboguice 1.1.1 release includes some basic event support for components injected into a context. See http://code.google.com/p/roboguice/wiki/Events for more info.
For Example:
#ContextScoped
public class MyObserver {
void handleOnCreate(#Observes OnCreatedEvent e) {
Log.i("MyTag", "onCreated");
}
}
public class MyActivity extends RoboActivity {
#Inject MyObserver observer; // injecting the component here will cause auto-wiring of the handleOnCreate method in the component.
protected void onCreate(Bundle state) {
super.onCreate(state); /* observer.handleOnCreate() will be invoked here */
}
}
You could delegate all the repetitive work to another class that would be embedded in your other activities. This way you limit the repetitive work to creating this object and calling its onCreate, onDestroy methods.
class MyActivityDelegate {
MyActivityDelegate(Activity a) {}
public void onCreate(Bundle savedInstanceState) {}
public void onDestroy() {}
}
class MyActivity extends ListActivity {
MyActivityDelegate commonStuff;
public MyActivity() {
commonStuff = MyActivityDelegate(this);
}
public onCreate(Bundle savedInstanceState) {
commonStuff.onCreate(savedInstanceState);
// ...
}
}
This minimalises the hassle and factorises all common methods and members of your activities. The other way to do it is to subclasse all the API's XXXActivty classes :(
Take a look at http://code.google.com/p/android-method-interceptor/, it uses Java Proxies.