Dagger 2 - How #ContributesAndroidInjector behaves when another provision method depends on its return type [duplicate] - dagger-2

This question already has answers here:
Why #ContributesAndroidInjector doesn't provide Android Framework type
(2 answers)
Closed 3 years ago.
I have two provision methods, one depending on the object the other method provides. Below code works properly the problem is stated under the section of Problem
Working Example
In StringProviderModule, added a method which is dependent on the object (Fragment instance) provided in the parent component.
#Module
class StringProviderModule {
#Module
companion object{
#JvmStatic
#Provides
fun provideFragmentArgument(cripledFragment: CripledFragment): String{
return cripledFragment.arguments?.getString(CripledFragment.ARG_STRING) ?: "No such argument"
}
}
}
In FragmentModule, added StringProviderModule to FragmentModule's generated subcomponent
#Module
abstract class FragmentModule {
#ContributesAndroidInjector(modules = [StringProviderModule::class])
abstract fun fragment: CripledFragment
}
In ActivityBinderModule, adding FragmentModule to the ActivityBinderModule's generated subcomponent
#Module
abstract class ActivityBinderModule {
#ContributesAndroidInjector(modules = [FragmentModule::class])
abstract fun mainActivity(): MainActivity
}
In AppComponent, adding ActivityBinderModule as a module
#Component(modules = [
AndroidSupportInjectionModule::class,
ActivityBinderModule::class
])
interface AppComponent: AndroidInjector<DaggerMyApp> {
#Component.Builder
interface Builder{
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
}
Then #Injecting String into Presenter class and #Injecting that presenter into CripledFragment. I can access Fragment's String argument in Presenter. Implementation is not important as it's outside the scope of the question, I can add code if needed, though.
Problem
When merging StringProviderModule and FragmentModule, IDE complains with following message "CripledFragment cannot be provided without an #Inject constructor or an #Provides-annotated method". Isn't #ContributesAndroidInjector already providing an instance of CripledFragment?
#Module
abstract class FragmentModule {
#ContributesAndroidInjector
abstract fun cripledFragment(): CripledFragment
#Module
companion object{
#JvmStatic
#Provides
fun provideFragmentArgument(cripledFragment: CripledFragment): String{
return cripledFragment.arguments?.getString(CripledFragment.ARG_STRING) ?: "No such argument"
}
}
}
I have checked multiple tutorials and dagger docs, couldn't find any proper explanation to infer the cause of my problem. Simple explanation rather than suggesting a solution is much appreciated as I'm not looking for a concrete solution.

This question has been answered here. Basically, #ContributesAndroidInjector provides MainActivity instance for a subcomponent it generates not for a module (in this case ActivityBinderModule) it resides in. That means any provision method inside FragmentModule will have access to MainActivity's instance.
#Module
abstract class ActivityBinderModule {
#ContributesAndroidInjector(modules = [FragmentModule::class])
abstract fun mainActivity(): MainActivity
}

Related

ContributesAndroidInjector same fragment with different dependencies

I've been trying to find a way to provide dependencies depending where a fragment is navigated from. I have the following in a library module:
interface FruitProvider {
fun provide(): String
}
class FruitFragment : Fragment() {
#Inject
lateinit var provider: FruitProvider
override fun onAttach(context: Context) {
AndroidSupportInjection.inject(this)
super.onAttach(context)
}
}
The idea is for other modules to consume this library and inject whatever they want as a FruitProvider.
As an example, if I have 2 providers:
class BananaProvider #Inject constructor() : FruitProvider {
override fun provide() = "banana"
}
class PearProvider #Inject constructor() : FruitProvider {
override fun provide() = "pear"
}
If I'm in the banana module I'd like to create and display an instance of FruitFragment injected with BananaProvider. Similarly for the pear module but with a PearProvider.
Background
Right now we have a method in FruitFragment to set the provider, i.e., setFruitProvider(provider: FruitProvider) that is called by the consumers of the library.
This approach is not ideal because if the activity gets recreated this field is null. At least this is what some debugging seems to suggest. Put simply, the fragment is recreated but the setFruitProvider is not being called again since it gets called from methods outside the fragment's lifecycle.
The interface FruitProvider cannot be Serializable so we cannot save it in the instance state. All that we think is left is to inject the fragment since this can be done by the fragment itself when attached.

