How to use Dagger2 in Android to inject activity objects easier? - android

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.

Related

Inject dependency that requires parameter in constructor

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()
}

Dagger 2 + MVP - single presenter assigned to multiple fragments

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);
}

How to inject Mocked Presenter of Activity in Instrumentation testing with Espresso

I have been trying this for a week. And I have crawled every article available but their implementations or examples fall short or stop at the steps of Espresso Tests.
My Android Application follows MVP architecture (And is in Java)
Scenario: [Giving just one example]
I have a HomeActivity which gets a HomePresenter using Dagger2. (Provides method in the HomeModule exposed through a void inject(HomeActivity activity) in the HomeComponent.
In my espressoTest for HomeActivity I would like to inject a mockpresent.
I Have not exposed this dependencies inside an AppModule through an AppComponent. which most examples on the net do (So they just create a new testApplication and then do the needfull)
I do not want to use the productFlavours way of injecting or providing mockclasses as it doesnt give me control over the Mockito.when methods.
So basically. I would like to inject a mockpresenter wherein i can do whatever Mockito.when()s on it for the sake of my unit tests in espresso.
My Code is below.
HomeComponent
#HomeScope
#Component(modules = HomeModule.class,dependencies = AppComponent.class)
public interface HomeComponent {
void inject(HomeActivity activity);
}
HomeModule
#Module
public class HomeModule {
private final IHomeContract.View view;
public HomeModule(IHomeContract.View view) {
this.view = view;
}
#Provides
#HomeScope
public IHomeContract.Presenter presenter(FlowsRepository flowsRepository, UserRepository userRepository, LoanRepository loanRepository) {
return new HomePresenter(view, flowsRepository, userRepository, loanRepository);
}
}
AppComponent
#Component(modules = {AppModule.class,RepositoryModule.class})
#AppScope
public interface AppComponent {
void inject(App app);
FlowsRepository flowRepository();
LoanRepository loanRepository();
UserRepository userRepository();
}
AppModule
#Module
public class AppModule {
private Context appContext;
public AppModule(#NonNull Context context) {
this.appContext = context;
}
#Provides
#AppScope
public Context context() {
return appContext;
}
}
App
component = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
component.inject(this);
HomeActivity
HomeComponent component = DaggerHomeComponent.builder()
.appComponent(((App) getApplication()).getComponent())
.homeModule(new HomeModule(this))
.build();
Once again. In my tests (espresso) i would like to inject a mockedHomePresenter the set by Mockito. So I can just unit test my views.
The key point in resolving the problem is to have such a Dagger Module that provides a mock Presenter in HomeActivity's instrumented test instead of the "real" one.
For this the following 2 extra actions need to be done (you might also want to see an example).
Delegate instantiation of HomeActivity's Component to some abstraction.
Substitute the implementation of the abstraction in instrumented tests to provide mocks.
I'll use Kotlin in the example below.
Define the delegate interface:
interface HomeComponentBuilder {
fun build(view: IHomeContract.View): HomeComponent
}
Move the HomeComponent initialisation from HomeActivity to the delegate implementation:
class HomeComponentBuilderImpl constructor(private val app: App) : HomeComponentBuilder {
override fun build(view: IHomeContract.View): HomeComponent =
DaggerHomeComponent.builder()
.homeModule(HomeModule(view))
.build()
}
Make the delegate be in application "scope" so that you could interchange its implementation for instrumented tests:
interface App {
val homeComponentBuilder: HomeComponentBuilder
...
}
App implementation should now contain
class AppImpl : Application(), App {
override val homeComponentBuilder: HomeComponentBuilder by lazy {
HomeComponentBuilderImpl(this#AppImpl)
}
...
}
Component initialisation in HomeActivity looks as follows:
(application as App)
.homeComponentBuilder
.build(this)
.inject(this)
For instrumented testing create TestHomeComponent that extends HomeComponent:
#HomeScope
#Component(modules = [TestHomeModule::class])
interface TestHomeComponent : HomeComponent
where TestHomeModule provides a mock Presenter
#Module
class TestHomeModule {
#Provides
fun providePresenter(): IHomeContract.Presenter = mock()
}
What's left to do is to make a test delegate implementation
class TestHomeComponentBuilderImpl : HomeComponentBuilder {
override fun build(view: IHomeContract.View): HomeComponent =
DaggerTestHomeComponent.builder()
.testTestHomeModule(TestHomeModule())
.build()
}
and initialise it in TestAppImpl
class TestAppImpl : Application(), App {
override val homeComponentBuilder: HomeComponentBuilder by lazy {
TestHomeComponentBuilderImpl()
}
...
}
The rest is standard. Create a custom AndroidJUnitRunner that uses TestAppImpl:
class TestAppRunner : AndroidJUnitRunner() {
override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application = Instrumentation.newApplication(TestAppImpl::class.java, context)
}
and add it to app module build.gradle
defaultConfig {
testInstrumentationRunner "your.package.TestAppRunner"
...
}
Usage example:
#RunWith(AndroidJUnit4::class)
class HomeActivityTest {
private lateinit var mockPresenter: IHomeContract.Presenter
#get:Rule
val activityRule = ActivityTestRule(HomeActivity::class.java)
#Before
fun setUp() {
mockPresenter = activityRule.activity.presenter
}
#Test
fun activity_onCreate_presenter_should_onViewCreated() {
verify(mockPresenter).someMethod()
}
}
So. Your problem is that you need to create a Module that provides a mock presenter for testing instead of the "real" one.
There is quite a good article on this here: Testing with Dagger

java.lang.IllegalArgumentException: No injector factory bound for Class<MyActivity_>

The error I have is following:
Caused by: java.lang.IllegalArgumentException: No injector factory
bound for Class. Injector factories were bound for
supertypes of MyActivity_: [MyActivity]. Did you mean to bind an
injector factory for the subtype?
As I understand it happens because I am using an AndroidAnnotations library.
AppComponent.class :
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
ActivityBindingModule.class })
public interface AppComponent extends AndroidInjector<DaggerApplication> {
#Component.Builder
interface Builder {
#BindsInstance Builder application(Application application);
AppComponent build();
}
void inject(Application application);
#Override
void inject(DaggerApplication instance);
}
AppModule.class :
#Module
public abstract class AppModule {
#Binds
abstract Context provideContext(Application application);
#Provides
#Singleton
static SharedPreferencesManager providesPreferences(Application application){
return SharedPreferencesManager_.getInstance_(application);
}
}
ActivityBindingModule.class :
#Module
public abstract class ActivityBindingModule {
#ContributesAndroidInjector(modules = LoginActivityModule.class)
#LoginActivityScope
abstract LoginActivity bindLoginActivity();
}
Application.class :
#EApplication
public class Application extends DaggerApplication {
#Inject
DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
AppComponent appComponent = DaggerAppComponent.builder()
.application(this)
.build();
appComponent.inject(this);
return appComponent;
}
}
LoginActivityModule.class
#Module
public class LoginActivityModule {
#Provides
#LoginActivityScope
#ActivityContext
public Context providesContext(LoginActivity loginActivity){
return loginActivity;
}
#Provides
#LoginActivityScope
public LoginViewModel providesLoginViewModel(TelephonyManager telephonyManager,
LoginModel loginModel,
SharedPreferencesManager sharedPreferencesManager,
LoginRemoteRepository loginRemoteRepository){
return new LoginViewModel(telephonyManager, loginModel, sharedPreferencesManager, loginRemoteRepository,
new CompositeSubscription());
}
#Provides
#LoginActivityScope
public LoginRemoteRepository providesRemoteRepository(#ActivityContext Context context,
MainApi mainApi,
SharedPreferencesManager sharedPreferencesManager){
return new LoginRemoteRepository(mainApi, sharedPreferencesManager, context.getContentResolver());
}
#Provides
#LoginActivityScope
public LoginModel provideLoginModel(){
return new LoginModel();
}
#Provides
#LoginActivityScope
public TelephonyManager provideTelephonyManager(Context context){
return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
}
}
LoginActivity.class :
#EActivity(R.layout.activity_login)
public class LoginActivity {
#Inject
LoginViewModel loginViewModel;
#AfterViews
void afterViews(){
AndroidInjection.inject(this);
}
}
How to deal with Dagger 2 error:
"Injector factories were bound for supertypes of ... Did you mean to bind an injector factory for the subtype?"
Suppose we do have some BaseActivity:
open class BaseActivity : MvpAppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
}
}
We do also have an ActivityBuilder Module (which provides the BaseActivity itself) and we have a Module which provides
the dependencies needed for the successful running of the BaseActivity.
#Module
abstract class ActivityBuilder {
#ContributesAndroidInjector(modules = [BaseModule::class])
abstract fun bindBaseActivity(): BaseActivity
}
If we will add a ChildActivity like that
class ChildActivity : BaseActivity() {
}
thinking: "Hey, Dagger 2 will satisfy the dependencies for the BaseActivity and since we are extending
it, we will get a ChildActivity up and running, right?". Wrong. We will get an exception "Injector factories were bound for supertypes of ... Did you mean to bind an injector factory for the subtype?"
What we should do? We should make Dagger 2 know explicitly about our ChildActivity.
a) Add AndroidInjection.inject(this) to the ChildActivity onCreate() method:
class ChildActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
}
}
b) add all subclasses of the BaseActivity to the ActivityBuilder module (which provides activities).
Note, that the BaseModule, satisfying dependencies for the BaseActivity, should also be included
to the #ContributesAndroidInjector of the child class (ChildActivity)
#Module
abstract class ActivityBuilder {
#ContributesAndroidInjector(modules = [BaseModule::class])
abstract fun bindBaseActivity(): BaseActivity
//Adding subclasses
#ContributesAndroidInjector(modules = [BaseModule::class])
abstract fun bindChildActivity(): ChildActivity
}
Just an informed guess: It probably happens because your Class is actually called different when you use AndroidAnnotations (they add the underscore somewhere). Then you have to define the binding also like so (not sure where the underscore goes):
#ContributesAndroidInjector(modules = LoginActivityModule_.class)
#LoginActivityScope
abstract LoginActivity bindLoginActivity();

