I'm having some troubles while I want to move some Dagger 2 boilerplate code in each activity to a BaseActivity.
BaseActivity extends AppCompatActivity
I have multiples activities, like:
ActivityA extends BaseActivity implements InterfaceA;
ActivityB extends BaseActivity implements InterfaceB;
...
In each activity I have a methods like this (where X is A, B, C, ... for each activity):
public void initActivity() {
ComponentX compX;
...
compX = appComponent.plus(new ModuleX(this)); // this == InterfaceX
...
compX.inject(this); // this == ActivityX
}
I was trying to reduce this code, moving it to the parent BaseActivity. But I'm having some problems to do it. I think that maybe with generics I could do it, but I don't know exactly how.
Here's the question: Which inject method are you calling, and can Java determine that at compile time?
As described in "A note about covariance", Dagger will generate code for any members-injection method you define, but only the static type you pass in.
#Component public interface YourComponent {
void injectBase(BaseActivity baseActivity);
void injectA(ActivityA activityA);
void injectB(ActivityB activityB);
void injectC(ActivityC activityC);
}
When calling injectA and passing an ActivityA instance, you'll get injection for the fields defined in ActivityA including the fields in BaseActivity. Same with ActivityB and ActivityC. However, if you call injectBase, Dagger will only inject the fields belonging to BaseActivity, even if the object you pass in happens to be an ActivityA, ActivityB, or ActivityC. Dagger generates code at compile time, so if you call injectBase, the injection will only happen for the fields on BaseActivity—because that's the code that was generated for BaseActivity's members injector, and those are the only fields Dagger knows how to inject for a BaseActivity parameter.
Naturally, because BaseActivity only knows that this is a subtype of BaseActivity, it can only call injectBase and not any specific subtypes. Importantly, this remains true even if all the names injectBase, injectA, and so forth, are all the same (like inject). The JVM will pick the narrowest overload it can determine at compile time, which will be inject(BaseActivity), which will inject BaseActivity's fields and nothing in subtypes. If you were to name them uniquely, you'd see which one you're calling, and why it's not injecting subtype fields.
Generics won't help here: You're looking for your Component to generate and call members injectors for ActivityA, ActivityB, and ActivityC. Generics will be erased, but furthermore the component can't take an arbitrary subclass of BaseActivity: Dagger can't generate code at compile time for types it might only encounter at runtime. You really need to prepare those types in Dagger at compile time.
One way around this is to allow the subtypes to inject themselves. The subtypes know that this is ActivityA (and so forth), and even though the code might look character-for-character the same, Java can identify the right type and compile it correctly.
// in BaseActivity
protected abstract void injectDependencies();
// in ActivityA
#Override protected void injectDependencies() { component.injectA(this); }
However, there's another recently-released option, using dagger.android, which uses Multibindings and (effectively) a Map<Class, MembersInjector> to dynamically inject the specific type you want. This works from a superclass, too, to the point that you can have your Activity extend DaggerActivity and everything will work just the way you'd like. (Consult the dagger.android.support package for your AppCompatActivity equivalent DaggerAppCompatActivity.)
Related
Is there a way in Dagger2 or in Dagger2 Android Injection support to inject the member instances without specifying the class names of the fragments.
I have a modular project where
The following line is asking to provide a binder Factory for the injectable class.
#Override
public void onAttach(Context context) {
AndroidSupportInjection.inject(this);
super.onAttach(context);
}
But my intention is to provide the injecting members through different modules in the project, where the I wouldn't need to specify the class name of the Fragment at all.
Is this possible in Dagger2 injection or not?
Is there a way in Dagger2 or in Dagger2 Android Injection support to inject the member instances without specifying the class names of the fragments.
tl;dr No. You always need a component that contains a .inject(FragmentInQuestion fragment) method for every Fragment you want to inject.
Dagger uses annotation processing at compile time to resolve all of your .inject(SomeFragment fragment) methods where it looks up fields annotated with #Inject and creates code to inject them.
It needs the actual class name, since it will only generate code for the class used with its fields.
class Fragment {
// no #Inject annotated fields, part of framework!
}
class SomeFragment extens Fragment {
#Inject SomeDependency dep;
}
class OtherFragment extens Fragment {
#Inject OtherDependency dep;
}
Declaring one generic component that injects all your fragments is impossible.
.inject(Fragment fragment) would not inject any properties, since Fragment does not contain any #Inject annotated fields. So neither of the dep fields in the above sample would be provided and they both would be null.
You could create a BaseFragment that contains common objects and write an injection for it, but again, any annotated fields of its children would not be supplied.
You could try some other workarounds, but in the end it would always mean that you'd be injecting or be working with a base type. While this might be feasible in some situations, I don't think it would work on more than some special edge cases.
The Android Injection part of Dagger 2 takes this approach and creates a generic interface that your components have to implement along with a Builder to bind the type in question.
interface AndroidInjector<T> {
inject(T instance)
}
By implementing this interface AndroidInjection can then look up (and create) the correct component and inject your Fragment. For the reasons mentioned above this will always be the actual class of your Fragment, and not some base type.
So by using the Android Injection part you won't be able to use some common base class, and even without it you'd have a hard time.
If you're looking for a way to move out the call of AndroidInjection.inject(this) to somewhere else you should look at the Google Android Architecture Components sample project where they use FragmentLifecycleCallbacks to inject the fragments globally at the right time.
The project I'm working on has a number of utility classes that require activity context.
I don't want to have to declare a new #Provides method for each activity that uses the dependency. i.e. I don't want this:
#Provides
static Navigator providesNavigator(ActivityOne activity) {
returns new Navigator(activity);
}
// ...and in another module
#Provides
static Navigator providesNavigator(ActivityTwo activity) {
returns new Navigator(activity);
}
So instead I declare these utilities in a single ActivityUtilitiesModule and pass our BaseActivity which all other activities extend. Now i don't have to declare my Navigator dependency x number of times.
#Provides
static Navigator(BaseActivity activity) {
return new Navigator(activity);
}
However, Dagger does not know how to satisfy the dependency for BaseActivity. This means for every Activity i need to create a provides method that will satisfy the BaseActivity dependency with the specific Activity being used. e.g.:
#Provides
static BaseActivity providesBaseActivity(ActivityOne activity) {
return activity;
}
This is better - I only need to repeat this one provider per activity, rather than repeating a provider for every utility class per activity, but it still feels like an unwelcome additional step in Dagger set up, and another thing that makes the code harder to understand.
Is there a pattern which allows me to avoid having to supply this BaseActivity provider per activity?
Please use Constructor Injection. Having provide* methods that only call the constructor are only noise and code to maintain. Add #Inject to the class constructor and possible scopes onto the class.
#SomePossibleScope
class Navigator {
#Inject
Navigator(Activity activity) { }
}
If you do this, you probably don't need your ActivityUtilitiesModule at all.
If your class depends on an Activity or BaseActivity then you need to provide it. And yes, you will have to tell Dagger about it in some way.
If you were to use an abstract class or interface you should make use of #Binds instead.
#Binds BaseActivity bindsBaseActivity(ActivityOne activity);
Compared to #Provides Dagger might optimize this code further, reducing the number of method calls and object creations, as well as a few less lines of code.
I don't know why your Utils depend on the Activity, but if they would only need a Context then you could also just provide the application context to them without a need to bind or provide your actual Activity.
I personally just bind the current Activity to the types it implements using the syntax above. And if you're using Constructor Injection properly that's more often than not the only lines of code that you'll find in my modules, making them very readable.
I have noticed that I can create a callback by using two methods:
Receive an interface at the constructor of the class implementing the callback.
Receive the activity itself at the constructor of the class implementing the callback.
First Approach
For example I could do this:
public MyClass(MyInterface listener) {
this.listener = listener;
}
And I could call myCallBackFunction() defined in MyActivity (which implements MyInterface) by writing listener.myCallBackFunction()
Second Approach
Or I could do this:
public MyClass(MyActivity activity) {
this.activity = activity;
}
And I could call myCallBackFunction() defined in MyActivity by writing activity.myCallBackFunction()
My concern: Is one approach better than the other? And if so, why?
Usually speaking, you'd better use first approach. The reason is here:
Suppose you have 4 classes, first is Vehicle, second is Bicycle, third is Bus and third is Subway. Bicycle, Bus and Subway are subclasses of Vehicle. There may be a method call drive(), which should have a parameter. Which one do you think best for parameter type? Bicycle, Bus, Subway, or Vehicle?
Apparently, passing Vehicle is best because you may want to add other kinds of vehicles in the future or you don't want to write nearly same code for different kinds of vehicles in your project. It is same to use Interface rather than specific class.
As a result, passing an interface to a method is always correct and better than passing a specific type of object to it. You can always implement the interface in other classes and they will also be parameter of that method. You don't need to think about actual type of the parameter, which will confuse you and make you think more about specific code for specific type. Instead, only one type, one piece of code macroscopically.
So the conclusion is: using MyActivity is good, but using MyInterface is better.
I think it can depend on what you're trying to achieve: the first approach may in general be better suited since MyClass is not required to know anything about the implementation of that interface method, so it's great for passing different objects (e.g. a RecyclerView Adapter being created with an OnItemClickedListener injected in the constructor can be re-used in different activities/fragments implementing the interface, whilst the adapter doesn't need to change). It helps to prevent coupling.
The second approach leaves one wondering: is MyClass tied to the activity lifecycle? It may still hold a reference to the activity after that activity has actually been destroyed by the system, which would leak memory as the Activity object is not garbage-collected. It's a matter of design, and can be seen as code smell, can you not achieve what you wanted within the activity itself, and rely on the lifecycle callbacks onCreate/.../onDestroy?
Is one approach better than the other? And if so, why?
Using Interface is the best way..
Assume that you are having
1) Activity MyActivity
2) class which extends Activity or View or Asynctask is Myclass.
Both MyActivity and Myclass are Implements MyInterface
If you are passing Activity you need to add one more constructor
public MyClass(MyActivity activity) {
this.activity = activity;
}
public MyClass(Myclass myclass) {
this.myclass= myclass;
}
If you are using interface
public MyClass(MyInterface listener) {
this.listener = listener;
}
that's it.
In one approach you are creating an instance of Interface and in another an instance of implementing activity. what is best is:
Interface interface;
public myClass(Activity acitivity)
{
interface = (Interface)activity;
}
i.e. typecast activity to interface. Now you can callback the overridden functions in Activity.
This way you can now loose information of Activity's functions and just access the overriden functions of interface from the activity.
You can avoid typecasting and create an object of Activity, if you need access to interface callbacks AND the activity's function/variables.
It depends on your needs.
I have some trouble to build activities with AndroidAnnotations.
I have a parent Activity named TemplateActivity:
#EActivity(R.layout.activity_template)
#NoTitle
public class TemplateActivity extends Activity
{
// some views
// ...
#ViewById(R.id.main_framelayout)
FrameLayout mainFrameLayout;
#AfterViews
public void postInit()
{
Log.d("DEBUG", "postInit"); // never called, strange...
}
public void setMainView(int layoutResID)
{
mainFrameLayout.addView(LayoutInflater.from(this).inflate(layoutResID, null));
}
}
And in my second Activity, I want to fill mainFrameLayout with anoter layout xml like that :
#EActivity
public class ChildActivity extends TemplateActivity
{
#Override
public void postInit()
{
super.postInit();
setMainView(R.layout.activity_child_one);
}
}
When I want to startActivity, my ChildActivity is blank and postInit was never called.
Can anybody tell me what is wrong? Thanks for advance.
The annotation in your parent class will result in a class TemplateActivity_ with the specified layout. The child class will inherit the "normal" stuff from that parent class, but have its own AA subclass (ChildActivity_). So you should specify the layout to use there as well. Just take a look at the generated classes to see what is going on there.
AA works by generating a new subclass for your annotated classes (e.g. TemplateActivity_ extends TemplateActivity) that contains the code necessary to achieve the results of your annotations. For example, in this class the onCreate() method will instantiate the layout needed, methods annotated with #Background get overridden with another implementation that calls the original method in a background thread. AndroidAnnotations doesn't really do anything at runtime, everything can be seen in the classes it generates, just look into the .apt_generated folder (or wherever you generated the classes to). This can also be helpful if it doesn't quite do what you want, because you can then just take a look at what it does and do it yourself in the way you need it.
In your case, the inheritance hierarchy is like this:
TemplateActivity (with annotations)
L--> TemplateActivity_ (with generated code for the layout)
L--> ChildActivity (your other class, no generated code)
L--> ChildActivity_ (with code generated for the annotations in ChildActivity)
Afaik not all annotations are passed on to subclasses.
Use #EActivity(R.layout.activity_child_one) in the child class and make the parent class abstract. That is working for me.
I think you should make you TempleteActivity an abstract class.
I have some common actions fired in onPause() and onResume() methods. (Like registering and unregistering BroadcatsReceivers)
Now I put them in abstract classes and extend Activity classes.
After creating some abstract classes with common actions I end up with situation when I can't extend Activity because of Java's lack of multiple inheritance.
How you deal with such things? Do you duplicate code or do something smarter?
I'm wondering if it's wider problem - not only concerning Android, but Java language.
Task A and Task B are two separate tasks. But to be used in just one class. So you can just have a single abstract class. The abstract may not be necessary if you feel that there are no functions that will have to be overridden in the child class.
If for some reason there may be a task D, which uses Task A alone, then you can do the following:
public abstract class ExtendedAbstractActivityA extends Activity
{
public void TaskA() {}
public void TaskB() {}
//Other abstract classes
}
You can call these tasks individually from your respective classes.