Why it still works without installing AndroidInjectionModule?

According to Dagger documentation about injecting activity objects, it says that installing AndroidInjectionModule in your application component. However, everything is fine without it.
Does it means that I don't need to declare it? Under what circumstances will it be wrong?
For example:
Injected instance
data class Food(val name: String)
Module
#Module
class FoodModule{
#Provides
fun provideFood(): Food {
return Food("cholocate")
}
}
BindingModule
#Module
abstract class MainActivityModule {
#ContributesAndroidInjector(modules = [FoodModule::class])
abstract fun FoodShop(): MainActivity
}
AppComponent (Without installing AndroidInjectionModule)
#Component(modules = [MainActivityModule::class])
interface AppComponent{
fun inject(app: App)
}
App
class App : Application(), HasActivityInjector {
#Inject
lateinit var dispatchingActivityInjector: DispatchingAndroidInjector<Activity>
override fun onCreate() {
super.onCreate()
DaggerAppComponent.create().inject(this)
}
override fun activityInjector(): AndroidInjector<Activity> {
return dispatchingActivityInjector
}
}
MainActivity
class MainActivity : AppCompatActivity() {
#Inject
lateinit var food: Food
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d("test", "Get ${food.name}")
}
}
It get chocolate successfully in MainActivity.
Does it means that I don't need to declare it? Under what circumstances will it be wrong?
It actually seems like you don't need to declare it, but it might lead to compile errors if you don't.
If you have a look at AndroidInjectionModule you can see that it just lists a bunch of #Multibinds methods for framework types.
#Multibinds
abstract Map<Class<? extends Activity>, AndroidInjector.Factory<? extends Activity>>
activityInjectorFactories();
Now if you look up Declaring #Multibinds you can read that
You do not have to use #Multibinds for sets or maps that have at least one #IntoSet, #ElementsIntoSet, or #IntoMap binding, but you do have to declare them if they may be empty.
And to declare them if they may be empty is exactly what the AndroidInjectionModule module is doing for you. If the Android Injection parts would require an undefined Map of injector factories you would probably get a compile time error stating that it cannot be provided.
The reason that you don't need the module is because you're using #ContributesAndroidInjector, of which the generated code will contain a #Binds #IntoMap etc. method, that declares the bindings map. Stated above—as it is not empty anymore—you would not need the additional #Multibinds declaration that AndroidInjectionModule provides for the non-empty multibinding.
You might not need the module, but it will declare all the framework injector factories for you in case that they might be empty, possibly preventing one or two compile errors. After all the javadoc simply states that it should be installed, not that it must.
This module should be installed in the component that is used to inject the Application class.

AS3 Eclipse: How to create template to extends myClass?

How do I create a template that each time when I create a class that extends MyClass, it will automatically add 3 functions.
EDIT:
In other words I am trying to implement Abstract functionality in AS3. Assume that MyClass have both private and protected methods.
I see the only way to write own code template and call it every time you need, in Flash Builder: window->preference->flash builder->editors->code template->action script->new and give the name to the template, for instance myclass.
You can use existed templates as an example for template syntax.
Template code for MyClass child class with three methods:
import my.package.MyClass
/**
* #author ${user}
*/
public class ${enclosing_type} extends MyClass
{
public function ${enclosing_type}()
{
}
override public function publicMethod():void
{
}
override protected function protectedMethod():void
{
}
override private function privateMethod():void
{
}
${cursor}
}
Usage:
Create new "action script file" or "new class",
remove all file content
type myclass and choose from auto-complete options template myclass
If you are actually extending MyClass, all of MyClass's functions are already available to your descendants. You can also override either of them with old header and desired new body, and still be able to call older versions of those functions via super qualifier. So, you add those functions to MyClass and let them be.
Another way is to make an interface - it's a set of declarations without any function bodies, which you have to implement in any class that wants this interface in its content. A short introduction to interfaces. Then your MyClass will be an interface, with 3 function declarations in it, and whichever class will be declared as implements MyClass will have to provide bodies for these functions.
Check other keywords on that page, including extends and implements.
Hope this helps.
EDIT: There are no abstract classes in AS3, however you can emulate abstract functions in a normal class via exception throwing:
protected function abstractFunction(...params):void {
throw new Error("Abstract!");
}