Dagger 2 injecting Android Application Context

I am using Dagger 2 and have it working however I now need access to the Android Application Context.
Its not clear to me how to inject and get access to the context. I have tried to do this as follows:
#Module
public class MainActivityModule {
private final Context context;
MainActivityModule(Context context) {
this.context = context;
}
#Provides #Singleton
Context provideContext() {
return context;
}
}
However this results in the following exception:
java.lang.RuntimeException: Unable to create application : java.lang.IllegalStateException: mainActivityModule must be set
If I inspect the Dagger generated code this exception is raised here:
public Graph build() {
if (mainActivityModule == null) {
throw new IllegalStateException("mainActivityModule must be set");
}
return new DaggerGraph(this);
}
I am not sure if this is the correct way to get Context injected - any help will be greatly appreciated.
#Module
public class MainActivityModule {
private final Context context;
public MainActivityModule (Context context) {
this.context = context;
}
#Provides //scope is not necessary for parameters stored within the module
public Context context() {
return context;
}
}
#Component(modules={MainActivityModule.class})
#Singleton
public interface MainActivityComponent {
Context context();
void inject(MainActivity mainActivity);
}
And then
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
.mainActivityModule(new MainActivityModule(MainActivity.this))
.build();
It took me a while to find a proper solution, so thought it might save some time for others, as far as I could gather this is the preferred solution with the current Dagger version (2.22.1).
In the following example I need the Application's Context to create a RoomDatabase (happens in StoreModule).
Please if you see any errors or mistakes let me know so I'll learn as well :)
Component:
// We only need to scope with #Singleton because in StoreModule we use #Singleton
// you should use the scope you actually need
// read more here https://google.github.io/dagger/api/latest/dagger/Component.html
#Singleton
#Component(modules = { AndroidInjectionModule.class, AppModule.class, StoreModule.class })
public interface AwareAppComponent extends AndroidInjector<App> {
// This tells Dagger to create a factory which allows passing
// in the App (see usage in App implementation below)
#Component.Factory
interface Factory extends AndroidInjector.Factory<App> {
}
}
AppModule:
#Module
public abstract class AppModule {
// This tell Dagger to use App instance when required to inject Application
// see more details here: https://google.github.io/dagger/api/2.22.1/dagger/Binds.html
#Binds
abstract Application application(App app);
}
StoreModule:
#Module
public class StoreModule {
private static final String DB_NAME = "aware_db";
// App will be injected here by Dagger
// Dagger knows that App instance will fit here based on the #Binds in the AppModule
#Singleton
#Provides
public AppDatabase provideAppDatabase(Application awareApp) {
return Room
.databaseBuilder(awareApp.getApplicationContext(), AppDatabase.class, DB_NAME)
.build();
}
}
App:
public class App extends Application implements HasActivityInjector {
#Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
#Override
public void onCreate() {
super.onCreate();
// Using the generated factory we can pass the App to the create(...) method
DaggerAwareAppComponent.factory().create(this).inject(this);
}
#Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjector;
}
}
I have read this article and it was very helpful.
https://medium.com/tompee/android-dependency-injection-using-dagger-2-530aa21961b4
Sample code.
Update: I removed from AppComponent.kt these lines because are not necessaries
fun context(): Context
fun applicationContext(): Application
AppComponent.kt
#Singleton
#Component(
modules = [
NetworkModule::class,
AppModule::class
]
)
interface AppComponent {
fun inject(viewModel: LoginUserViewModel)
}
AppModule.kt
#Module
class AppModule(private val application: Application) {
#Provides
#Singleton
fun providesApplication(): Application = application
#Provides
#Singleton
fun providesApplicationContext(): Context = application
#Singleton
#Provides
fun providesNetworkConnectivityHelper(): NetworkConnectivityHelper{
return NetworkConnectivityHelper(application.applicationContext)
}
}
NetworkConnectivityHelper.kt
And only added #Inject constructor to pass the Context
class NetworkConnectivityHelper #Inject constructor(context: Context) {
private val connectivityManager =
context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
#Suppress("DEPRECATION")
fun isNetworkAvailable(): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val nc = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
nc != null
&& nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
&& nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
}
val networkInfo = connectivityManager.activeNetworkInfo
return networkInfo != null && networkInfo.isConnected
}
}
App class.kt
class App : Application() {
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
this.appComponent = this.initDagger()
}
private fun initDagger() = DaggerAppComponent.builder()
.appModule(AppModule(this))
.build()
}
Finally in my Activity I injected my helper
#Inject lateinit var networkConnectivity: NetworkConnectivityHelper
And YEI! it works for me.
Was not correctly building the Application component, needed to pass in the Application. This Dagger 2 example perfectly shows how to do this: https://github.com/google/dagger/tree/master/examples/android-simple/src/main/java/com/example/dagger/simple
Update:
Working link: https://github.com/yongjhih/dagger2-sample/tree/master/examples/android-simple/src/main/java/com/example/dagger/simple
probably we could inject the context as shown below:
the application component
#Component(
modules = [
(ApplicationModule::class),
(AndroidSupportInjectionModule::class),
(UiBindingModule::class)
]
)
interface ApplicationComponent : AndroidInjector<AndroidApplication> {
override fun inject(application: AndroidApplication)
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: AndroidApplication): Builder
#BindsInstance
fun context(context: Context): Builder
fun build(): ApplicationComponent
}
}
the custom application extending dagger application
class AndroidApplication : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerApplicationComponent.builder().application(this).context(this).build()
}
}
Example ApplicationModule
#Module
abstract class ApplicationModule {
/**
* Binds a background thread executor, which executes threads from a thread pool
* #param jobExecutor
* #return
*/
#Binds
internal abstract fun provideThreadExecutor(jobExecutor: JobExecutor): ThreadExecutor
/**
* Binds main ui looper thread
* #param uiThread
* #return
*/
#Binds
internal abstract fun providePostExecutionThread(uiThread: UIThread): PostExecutionThread
}
Example UI BindingModule
#Module
abstract class UiBindingModule {
#ContributesAndroidInjector(modules = [(MainActivityModule::class)])
internal abstract fun mainActivity(): MainActivity
#ContributesAndroidInjector(modules = [(MapFragmentModule::class)])
internal abstract fun mapFragment(): MapFragment
}
#Singleton
#Component(modules = [YourModule::class, ThatOtherModule::class])
interface ApplicationComponent {
#Component.Builder
interface Builder {
#BindsInstance fun applicationContext(applicationContext: Context): Builder
fun build(): ApplicationComponent
}
}
class YourApplication : Application() {
val component: ApplicationComponent by lazy {
DaggerApplicationComponent.builder()
.applicationContext(applicationContext)
.build()
}
}
Use the #BindInstance to declare a abstract function that provides the context dependency. i.e #BindsInstance fun applicationContext(applicationContext: Context): Builder
set the context using .applicationContext(applicationContext)

Categories

Resources