Main Activity
#AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
Hilt Module
#Module
#InstallIn(SingletonComponent::class)
class AppModule {
#Singleton
#Provides
fun provideActivity(): MainActivity = MainActivity()
#Singleton
#Provides
fun provideActivityUtils(#ActivityRetainedScoped context: MainActivity): ActivityUtils =
ActivityUtilsImpl(context)
}
And utils class with needed functions
class ActivityUtilsImpl #Inject constructor(#ActivityRetainedScoped private val activity:
MainActivity) : ActivityUtils {
override fun showKeyboard() {
val imm = activity.getSystemService(Context.INPUT_METHOD_SERVICE) as
InputMethodManager?
imm?.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0)
}
}
When I try to call something from the utils class using an activity instance, I get the error java.lang.IllegalStateException: System services not available to Activities before onCreate ()
Tried #ActivityContext the same thing, how can I make it call normally
#Singleton
#Provides
fun provideActivity(): MainActivity = MainActivity()
Never ever ever ever ever ever instantiate an Activity manually.
Should be
#InstallIn(ActivityComponent::class)
#Module
class ActivityModule {
#Provides #ActivityScope fun activityUtils(activity: Activity): ActivityUtils = ActivityUtilsImpl(activity)
I'm using dagger 2.25.2 and androidx.lifecycle for dependency injection in my android project. But I've got error ViewModelFactory has not been initialized
Here is my code's
class PropertyActivity : AppCompatActivity() {
#Inject
lateinit var propertyViewModelFactory: PropertyViewModelFactory
lateinit var propertyViewModel: PropertyViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.property_activity)
propertyViewModel = ViewModelProviders.of(this, propertyViewModelFactory).get(
PropertyViewModel::class.java)
propertyViewModel.loadProperties()
}
ViewModelFactory:
class PropertyViewModelFactory #Inject constructor(
private val propertyViewModel: PropertyViewModel) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(PropertyViewModel::class.java!!)) {
return propertyViewModel as T
}
throw IllegalArgumentException("Unknown class name")
}
}
AppModule:
#Module
class AppModule(val app: Application){
#Provides
#Singleton
fun provideApplication(): Application = app
#Provides
#Singleton
fun providePropertyViewModelFactory(factory: PropertyViewModelFactory): ViewModelProvider.Factory = factory
}
BuildersModule:
#Module
abstract class BuildersModule {
#ContributesAndroidInjector
abstract fun contributePropertyActivity(): PropertyActivity
}
AppComponent:
#Singleton
#Component(modules = arrayOf(AndroidInjectionModule::class,BuildersModule::class, AppModule::class))interface PlotComponent {
fun inject(app: Application)
}
Application Class :
class PlotApplication : Application(), HasAndroidInjector {
#Inject
lateinit var activityInjector: DispatchingAndroidInjector<Any>
override fun onCreate() {
super.onCreate()
DaggerPlotComponent.builder()
.appModule(AppModule(this))
.build().inject(this)
}
override fun androidInjector(): AndroidInjector<Any> = activityInjector
}
I doesn't see where you inject dependency in your activity. The problem may be in this.
Add
AndroidInjection.inject(this)
in your onCreate
I have problems when starting Dagger in Android project with Kotlin.
This estructure is the next one
Dagger is included in an Android module that is called by the client application
MagicBox.kt
interface MagicBox {
fun getDate(): Long?
}
MagicBoxImpl.kt
class MagicBoxImpl (): MagicBox{
var date: Long = Date().time
override fun getDate(): Long {
return date
}
}
MainModule.kt
#Module
class MainModule (private val app: Application) {
#Provides
#Singleton
fun provideMagicBox(): MagicBox {
return MagicBoxImpl()
}
}
MainComponent.kt
#Singleton
#Component(modules = [MainModule::class, PresenterModule::class])
interface MainComponent{
fun inject(target: Activity)
}
Application.kt
class Application: Application() {
lateinit var mainComponent: MainComponent
override fun onCreate() {
super.onCreate()
mainComponent = initDagger(this)
}
private fun initDagger(app: Application): MainComponent =
DaggerMainComponent.builder()
.mainModule(MainModule(app))
.build()
}
MainActivity.kt
#Inject
lateinit var magicBox: MagicBox
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_layout)
(application as ClientSdk).mainComponent.inject(this)
tvDaggerTest = findViewById(R.id.tvDaggerTest)
tvDaggerTest!!.text = magicBox.getDate().toString()
}
Get the following error
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property magicBox has not been initialized
fun inject(target: Activity) should be fun inject(target: MainActivity)
Also for better Dagger usage, the following should be:
#Module
abstract class MainModule {
#Binds
abstract fun magicBox(impl: MagicBoxImpl): MagicBox
}
and
#Singleton class MagicBoxImpl #Inject constructor(): MagicBox {
I am currently learning how to inject into Android apps with Dagger 2. I wrote a very basic code, but it refuses to work. My goals is it to inject the MainActicity as it should be. It builds
My code:
class MainActivity : AppCompatActivity() {
#Inject lateinit var info: Info
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
txt_view.text = info.textInformation
}
}
class Info {val textInformation = "You are able to read this" }
#Module
class InfoModule{
#Provides
fun info ():Info{
return Info()
}
}
class CustomApp : Application (),HasActivityInjector{
#Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
override fun activityInjector(): AndroidInjector<Activity> {
return dispatchingAndroidInjector
}
}
#Component(modules = arrayOf(AndroidInjectionModule::class,
ActivityModule::class))
interface ApplicationComponent{
fun inject(application: CustomApp)
}
#Module
abstract class ActivityModule{
#ContributesAndroidInjector(modules = arrayOf(InfoModule::class))
abstract fun contributeInfoActivityInjector():MainActivity
}
The answer is that I forgot to add this code to the CustomApp class, and .CustomApp to the manifest file
override fun onCreate() {
super.onCreate()
initDi()
}
private fun initDi() {
DaggerApplicationComponent.builder().build().inject(this)
}
I'm trying to inject the ViewModelFactory into my Activity, but it keeps throwing this same error: lateinit property viewModelFactory has not been initialized. I can't find what I may be doing wrong. See the code above from my classes
AppComponent.kt
#Component(modules = [(AppModule::class), (NetworkModule::class), (MainModule::class)])
interface AppComponent {
fun inject(application: TweetSentimentsApplication)
fun inject(mainActivity: MainActivity)
fun context(): Context
fun retrofit(): Retrofit
}
MainModule.kt
#Module
class MainModule {
#Provides
fun mainViewModelFactorty(repository: TweetRepository): MainViewModelFactory = MainViewModelFactory(repository)
#Provides
fun local(database: AppDatabase): TweetLocal = TweetLocal(database)
#Provides
fun remote(tweetService: TweetService): TweetRemote = TweetRemote(tweetService)
#Provides
fun tweetService(retrofit: Retrofit): TweetService = retrofit.create(TweetService::class.java)
#Provides
fun repository(local: TweetLocal, remote: TweetRemote): TweetRepository = TweetRepository(local, remote)
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
#Inject lateinit var viewModelFactory: MainViewModelFactory
private val viewModel: MainViewModel? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java)
viewModel?.init("guuilp")
viewModel?.getTweetList()?.observe(this, Observer {
Toast.makeText(this, it?.size.toString(), Toast.LENGTH_LONG).show()
})
}
}
TweetSentimentsApplication.kt
open class TweetSentimentsApplication: Application(){
companion object {
lateinit var appComponent: AppComponent
}
override fun onCreate() {
super.onCreate()
initDI()
}
private fun initDI() {
appComponent = DaggerAppComponent.builder()
.appModule(AppModule(this))
.build()
}
}
You have to call the inject(mainActivity: MainActivity) method you've defined in AppComponent when you're initializing your MainActivity, that's how Dagger actually injects the dependencies you need.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
// This is where the dependencies are injected
TweetSentimentsApplication.appComponent.inject(this)
ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java)
...
}
Also, make sure that your application name is added in the AndroidManifest.xml file.
<application
android:name=".YourAppName"
..../>
You can also do this:
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
val mainViewModel: MainViewModel by lazy {
ViewModelProviders.of(this, viewModelFactory)[MainViewModel::class.java]
}
and use abstract modules with #ContributesAndroidInjector for the activity, and
abstract module for view model. Using abstract is more efficient:
#Module
abstract class AndroidBindingModule {
#ContributesAndroidInjector
internal abstract fun contributesAnActivity(): AnActivity
}
#Module
abstract class ViewModelModule {
//the default factory only works with default constructor
#Binds
#IntoMap
#ViewModelKey(AViewModel::class)
abstract fun bindArtViewModel(aViewModel: AViewModel): ViewModel
#Binds
abstract fun bindViewModelFactory(factory: AViewModelFactory): ViewModelProvider.Factory
}
#Documented
#Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
#Retention(RetentionPolicy.RUNTIME)
#MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
You could also extend DaggerAppCompatActivity in place of AppCompatActivity. E.g.
class MainActivity : DaggerAppCompatActivity() {
#Inject lateinit var viewModelFactory: MainViewModelFactory
private val viewModel: MainViewModel? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java)
viewModel?.init("guuilp")
viewModel?.getTweetList()?.observe(this, Observer {
Toast.makeText(this, it?.size.toString(), Toast.LENGTH_LONG).show()
})
}
}
My mistake was creating a new object and taking a component from it such as App().component.
So in this situation you need to put component field in companion object and replace code with App.component
Maybe you missed implementing "Injectable" Interface in fragment/activity. Which marks fragment/activity as injectable.