I would like to implement a part of an application that takes some steps that would be handled by one presenter. I have declared one scope:
#Scope
annotation class FormScope
next, I wanted to declare a module that would provide necessary dependencies:
#Module
object FormModule {
#JvmStatic
#Provides
fun providesFragmentManager(activity: FragmentActivity): FragmentManager = activity.supportFragmentManager
#JvmStatic
#Provides
fun providesNavigation(fragmentManager: FragmentManager): SobergridCoachingNavigationUnauthorizedFirstStep = SobergridCoachingNavigationUnauthorizedFirstStep(fragmentManager)
#JvmStatic
#Provides
fun providesCoordinator(navigation: NavigationUnauthorized): CoordinatoUnauthorized = CoordinatoUnauthorized(navigation)
#JvmStatic
#Provides
#Reusable
fun providesPresenter(coordinator: CoordinatoUnauthorized): OnboardingFragmentContract.Presenter = FormPresenter(coordinator)
}
and finally, I bind the modules into fragments that I want inject dependencies into:
#Module(includes = [AndroidSupportInjectionModule::class])
abstract class FragmentBindingModule {
#FormScope
#ContributesAndroidInjector(modules = [FormFirstModule::class, FormModule::class])
abstract fun contributesFormFirst(): FormFirstFragment
#ContributesAndroidInjector(modules = [FormSecondModule::class, FormModule::class])
abstract fun contributesFormSecond(): FormSecondFragment
#ContributesAndroidInjector(modules = [FormThirdModule::class, FormModule::class])
abstract fun contributesFormThird(): FormThirdFragment
}
The problem that I encounter is that every single time a new fragment is showed the Dagger creates a new instance of the Presenter. I want to use a single presenter for all of those Fragments. What I do wrong? What should I improve to be able to achieve my goal?
UPDATE
I have also tried annotating my provide method with #Singleton
#JvmStatic
#Provides
#Signleton
fun providesPresenter(coordinator: CoordinatoUnauthorized): OnboardingFragmentContract.Presenter = FormPresenter(coordinator)
but this leads to the compilation error. The last thing that I tried was to put annotations (both #Reusable and #Singleton) before the declaration of the Presenter class. This approach gives me no compilation errors but still, there is more than one instance of the Presenter class.
Move your Presenter provides to FragmentActivity Module with Scope to get the same Presenter for all fragments in Activity
#Module
public class FragmentActivityModule {
//common provides for all fragments
#Provides
#FormScope
public YourPresenter providesYourPresenter() {
return new YourPresenter();
}
....
And your builder have to look like this
#Module
public abstract class ActivityBuilder {
#FormScope
#ContributesAndroidInjector(modules = {FragmentActivityModule.class, Form1FragmentProvider.class
, Form2FragmentProvider.class})
abstract FragmentActivity bindFragmentActivity();
In Form1FragmentModule are provides only for Form1Fragment.
Create FragmentProviders for all fragments Form1FragmentProvider, Form2FragmentProvider...
#Module
public abstract class Form1FragmentProvider {
#ContributesAndroidInjector(modules = Form1FragmentModule.class)
abstract Form1Fragment provideForm1FragmentFactory();
}
Do not forget implement HasSupportFragmentInjector in your FragmentActivity
public class FragmentActivity extends AppCompatActivity implements HasSupportFragmentInjector {
#Inject
DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;
....
#Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return fragmentDispatchingAndroidInjector;
}
Do not forget attach AndroidSupportInjection in your Fragments
#Override
public void onAttach(Context context) {
AndroidSupportInjection.inject(this);
super.onAttach(context);
}
Related
So i want to build multi modular android project and have a problem with injecting the interface and the implementation.
I have two interface for navigation between fragment
first interface
interface Fragment1Navigation {
fun navigateToFragment1()
fun navigateToFragment2()
}
second interface
interface Fragment2Navigation {
fun navigateToFragment3()
fun navigateToFragment4()
}
then i have the class that implement those two interfaces
class Navigator: BaseNavigator(), Fragment1Navigation, Fragment2Navigation {
override fun navigateToFragment1() {
// some implementation
}
override fun navigateToFragment2() {
// some implementation
}
override fun navigateToFragment3() {
// some implementation
}
override fun navigateToFragment4() {
// some implementation
}
}
i want to inject this Navigator class in my mainActivity to bind my navcontroller first and also i want to inject the interface in other fragment and i made the module class like this
#Module()
class NavigationModule {
#Provides
#Singleton
fun provideNavigator(): Navigator = Navigator()
#Provides
fun provideFragment1Navigation(): Fragment1Navigation = Navigator()
#Provides
fun provideFragment2Navigation(): Fragment2Navigation = Navigator()
}
I want those provideFragmentNavigation and provideNavigatioin to provide the same Navigator() so interface in fragment lead to same navigator like in main activity, but turns out it provide different instance so the interface in fragment lead to different navigator from mainactivity
Two ways u can achieve this:
using #Binds instead of #Provides
passing the Navigation() object directly to the constructor of NavigationModule
using binds
class Navigator #Inject constructor(): BaseNavigator(), Fragment1Navigation, Fragment2Navigation {
// body
}
Module
#Module()
abstract class NavigationModule {
#Binds
abstract fun provideNavigator(navigator: Navigator): BaseNavigator
#Binds
abstract fun provideFragment1Navigation(navigator: Navigator): Fragment1Navigation
#Binds
abstract fun provideFragment2Navigation(navigator: Navigator): Fragment2Navigation
}
I'm creating an application in Kotlin using the MVP pattern.
I would need to inject a Repository into my Presenter for this purpose. Except that for this, my Repository requires a Retrofit interface as a parameter of its constructuor.
I'm a beginner in the use of Dagger2, and the answers found on the internet are far too complicated for such a basic case like mine.
Here's the repository i want to be injected :
class RepositoryInventory(private val api: Service): IRepositoryInventory {
override fun getInventoryItemByNum(itemnum: String): Observable<Response<Item>> {
return api.getInventoryItemByNum(itemnum)
.toObservable()
}
override fun getAllInventoryItems(): Single<Response<Item>> {
return api.getAllInventoryItems()
}
}
My Component
#Singleton
#Component(modules = arrayOf(ActivityModule::class))
interface ActivityComponent {
fun inject(loginActivity: LoginActivity)
fun inject(itemDetailActivity: ItemDetailActivity)
}
My module :
#Module
class ActivityModule(private var activity: Activity) {
#Provides
fun provideActivity(): Activity {
return activity
}
#Provides
fun provideLoginPresenter(): LoginPresenter {
return LoginPresenter()
}
#Provides
fun provideItemDetailPresenter(): ItemDetailPresenter {
return ItemDetailPresenter()
}
}
In my activity, my module is injected with this method :
private fun injectDependency() {
val activityComponent = DaggerActivityComponent.builder()
.activityModule(ActivityModule(this))
.build()
activityComponent.inject(this)
}
I have 2 components and 2 modules: one designed to inject into a fragment and the other into an activity.
Except in my case, I want to inject into a Presenter that is not a Fragment or an Activity but a class
Ok, my guess is you want to inject RepositoryInventory into LoginPresenter. If so, you can make use of #ContributesAndroidInjector and Binds
First, create a LoginActivityModule
#Module
abstract class LoginActivityModule {
#Binds
abstract fun loginPresenter(loginPresenter: LoginPresenter): LoginPresenter
}
Then, create a module called ActivityBindingModule
#Module
abstract class ActivityBindingModule {
#ContributesAndroidInjector(modules = [LoginActivityModule::class])
abstract fun loginActivity(): LoginActivity
#ContributesAndroidInjector()
abstract fun itemDetailActivity(): ItemDetailActivity
}
And change your ActivityComponent like this
#Singleton
#Component(modules = arrayOf(ActivityModule::class, ActivityBindingModule::class))
interface ActivityComponent {
}
And in your LoginPresenter:
class LoginPresenter #Inject constructor(private val repositoryInventory: IRepositoryInventory) {
...
}
Remember to remove this in ActivityModule:
#Provides
fun provideLoginPresenter(): LoginPresenter {
return LoginPresenter()
}
I have a class called AlertManager which requires Activity instance to show Toast and AlertDialog.
class AlertManager #Inject constructor(private val activity: Activity) {
fun showToast(message: String) {
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show()
}
}
Now, I want AlertManager as dependency in two activities HomeActivity & ProductsActivity. Currently I have created modules for each Activity like:
#Module
class HomeActivityModule {
#Provides
#ActivityContext
fun provideAlertManager(activity: HomeActivity) = AlertManager(activity)
}
And
#Module
class ProductsActivityModule {
#Provides
#ActivityContext
fun provideAlertManager(activity: ProductsActivity) = AlertManager(activity)
}
And binding them with Dagger like
#Module
abstract class ActivityProvider {
#ContributesAndroidInjector(modules = [HomeActivityModule::class])
#ActivityContext
abstract fun bindHomeActivity(): HomeActivity
#ContributesAndroidInjector(modules = [ProductsActivityModule::class])
#ActivityContext
abstract fun bindProductsActivity(): ProductsActivity
}
Now my questions are:
1) How can I avoid creating modules for each activities and have common ActivityModule which I can bind with whatever Activity I want?
2) Let's say I have a fragment called HomeFragment inside HomeActivity, then how can I inject the same AlertManager instance of HomeActivity inside the fragment?
I am stuck here since quite long and have tried to find a lot over internet but I am unable to find any blog or guide which can help me to achieve what I am looking for. If someone can point me in right direction, I'll be grateful.
1) How can I avoid creating modules for each activities and have common ActivityModule which I can bind with whatever Activity I want?
You can have some sort of AlertManagerModule where you add generic activity.
#Provides
fun provideAlertManager(activity: Activity) = AlertManager(activity)
You still will have to make individual activity modules. One change you can make is:
#Module
abstract class HomeActivityModule {
#Binds
abstract fun providesActivity(activity: HomeActivity) : Activity
}
And then you can add them to the ActivityProvider class:
#Module
abstract class ActivityProvider {
#ContributesAndroidInjector(modules = [HomeActivityModule::class, AlertManagerModule::class])
abstract fun bindHomeActivity(): HomeActivity
#ContributesAndroidInjector(modules = [ProductsActivityModule::class, AlertManagerModule::class])
abstract fun bindProductsActivity(): ProductsActivity
}
2) Let's say I have a fragment called HomeFragment inside HomeActivity, then how can I inject the same AlertManager instance of HomeActivity inside the fragment?
Since you're using DaggerActivity and most likely using DaggerFragment, the fragment instantiated in the HomeFragment can directly get the AlertManager by simply using the #Inject annotation in the fragment provided you add in the HomeActivityModule:
#Module
abstract class HomeActivityModule {
#Binds
abstract fun providesActivity(activity: HomeActivity) : Activity
#FragmentScope
#ContributesAndroidInjector
abstract fun providesHomeFragment() : HomeFragment;
}
I have been trying to, unsuccessfully, inject the Parent Fragment into its sub fragments for navigation purposes. I have followed a couple of different posts but I can't seem to understand what am I missing in my implementation.
I have a MainActivity that contains a ViewPager with one such page containing EventsFragment. This fragment in turn has two child fragments EventsListFragment and EventsDetailFragment. I wanted to inject EventsFragment fragment into EventsListFragment so that I can tell it to navigate to EventsDetailFragment.
I know this is probably a repetition of the posts below and I really apologize for that but I honestly cannot see what am I missing. These are the posts I've found:
Dagger 2.10 Android subcomponents and builders
How to create custom scoped modules in dagger 2.10
https://google.github.io/dagger/subcomponents.html
My implementation is as follows:
ApplicationComponent
#Singleton
#Component(modules = [
AndroidSupportInjectionModule::class,
ActivityModule::class,
BaseApplicationModule::class,
ApplicationModule::class])
interface ApplicationComponent : AndroidInjector<AndroidApplication> {
#Component.Builder
abstract class Builder : AndroidInjector.Builder<AndroidApplication>()
}
ActivityModule
#Module(includes = [MainActivityProvider::class])
abstract class ActivityModule{
}
MainActivityProvider
#Module(includes = [EventsFragmentProvider::class])
abstract class MainActivityProvider {
#PerActivity
#ContributesAndroidInjector(modules = [MainActivityModule::class])
abstract fun provideMainActivityFactory(): MainActivity
}
EventsFragmentProvider
#Module(includes = [EventsListProvider::class,
EventsDetailProvider::class])
abstract class EventsFragmentProvider {
#PerFragment
#ContributesAndroidInjector(modules = [EventsFragmentModule::class])
abstract fun provideEventsFragmentFactory(): EventsFragment
}
EventsFragmentModule
#Module
class EventsFragmentModule {
#Binds
abstract fun providesEventListView(eventsFragment: EventsFragment): EventContract.Flow
}
EventsListProvider
#Module
abstract class EventsListProvider {
#PerFragment
#ContributesAndroidInjector(modules = [EventsListModule::class])
abstract fun provideEventsListFragmentFactory(): EventsListFragment
}
EventsFragment
class EventsFragment : DaggerFragment(), EventContract.Flow {
override fun navigateToList() {
addFragment(navigator.getEventsListFragment(context!!))
}
override fun navigateToDetail(id: String) {
println("id = ${id}")
}
...
}
EventContract
interface EventContract {
interface Flow {
fun navigateToList()
fun navigateToDetail(id: String)
}
}
EventsListFragment
class EventsListFragment : DaggerFragment() {
#Inject
lateinit var eventsFlow: EventContract.Flow
...
}
Error
[Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)] EventContract.Flow cannot be provided without an #Provides-annotated method.
public abstract interface ApplicationComponent extends dagger.android.AndroidInjector<AndroidApplication> {
^
EventContract.Flow is injected at
EventsListFragment.eventsFlow
EventsListFragment is injected at
dagger.android.AndroidInjector.inject(T)
component path: ApplicationComponent →EventsListProvider_ProvideEventsListFragmentFactory.EventsListFragmentSubcomponent
I may be using a anti-pattern but this is what got me working to this point. Im open to changes that may help me achieve this
The error is caused by the following module:
#Module
class EventsFragmentModule {
#Binds
abstract fun providesEventListView(eventsFragment: EventsFragment): EventContract.Flow
}
Dagger would not provide you with a Fragment and you shouldn't do that.
Moreover, I think you misunderstand the meaning of #ContributesAndroidInjector. It means creating an AndroidInjector for you but proving an instance.
#Module
abstract class EventsListProvider {
#PerFragment
#ContributesAndroidInjector(modules = [EventsListModule::class])
abstract fun provideEventsListFragmentFactory(): EventsListFragment
}
So you should pass your EventsFragment instance into the module like this post instead of using field injection.
On Android, When using Dagger2, I have to call the following line on every activities that uses apiService:
#Inject
public ApiService apiService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerApiComponent.builder()
.activityModule(new ActivityModule(this))
.build()
.inject(this);
//...
}
How can I summarize it to something like:
DaggerApiComponent.builder()
.activity(this)
.build()
.inject(this);
or even simpler to something like:
MyApplication.injectApiService(this);
How should I change my component and modules to use Dagger2 with less copy-pasting code in my activities?
Here is my ApiComponent:
#Singleton
#Component(modules = ApiModule.class)
public interface ApiComponent {
void inject(MainActivity activity);
void inject(...
}
Here is ApiModule:
#Module(includes = {RetrofitModule.class, ActivityModule.class})
public class ApiModule {
#Singleton
#Provides
public static ApiService provideApiService(Activity activity) {
//...
}
}
and ActivityModule:
#Module
public class ActivityModule {
private final Activity context;
public ActivityModule(Activity context) {
this.context = context;
}
#Singleton
#Provides
public Activity provideActivityContext() {
return context;
}
}
The approach of such "DI" has two problems:
like what the OP said: we need to copy & paste this boilerplate wherever we need injection, which is tedious and hard to refactor.
Injectee (e.g. Activity) should not know where the #Inject instance is from, what it cares about is just "hey, give me an instance of it".
To resolve above problems, dagger.android comes to the rescue.
Create AndroidInjector for each component.
// App component
#Singleton
#Component(
modules = [
AndroidSupportInjectionModule::class, // build-in module
ActivityBindingModule::class,
AppModule::class
]
)
interface AppComponent : AndroidInjector<MainApplication> {
// we need to bind `MainApplication` instance to this component,
// so we have a builder here.
#Component.Builder
abstract class Builder : AndroidInjector.Builder<MainApplication>()
}
// Each controller (e.g. `Activity` / `Fragment` / `Service`) subcomponents
#Module
abstract class ActivityBindingModule {
// will generate a FooActivitySubcomponent under ActivityBindingModule's component
#ActivityScoped
#ContributesAndroidInjector(modules = [FooModule::class])
internal abstract fun fooActivity(): FooActivity
#ActivityScoped
#ContributesAndroidInjector(modules = [BarModule::class])
internal abstract fun barActivity(): BarActivity
}
Wire up your AndroidInjectors, so it can do injection for us using provided injector in step 1.
// App component
class MainApplication : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder().create(this)
}
}
// Each controller subcomponents
class FooActivity : DaggerAppCompatActivity() {
#Inject lateinit var foo: Foo
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// no need to call inject() here anymore!
foo.doSth()
}
}
For a concrete example: check out iosched
You can use the Android Activity injector, the usage is described well here.