I'm a Dagger newb and have a trouble with using it.
What I want to develop is that using RxAndroidBle and to initialize it by Dagger for providing Context.
So I researched how it can be implemented, and I wrote some codes and It seems to be working for me but not working at all.
The followings are my codes.
AppComponent.kt
#Singleton
#Component(modules = [
AppModule::class,
BluetoothModule::class,
AndroidInjectionModule::class])
interface AppComponent : AndroidInjector<BluetoothController> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(app: Application): Builder
fun build(): AppComponent
}
}
AppModule.kt
#Module
class AppModule {
#Provides
#Named("appContext")
#Singleton
fun provideContext(application: Application): Context =
application.applicationContext
}
BluetoothModule.kt
#Module
class BluetoothModule {
#Provides
#Named("rxBleClient")
#Singleton
fun provideRxBleClient(#Named("appContext") context: Context):RxBleClient =
RxBleClient.create(context)
}
BluetoothController.kt for injecting by DaggerApplication.
class BluetoothController : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder().application(this).build()
}
}
I've inserted
android:name".BluetoothController"
to AndroidManifest.xml
And this is how I would use it.
#field:[Inject Named("rxBleClient")]
lateinit var rxBleClient: RxBleClient
But it always occurs an error says: lateinit property context has not been initialized
What things I've missed? Can anyone help me?
Thanks in advance.
Add the below code to make this happen.
Create ActivityBuilderModule for injecting within the activity. Consider our activity as MainActivity
#Module
abstract class ActivityBuilderModule {
#ContributesAndroidInjector(modules=[MainActivityModule::class])
abstract fun contributeSplashActivity(): MainActivity
}
Create your MainActivityModule
#Module
class MainActivityModule{
#Provides()
fun contributeSplashActivity(mainActivity: MainActivity):
MainActivity=mainActivity
}
Modify your component.
#Singleton
#Component(modules = [
AppModule::class,
BluetoothModule::class,
ActivityBuilderModule::class,
AndroidInjectionModule::class])
interface AppComponent : AndroidInjector<BluetoothController> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(app: Application): Builder
fun build(): AppComponent
}
}
Within MainActivity just inject.
class MainActivity{
...
#Inject
lateinit var rxBleClient: RxBleClient
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
}
}
Let us know in case of any issue.
the context object is not initialized before its called
Though i dont know, how chained your initialization are.
Use #Inject to add deppendencies, do something like this
#Module
class BluetoothModule(val context : Context) {
//#Inject private lateinit var context : Context
#Provides
#Named("rxBleClient")
#Singleton
fun provideRxBleClient():RxBleClient =
RxBleClient.create(context)
}
let your call be like this
val component = AppComponent()
component.bluetoothModule(appContext)
.//other calls here
.build()
Related
I'm trying to add UI tests for the first time to an app, and I was looking at using espresso.
The app uses Dagger2 for DI with #Inject annotations for classes that should be injectable, and AndroidInjection/AndroidSupportInjection in screens (Activity / Fragment).
class Application : android.app.Application(), HasActivityInjector, HasServiceInjector {
...
override fun onCreate() {
super.onCreate()
initDagger()
Timber.d("Application initialized successfully!")
}
protected open fun initDagger() {
Components.initialize(this)
}
}
object Components : ComponentFactory {
private lateinit var sComponent: AppComponent
fun initialize(app: Application) {
sAppComponent = DaggerAppComponent.builder()
.applicationModule(ApplicationModule(this))
.build()
}
// overrides
}
interface ComponentFactory {
fun app(): AppComponent
fun authenticated(): AuthenticatedComponent
}
Next the Components and Modules. The AppActivitiesModule and AuthenticatedActivitiesModule are classes with #ContributesAndroidInjector for screens.
#Singleton
#Component(modules = [AppModule::class, AppActivitiesModule::class, AndroidInjectionModule::class, AndroidSupportInjectionModule::class])
interface AppComponent {
fun authenticatedComponentBuilder(): AuthenticatedComponent.Builder
fun inject(app: Application)
#Component.Builder
interface Builder {
fun build(): AppComponent
fun applicationModule(applicationModule: ApplicationModule): Builder
}
}
#Module
open class AppModule(private val application: Application) {
// some #Provides
}
#AuthenticatedScope
#Subcomponent(modules = [AuthenticatedModule::class, AuthenticatedActivitiesModule::class])
interface AuthenticatedComponent {
fun inject(application: Application)
#Subcomponent.Builder
interface Builder {
fun userModule(module: UserModule): Builder
fun build(): AuthenticatedComponent
}
}
#Module
class AuthenticatedModule(private val userId: Long,
private val userRole: User.Role) {
// Some #Provides #AuthenticatedScope
}
And a typical use case would be:
#Singleton
class AppLevelService
#Inject constructor(...) { ... }
#AuthenticatedScope
class AuthenticatedLevelServices
#Inject constructor(...) { ... }
class ViewModel
#Inject constructor(private val appService: AppLevelService,
private val authService: AuthenticatedLevelServices) { ... }
class MyActivity : BaseActivity {
#Inject
lateinit var vmProvider: Provide<ViewModel>
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
}
}
How can I make the test setup for this "type" of dagger usage?
I find a lot of examples of testing with dagger2 but for #Provides annotations, and I'm thinking there should be a way to mock injectable classes with #Inject.
I've tried DaggerMock but I get:
You must define overridden objects using a #Provides annotated method instead of using #Inject annotation
Not that it should affect anything, but I'm also using a custom Runner with DexOpener.
Any ideas or good documentation / examples for testing this setup?
During testing, my view models are still using production services, and I want them to use test services. I'm using robolectric for testing, and still haven't found a solution to my problem.
What I thought would be most promising would be using a viewModelFactory generator in my test fragment, like ViewModelUtil.createFor(mockedViewModel) is used in the GitHubBrowserSample. But that's not working for me, my test services created in the mocked viewModel will then get replaced by production services when running startFragment as shown below.
My main application extends DaggerApplication() and my production component uses a #Component.Builder so I'm not able to specify a module in my building process.
Additionally, I've tried having TestAppComponent extend from my AppComponent but that doesn't seem to do the trick either.
I've also tried setting a builder as a static object in MyApplication.kt like so:
companion object {
var builder: AndroidInjector.Factory<MyApplication> =
DaggerAppComponent.builder()
}
...
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return builder.create(this)
}
And in my FragmentTest.kt init doing something like:
private fun init() {
bagFragment = BagFragment.newInstance()
MyApplication.builder = DaggerTestAppComponent.builder()
SupportFragmentTestUtil.startFragment(bagFragment, AppCompatActivity::class.java)
}
But that doesn't work because of a type mismatches. If there was some solution where extending from DaggerApplication() was still a possibility I would be very interested in that.
Here's my setup:
AppComponent.kt
#Singleton
#Component(modules = [
AndroidInjectionModule::class,
AndroidSupportInjectionModule::class,
AppModule::class,
ActivityModule::class,
FragmentModule::class,
ProductionModule::class])
interface AppComponent : AndroidInjector<MyApplication>{
#Component.Builder
abstract class Builder : AndroidInjector.Builder<MyApplication>()
}
ProductionModule.kt
#Module
open class ProductionModule {
#Provides
fun browseCategoriesViewModel(authenticationService: AuthenticationService): BrowseCategoriesFragment.BrowseCategoriesViewModel{
return BrowseCategoriesFragment.BrowseCategoriesViewModel(authenticationService)
}
// Provides multiple things
...
ViewModelModule.kt
#Module
abstract class ViewModelModule {
#Binds
abstract fun bindViewModelFactory(factor: ViewModelFactory): ViewModelProvider.Factory
/*** Fragments ***/
#Binds
#IntoMap
#ViewModelKey(BagFragment.BagViewModel::class)
abstract fun bindBagViewModel(viewModel: BagFragment.BagViewModel): ViewModel
MyApplication.kt
class MyApplication: DaggerApplication(), HasActivityInjector, HasSupportFragmentInjector {
#Inject
lateinit var sharedPreferencesService: SharedPreferencesService
#Inject
lateinit var activityInjector: DispatchingAndroidInjector<Activity>
#Inject
lateinit var supportFragmentInjector: DispatchingAndroidInjector<Fragment>
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
MultiDex.install(this)
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder().create(this)
}
override fun activityInjector() = activityInjector
override fun supportFragmentInjector() = supportFragmentInjector
}
The AppModule, FragmentModule, and ActivityModule are set up according to documentation, and haven't given me any issues.
Here is my test setup:
TestAppComponent.kt
#Singleton
#Component(modules =[
AndroidInjectionModule::class,
AndroidSupportInjectionModule::class,
TestModule::class])
interface TestAppComponent: AndroidInjector<MyApplication>{
#Component.Builder
abstract class Builder: AndroidInjector.Builder<MyApplication>()
}
TestModule.kt
#Module
internal class TestModule {
#Provides
fun bagViewModel(dataService: FakeDataService,
authenticationService: FakeAuthenticationService,
favoritesService: FakeFavoritesService,
cartService: FakeCartService): BagFragment.BagViewModel {
return BagFragment.BagViewModel(dataService, authenticationService, favoritesService, cartService)
}
#Provides
#Singleton
fun dataService(): FakeDataService = FakeDataService()
#Provides
#Singleton
fun authenticationService(): FakeAuthenticationService = FakeAuthenticationService()
// provides many other things
My current test fragment init method looks like this:
private fun init() {
bagFragment = BagFragment.newInstance()
SupportFragmentTestUtil.startFragment(bagFragment, AppCompatActivity::class.java)
}
Last but not least, an example production fragment:
BagFragment.kt
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
lateinit var viewModel: BagViewModel
override fun onCreate(savedInstanceState: Bundle?) {
AndroidSupportInjection.inject(this)
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this, viewModelFactory).get(BagViewModel::class.java)
}
When I call SupportFragmentTestUtil.startFragment() i'm assuming that AndroidInjection.inject(this) will replace my fake test module with the production module. I've also tried putting AndroidInjection.inject(this) in an init method as opposed to onCreate but that gives me problems else where and is contrary to documentation. Please help!
please help me! I have a trouble in use of dagger 2.
I want to bind some dependency in runtime not in compile time inside MainActivity by using #Subcomponent.Builder and #BindsInstance
I have an ApplicationComponent and it has a Builder and its #BindsInstance looks working fine. I can use like below
DaggerApplicationComponent
.builder()
.application(this)
.build()
.inject(this)
but some trouble came from MainActivity...
below are snippets of codes
[ApplicationComponent]
#Singleton
#Component(modules = [ApplicationModule::class])
internal interface ApplicationComponent : AndroidInjector<MyApplication> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): ApplicationComponent
}
}
[ApplicationModule]
#Module(
includes = [
AndroidInjectionModule::class,
AndroidSupportInjectionModule::class,
ActivityInjectionModule::class
],
subcomponents = [
MainComponent::class
]
)
internal abstract class ApplicationModule {
#PerActivity
#ContributesAndroidInjector(modules = [SplashModule::class])
abstract fun splashActivity(): SplashActivity
#Binds
#IntoMap
#ActivityKey(MainActivity::class)
abstract fun mainActivity(builder: MainComponent.Builder): AndroidInjector.Factory<out Activity>
}
[MainComponent]
#PerActivity
#Subcomponent(modules = [MainModule::class])
internal interface MainComponent : AndroidInjector<MainActivity> {
#Subcomponent.Builder
abstract class Builder : AndroidInjector.Builder<MainActivity>() {
#BindsInstance
abstract fun testClass(mainTestClass: MainTestClass): Builder
}
}
[MainActivity]
internal class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// This works find without runtime injection
// AndroidInjection.inject(this)
/**
*I want to bind some dependency(in this case, MainTestClass) in runtime like below.
* so that I can use MainTestClass inside MainModule to inject this to other classes.
* but, for some reason,
* DaggerMainComponent IS NOT GENERATED AUTOMATICALLY...
*/
DaggerMainComponent.builder()
.testClass(MainTestClass())
.build()
.inject(this);
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
startActivity(Intent(this, SplashActivity::class.java))
}
}
The problem is that I cannot access DaggerMainComponent because the Dagger doesn't generate it automatically.
I am searching for lots of websites to solve this but failed.
Is there any way to make it?
I have found a way to achieve what you want, I think. Apologies for not using your particular example, but it was easier to paste in the code I have and know that works from my own IDE. I've added comments on the critical lines. Here's the code:
Singleton component
#Singleton
#Component(modules = [
AndroidSupportInjectionModule::class,
RuntimeBindingModule::class // my addition!
])
interface MainApplicationComponent {
fun inject(app: MainApplication)
// my addition!
fun runtimeBuilder(): RuntimeBindingActivitySubcomponent.Builder
#Component.Builder
interface Builder {
fun build(): MainApplicationComponent
#BindsInstance fun app(app: Context): Builder
}
}
This binding code is essentially identical to yours.
#Subcomponent
interface RuntimeBindingSubcomponent : AndroidInjector<RuntimeBindingActivity> {
#Subcomponent.Builder
abstract class Builder : AndroidInjector.Builder<RuntimeBindingActivity>() {
#BindsInstance abstract fun bindInt(intVal: Int): Builder
}
}
#Module(subcomponents = [RuntimeBindingSubcomponent::class])
abstract class RuntimeBindingActivityModule {
#Binds #IntoMap #ActivityKey(RuntimeBindingActivity::class)
abstract fun bindInjectorFactory(
builder: RuntimeBindingActivitySubcomponent.Builder
): AndroidInjector.Factory<out Activity>
}
MainApplication
open class MainApplication : Application(), HasActivityInjector {
// This needs to be accessible to your Activities
lateinit var component: MainApplication.MainApplicationComponent
override fun onCreate() {
super.onCreate()
initDagger()
}
private fun initDagger() {
component = DaggerMainApplicationComponent.builder()
.app(this)
.build()
component.inject(this)
}
}
RuntimeBindingActivity
class RuntimeBindingActivity : AppCompatActivity() {
// I had to use #set:Inject because this is a primitive and we can't use lateinit
// on primitives. But for your case,
// `#Inject lateinit var mainTestClass: MainTestClass` would be fine
#set:Inject var intVal: Int = -1
override fun onCreate(savedInstanceState: Bundle?) {
// And this is how you can get runtime binding
val subComponent = (application as MainApplication).component.runtimeBuilder()
with(subComponent) {
seedInstance(this#RuntimeBindingActivity)
bindInt(10) // runtime binding
build()
}.inject(this)
Log.d("RuntimeBindingActivity", "intVal = $intVal")
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_runtime_binding)
}
}
It is critically important to note that the subcomponent which you generate in this way doesn't get magically stored somewhere by dagger. If you want your late-bound instance to be available for injecting into other classes controlled by your #PerActivity scope, you need to manually manage the lifecycle of this subcomponent. Store it somewhere (maybe in your custom Application class), and then you also must set its reference to null when your activity is destroyed, or you'll be leaking that activity.
Hi i have a ViewModel that has two var's that get injected using Dagger 2.11.
However it never gets injected in my viewModel whilst everywhere else in my app, these same dependecy Vars get initialised and injected perfectly
Below is my viewModel
class MyViewModel : AndroidViewModel, MyViewModelContract {
private lateinit var pointsBalance: MutableLiveData<PointsBalance>
#Inject
lateinit var accountDelegator: AccountDelegatorContract
#Inject
constructor(application: Application) : super(application)
init {
DaggerApplicationComponent.builder().application(getApplication() as MyApplication).build().inject(this)
}
override fun getPointsBalance(): LiveData<PointsBalance> {
if (!this::pointsBalance.isInitialized) {
//get balance from network api etc
}
return pointsBalance
}
accountDelegator complains that it is not initialised
Below is how i bind this viewModel inside MyModule.class
#Provides
#JvmStatic
#Singleton
fun providesViewModel(viewModel: MyViewModel): MyViewModelContract = viewModel
my custom application
class MyApplication : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
val applicationComponent = DaggerApplicationComponent.builder()
.application(this)
.build()
applicationComponent.inject(this)
return applicationComponent
}
}
my applicationComponent
#Singleton
#Component(modules = arrayOf(MyModule::class,
))
interface ApplicationComponent : AndroidInjector<DaggerApplication> {
fun inject(mApplication: MyApplication)
override fun inject(instance: DaggerApplication)
#Component.Builder
interface Builder {
#BindsInstance
fun application(applicaton: MyApplication): Builder
fun build(): ApplicationComponent
}
}
How i use this viewmodel in a activity/fragment
viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
I believe issue is that you're not calling inject. You'd need for a start to add following to ApplicationComponent
void inject(MyViewModel viewModel);
What I'm doing here then fwiw is inheriting from AndroidViewModel (which gives access to application instance) then calling following in ViewModel class (you'd need to substitute DaggerProvider.getComponent with however you're accessing component in your code)
init {
DaggerProvider.getComponent(application).inject(this)
}
Hi i am new to dagger 2 and trying to inject an instance of sharedPreference inside my MyActivity class below:
class MyApplication : Application() {
companion object {
#JvmStatic lateinit var applicationComponent : ApplicationComponent
}
override fun onCreate() {
super.onCreate()
applicationComponent = DaggerApplicationComponent.builder().androidModule(AndroidModule(this)).build()
}
}
Here is the component and modules
#Singleton
#Component(modules = arrayOf(AndroidModule::class))
interface ApplicationComponent {
fun inject(mainActivity: MainActivity)
}
#Module
class AndroidModule (private val application: Application){
#Provides
#Singleton
fun provideApplicationContext() : Context = application
#Provides
#Singleton
fun provideSharedPreference() : SharedPreferences = application.getSharedPreferences("shared pref", Context.MODE_PRIVATE)
}
class MainActivity: Activity{
#Inject
internal lateinit var sharedPreference: SharedPreferences
#Inject
internal lateinit var MainScreenPresenter: MainScreenContract.Presenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_Screen)
MyApplication.applicationComponent.inject(this)
sharedPreference.toString()
initiateViews()
}
}
I get the error below:
Error:(7, 1) error: android.content.SharedPreferences cannot be provided without an #Provides- or #Produces-annotated method.
You have done it a little bit incorrect. First of all now there is dagger-android that helps with the problem of principle that solves the problem that components (such as Activities) should not know about how the injection happens.
you can read it here: https://medium.com/#iammert/new-android-injector-with-dagger-2-part-1-8baa60152abe
Just to be sure that dagger dependencies are in the Android project:
android {
kapt {
generateStubs = true
}
}
compile "com.google.dagger:dagger:2.13"
compile "com.google.dagger:dagger-android:2.13"
compile "com.google.dagger:dagger-android-support:2.13"
kapt "com.google.dagger:dagger-compiler:2.13"
kapt "com.google.dagger:dagger-android-processor:2.13"
Your mistake is that you didn't tell to your graph that you want to make injections into the MainActivity. In the best way you should create Subcomponent for MainActivity, connect it with another Module for MainActivity that have injections that you want to inject into the MainActivity, set in your AppComponent the connection with Subcomponent and only than in MainAcitivy onCreate() method inject your dependency graph. But with dagger-android everything is easier.
Here is the code:
#Singleton
#Component(modules = [
AndroidSupportInjectionModule::class,
ActivityBindingModule::class,
AppModule::class
])
interface AppComponent : AndroidInjector<DaggerApplication> {
fun inject(application: MyApplication)
override fun inject(instance: DaggerApplication)
#Component.Builder
interface Builder {
#BindsInstance fun application(application: MyApplication): Builder
fun build(): AppComponent
}
}
AndroidSupportInjectionModule.class : This goes from the dagger.android.support library. And it provides Android components (Activities/Fragments/Services/BroadcastReceiver/ContentProvider) with our module.
#Component.Builder in dagger2.10 provides us better way to create a builder of DaggerAppComponent.
#BindsInstance in the Builder will automatically create an instance of MyApplication so in AppModule we don't need to instantiate with MyApplication. It is already a dependency in the graph.
ActivityBindingModule.clas is our. I will tell about it later.
You can read more about this part here: https://proandroiddev.com/dagger-2-component-builder-1f2b91237856
Next is AppModule.class :
#Module
abstract class AppModule{
#Binds
abstract fun provideContext(application: MyApplication) : Context
#Module
companion object {
#JvmStatic
#Provides
fun provideSharedPreferences(context: Context): SharedPreferences =
context.getSharedPreferences("SharedPreferences", Context.MODE_PRIVATE)
}
}
#Binds annotation replaces #Provides annotation and it just returns the value in the function parameter. As you see an instance of MyApplication is already in the graph and there is no need to inject MyApplication in the AppModule constructor.
NOTE: function with #Binds annotation should be abstract, and if there are function with #Provides annotation they should be static.
The solution in Kotlin for static funcitons you can find here: https://github.com/google/dagger/issues/900
ActivityBindingModule.class:
#Module
abstract class ActivityBindingModule {
#ContributesAndroidInjector(modules = [MainActivityModule::class])
internal abstract fun bindMainActivity(): MainActivity
}
With the ActivityBindingModule class we just create separate Module that will create Subcomponents for Android components for us.
More about ContributesAndroidInjector and Binds you can read here:
https://proandroiddev.com/dagger-2-annotations-binds-contributesandroidinjector-a09e6a57758f
MainActivityModule.class:
#Module
abstract class MainActivityModule {
#Binds
internal abstract fun provideMainActivity(activity: MainActivity): MainActivity
}
MyApplication.class:
class MyApplication: DaggerApplication(){
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
val appComponent = DaggerAppComponent.builder()
.application(this)
.build()
appComponent.inject(this)
return appComponent
}
}
Do not forget insert Application in the Mainfest file.
<application
android:name=".MyApplication"
...
>
...
</application>
For your Application class you need to implement DaggerApplication that implements HasActivityInjector/HasFragmentInjector/etc as well as call AndroidInjection.inject().
About this you can read more here : https://google.github.io/dagger/android.html
MainActivity.class:
class MainActivity : DaggerAppCompatActivity() {
#Inject
lateinit var sharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d("TAAAAG", sharedPreferences.toString())
}
}
As you can see MainActivity now does not know how SharedPreferences are injected. Actually there is AndroidInjection.inject(this); in the DaggerAppCompatActivity. If you don't extend you class from this, than you need to specify it in onCreate method by yourself, otherwise no injections will be done.
EDIT:
You can check the code from GitHub: https://github.com/Belka1000867/Dagger2Kotlin