Does Google Dart support mixins?

I've skimmed through the language documentation and it seems that the Google Dart does not support mixins (no method bodies in interfaces, no multiple inheritance, no Ruby-like modules). Am I right about this, or is there another way to have mixin-like functionality in Dart?
I'm happy to report that the answer is now Yes!
A mixin is really just the delta between a subclass and a superclass. You can then "mix in" that delta to another class.
For example, consider this abstract class:
abstract class Persistence {
void save(String filename) {
print('saving the object as ${toJson()}');
}
void load(String filename) {
print('loading from $filename');
}
Object toJson();
}
You can then mix this into other classes, thus avoiding the pollution of the inheritance tree.
abstract class Warrior extends Object with Persistence {
fight(Warrior other) {
// ...
}
}
class Ninja extends Warrior {
Map toJson() {
return {'throwing_stars': true};
}
}
class Zombie extends Warrior {
Map toJson() {
return {'eats_brains': true};
}
}
Restrictions on mixin definitions include:
Must not declare a constructor
Superclass is Object
Contains no calls to super
Some additional reading:
http://www.dartlang.org/articles/mixins/
http://blog.sethladd.com/2013/03/first-look-at-dart-mixins.html
Edit:
The Dart team have now released their proposal for Mixins, the original issue for Mixins was here.
It's not implemented yet, but in the meantime I've released an extensible Mixins library for Dart which includes a port of the popular Underscore.js functional utility library: https://github.com/mythz/DartMixins

How to test annotation object in the class that implement AbstractModule

I got a question regarding binding and annotation.
I have the following class:
public class MailFacadeImpl implements MailFacade {
private final PersonDao personDao;
#Inject
public MailFacadeImpl(#Mail PersonDao personDao) {
super();
this.personDao = personDao;
}
The PersonDao is annotated with a custom annotation.
I would like to be able to test this annotation inside the class that implement AbstractModule.
here is a piece of code:
bind(new TypeLiteral<SecurityRulesFactory<Person>>(){}).toProvider(FactoryProvider.newFactory(
new TypeLiteral<SecurityRulesFactory<Person>>(){}, new TypeLiteral<MailSecurityRulesCrdb>(){}));
I would like to have somthing similar to :
if(PersonDAO is annotated with(Mail.class) ){
bind(new TypeLiteral<SecurityRulesFactory<Person>>(){}).toProvider(FactoryProvider.newFactory(
new TypeLiteral<SecurityRulesFactory<Person>>(){}, new TypeLiteral<MailSecurityRulesCrdb>(){}));
}
Do you think it's possible?
thx for your help :-)
Have a nice friday!
It's not clear why you want your module to do this test. Instead, your module can specify how to get or create an instance of PersonDao for injection points annotated with Mail:
bind(PersonDao.class).annotatedWith(Mail.class).to(EmailAwarePersonDao.class);
Note that your PersonDao.class.isAnnotationPresent(Mail.class) won't help here, since the PersonDao class itself isn't annotated with Mail; the parameter to the MailFacadeImpl constructor has that annotation. There are ways to test for that, but if you are trying to do that from a Guice module, you're probably doing something wrong.