Some users have crashes, at what point they occur and for what reason it is not yet clear.
Logs (Firebase Crashlytics)
Fatal Exception: fu.p
lateinit property factory has not been initialized
com.its.yarus.base.BaseFragment.getFactory (BaseFragment.java:7)
com.its.yarus.ui.search.SearchTypeFragment$vm$3.invoke (SearchTypeFragment.java:1)
androidx.lifecycle.ViewModelLazy.getValue (ViewModelLazy.java:1)
com.its.yarus.ui.search.SearchTypeFragment.getVm (SearchTypeFragment.java:2)
com.its.yarus.ui.search.SearchTypeFragment.update$lambda-2 (SearchTypeFragment.java:5)
androidx.camera.camera2.internal.Camera2CameraControlImpl$$InternalSyntheticLambda$7$e9d410b43813df60e795612fb2f87f0233ac603a6d2ad492f8d3b620f6813f78$0.run$bridge (Camera2CameraControlImpl.java:5)
BaseFragment
abstract class BaseFragment: Fragment() {
#Inject
lateinit var uploadManager: UploadManager
#Inject
lateinit var factory: ViewModelFactory
#Inject
lateinit var ciceroneHolder: LocalCiceroneHolder
abstract var fragmentName: String
}
BaseMainFragment
....
override fun onCreate(savedInstanceState: Bundle?) {
YarusApp.component?.inject(this)
super.onCreate(savedInstanceState)
}
....
SearchTypeFragment
class SearchTypeFragment : BaseRecyclerFragment(), NotTopInset {
override val vm by viewModels<SearchViewModel>(
ownerProducer = { this },
factoryProducer = { factory }
)
private val vmSearch by viewModels<SearchMainViewModel>(
ownerProducer = { requireParentFragment() },
factoryProducer = { factory }
)
}
Inheritance chain:
SearchTypeFragment (located in the ViewPager) у SearchFragment ->
BaseRecyclerFragment ->
BaseMainFragment ->
BaseFragment
when passing the factory variable, the crash occurs
ViewModelFactory
#Singleton
class ViewModelFactory #Inject constructor(private val viewModels: MutableMap<Class<out ViewModel>, Provider<ViewModel>>) :
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val viewModelProvider = viewModels[modelClass]
?: throw IllegalArgumentException("model class $modelClass not found")
return viewModelProvider.get() as T
}
}
ViewModelModule
#Module
abstract class ViewModelModule {
#Binds
#Singleton
internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
}
MainComponent
#Singleton
#Component(
modules = [
AndroidSupportInjectionModule::class,
NetworkModule::class,
RepositoryModule::class,
ApplicationModule::class,
ViewModelModule::class,
RoomModule::class,
NavigationModule::class,
SocketModule::class,
ServiceModule::class,
ExoPlayerModule::class
]
)
interface MainComponent {
fun inject(application: YarusApp)
fun inject(activity: BaseActivity)
fun inject(activity: OnBoardingActivity)
fun inject(activity: SignInActivity)
fun inject(fragment: BaseFragment)
fun inject(fragment: BaseSignInFragment)
fun inject(fragment: BaseOnboardingFragment)
fun inject(fragment: BaseDialogFragment)
fun inject(fragment: BaseMusicFragment)
fun inject(service: CloudMessagingService)
fun inject(service: PlayerService)
fun inject(seance: SeanceBottomSheet)
fun inject(seance: PlaceBottomSheet)
fun inject(promo: PromoBottomSheet)
fun inject(bottomSheet: BaseBottomSheetDialogFragment)
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): MainComponent
}
}
Application
class YarusApp : Application() {
override fun onCreate() {
super.onCreate()
component = DaggerMainComponent.builder().application(this).build()
component?.inject(this)
}
companion object {
var component: MainComponent? = null
}
}
Do you have any ideas or suggestions on how to fix this problem?
Related
I am trying to implement the latest version of Dagger2 in a Single-Activity app, but it is not known why, when initializing my starting activity, Dagger2 does not inject dependencies, I has a
fatal error in my base activity : Unable to resume activity kotlin.UninitializedPropertyAccessException: lateinit property navigatorHolder has not been initialized
here is my code
AppComponent:
#Singleton
#Component(modules = [
AndroidSupportInjectionModule::class,
ActivityInjectionModule::class,
ActivityProviderModule::class,
AndroidInjectionModule::class,
NetworkModule::class,
RemoteModule::class,
NavigationModule::class,
ParserModule::class,
CacheModule::class])
interface AppComponent : AndroidInjector<App> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
#BindsInstance
fun context(context: Context) : Builder
fun build(): AppComponent
}
override fun inject(app: App)
}
App:
class App : DaggerApplication(){
private val applicationInjector =
DaggerAppComponent.builder().application(this).context(this).build()
override fun applicationInjector(): AndroidInjector<out DaggerApplication> =
applicationInjector
companion object {
lateinit var cicerone: Cicerone<Router>
private set
}
override fun onCreate() {
super.onCreate()
cicerone = Cicerone.create()
initAppComponent()
initStetho()
Timber.plant(Timber.DebugTree())
}
AppActivity:
class AppActivity : MvpAppCompatActivity() , HasAndroidInjector, RouterProvider {
#Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
#Inject
lateinit var mainActivityProvider: ActivityProvider
#Inject
lateinit var navigatorHolder: Lazy<NavigatorHolder>
#Inject
override lateinit var ciceroneRouter: Router
override fun androidInjector(): AndroidInjector<Any> = dispatchingAndroidInjector
private val navigator : Navigator by lazy {
CustomSupportAppNavigator(this, supportFragmentManager, R.layout.activity_main)
}
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
AndroidInjection.inject(this)
mainActivityProvider.acitvity = this
Timber.e("onCreate AppActivity")
super.onCreate(savedInstanceState, persistentState)
setContentView(R.layout.activity_main)
initBottomBar()
}
override fun onResumeFragments() {
super.onResumeFragments()
navigatorHolder.get().setNavigator(navigator)
}
ActivityInjectionModule
#Module(includes = [AndroidInjectionModule::class])
interface ActivityInjectionModule {
#ContributesAndroidInjector(
modules = [FragmentInjectionModule::class]
)
fun activityInjector() : AppActivity
}
NavigationModule
#Module
class NavigationModule {
#Provides
#Singleton
fun provideRouter() = App.cicerone.router
#Provides
#Singleton
fun provideNavigatorHolder() : NavigatorHolder {
return App.cicerone.navigatorHolder
}
#Provides
#Singleton
fun provideLocalNavigationHolder(): LocalCiceroneHolder {
return LocalCiceroneHolder()
}
}
Also i post android:name=".App" inandroid manifest.
I have tried many different options already, but I still cannot find the reason
Can you change your App to like this and try again:
class App : DaggerApplication(){
private val appComponent =
DaggerAppComponent.builder().application(this).context(this).build().inject(this)
companion object {
lateinit var cicerone: Cicerone<Router>
private set
}
override fun onCreate() {
super.onCreate()
cicerone = Cicerone.create()
initAppComponent()
initStetho()
Timber.plant(Timber.DebugTree())
}
Also remove AndroidInjector<App> from AppComponent
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'm integrating dagger with the viewmodel. when I include the viewmodelprovider factory in AppComponent, app is not getting build and its shows an error like
"error: [Dagger/MissingBinding] java.util.Map,javax.inject.Provider> cannot be provided without an #Provides-annotated method". I have added my code below.
ViewModelFactory
class DaggerViewModelFactory
#Inject
constructor(private val creators: Map<Class<out ViewModel>, #JvmSuppressWildcards Provider<ViewModel>>) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
var creator: Provider<out ViewModel>? = creators[modelClass]
if (creator == null) {
for ((key, value) in creators) {
if (modelClass.isAssignableFrom(key)) {
creator = value
break
}
}
}
if (creator == null) {
throw IllegalArgumentException("unknown model class " + modelClass)
}
try {
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
then i have my AppComponent class
#Singleton
#Component(modules = [AndroidInjectionModule::class, ViewModelFactoryModule::class, ActivityBuilder::class])
interface AppComponent : AndroidInjector<FreedomApplication> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: DaggerApplication): Builder
fun build(): AppComponent
}
}
my viewmodel module bind class
#Module
abstract class WelcomeModule {
#Binds
#IntoMap
#ViewModelKey(WelcomeViewModel::class)
abstract fun bindWelcomeViewModel(welcomeViewModel: WelcomeViewModel): ViewModel
}
my fragment builder class
#Module
abstract class FragmentBuilder {
#ContributesAndroidInjector
abstract fun injectWelcomeFragment() : WelcomeFragment
}
then my fragment class to include viewmodelprovider factory
class WelcomeFragment #Inject constructor(): DaggerFragment() {
#Inject
lateinit var providerFactory: ViewModelProvider.Factory
companion object {
fun newInstance() = WelcomeFragment()
}
private lateinit var viewModel: WelcomeViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.welcome_fragment, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this, providerFactory).get(WelcomeViewModel::class.java)
// TODO: Use the ViewModel
}
}
and view model to integrate for the fragment
class WelcomeViewModel #Inject constructor() : ViewModel() {
// TODO: Implement the ViewModel
}
Fixed the above issue, Kindly refer the below code.
#Singleton
#Component(modules = [ActivityBuilder::class, NetworkModule::class, ViewBindModule::class, AndroidSupportInjectionModule::class])
interface AppComponent : AndroidInjector<FreedomApplication> {
override fun inject(application: FreedomApplication)
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: FreedomApplication): Builder
fun build(): AppComponent
}
}
#Module
abstract class ActivityBuilder {
#ContributesAndroidInjector(modules = [FragmentBuilder::class])
abstract fun bindWelcomeActivity (): WelcomeActivity
#ContributesAndroidInjector(modules = [FragmentBuilder::class])
abstract fun bindDashBoardActivity (): DashBoardActivity
}
#Module
abstract class ViewBindModule {
#Binds
abstract fun bindViewModelFactory(factory: DaggerViewModelFactory): ViewModelProvider.Factory
#Binds
#IntoMap
#ViewModelKey(WelcomeViewModel::class)
abstract fun bindWelcomeViewModel(viewModel: WelcomeViewModel): ViewModel
#Binds
#IntoMap
#ViewModelKey(LoginViewModel::class)
abstract fun bindLoginViewModel(viewModel: LoginViewModel): ViewModel
#Binds
#IntoMap
#ViewModelKey(DashBoardViewModel::class)
abstract fun bindDashboardViewModel(viewModel: DashBoardViewModel): ViewModel
}
class DaggerViewModelFactory #Inject constructor(private val creators: Map<Class <out ViewModel>, #JvmSuppressWildcards Provider<ViewModel>>):ViewModelProvider.Factory {
#Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
var creator :Provider<ViewModel>? = creators[modelClass]
if (creator == null)
for ((key, value) in creators){
if (modelClass.isAssignableFrom(key)){
creator = value
break
}
}
if (creator == null) throw IllegalArgumentException("Unknown model class")
try{
return creator.get() as T
}catch (e:Exception){
throw RuntimeException(e)
}
}
}
abstract class FragmentBuilder {
#ContributesAndroidInjector
abstract fun bindWelcomeFragment(): WelcomeFragment
#ContributesAndroidInjector
abstract fun bindLoginFragment(): LoginFragment
#ContributesAndroidInjector
abstract fun bindDashBoardFragment(): DashBoardFragment
}
I'm using dagger 2 on my android project. At first I'm use only one component in the name of AppComponent and my project works fine. Then I split AppComponent and create these component: ActivityComponent, ContentComponent for different scope. When I was build my project I getting an error:
Caused by: java.lang.IllegalArgumentException: No injector factory bound for Class<project.presenter.activity.MainActivity>
at dagger.android.DispatchingAndroidInjector.inject(DispatchingAndroidInjector.java:106)
at dagger.android.AndroidInjection.inject(AndroidInjection.java:61)
at project.AppInjector$registerCallBack$1$handleActivity$2.invoke(AppInjector.kt:77)
at project.AppInjector$registerCallBack$1$handleActivity$2.invoke(AppInjector.kt:53)
at project.AppInjector$registerCallBack$1.injectNow(AppInjector.kt:120)
at project.AppInjector$registerCallBack$1.handleActivity(AppInjector.kt:77)
at project.AppInjector$registerCallBack$1.onActivityCreated(AppInjector.kt:81)
at android.app.Application.dispatchActivityCreated(Application.java:197)
at android.app.Activity.onCreate(Activity.java:1016)
at android.support.v4.app.SupportActivity.onCreate(SupportActivity.java:66)
at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:297)
Another problem is If I add ActivityScope to ActivityComponent I'm getting this error:
Error:(4, 1) error: #project.di.ContentScope project.di.ContentComponent depends on more than one scoped component:
Injectable:
interface Injectable
Components:
#AppScope
#Component(modules = [AndroidInjectionModule::class, AppModule::class, DatabaseModule::class])
interface AppComponent {
interface Builder {
#BindsInstance
fun application(app: App): Builder
fun build(): AppComponent
}
fun inject(app: App)
fun getDatabase(): RoomDatabase
}
#ContentScope
#Component(
dependencies = [AppComponent::class, ActivityComponent::class],
modules = [AndroidInjectionModule::class, ContentModules::class])
interface ContentComponent {
fun inject(favorite: Favorite)
fun inject(contentManager: ContentManager)
fun getObservableManager(): ModuleObservableManager
fun getFavorite(): Favorite
}
#Subcomponent(modules = [ActivityModules::class])
interface ActivityComponent
Modules:
#ActivityScope
#Module(includes = [MainActivityModule::class])
class ActivityModules
#ActivityScope
#Module(includes = [FragmentViewModelModule::class])
abstract class MainActivityModule {
#ActivityScope
#ContributesAndroidInjector(modules = [MainFragmentModule::class])
abstract fun bind(): MainActivity
}
#AppScope
#Module
class AppModule(private val application: Application) {
#AppScope
#Provides
fun provideApplication(): Application = application
#AppScope
#Provides
#ApplicationContext
fun provideContext(): Context = application.applicationContext
}
#ContentScope
#Module
class ContentModules {
val database: RoomDatabase
#Inject
constructor(database: RoomDatabase) {
this.database = database
}
#ContentScope
#Provides
fun provideModuleObservable()
= ModuleObservableManager()
#ContentScope
#Provides
fun provideFavoriteDao()
= database.favoriteDao()
#Provides
fun provideFavorite(dao: FavoriteDao)
= Favorite(dao)
}
#AppScope
#Module
class DatabaseModule {
#AppScope
#Provides
fun provideDatabase(#ApplicationContext context: Context): RoomDatabase =
Room.databaseBuilder(context, RoomDatabase::class.java, RoomDatabase.CONS.NAME)
.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.build()
}
#FragmentScope
#Module
abstract class FragmentViewModelModule {
#Binds
#IntoMap
#ViewModelKey(MainFragmentViewModel::class)
abstract fun bindHomeViewModel(model: MainFragmentViewModel): ViewModel
#Binds
abstract fun bindAppViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
}
#AppScope
class ViewModelFactory #Inject
constructor(private val creators: Map<Class<out ViewModel>, #JvmSuppressWildcards Provider<ViewModel>>) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
var creator: Provider<out ViewModel>? = creators[modelClass]
if (creator == null) {
for ((key, value) in creators) {
if (modelClass.isAssignableFrom(key)) {
creator = value
break
}
}
}
if (creator == null) throw IllegalArgumentException("unknown model class " + modelClass)
try {
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
#MustBeDocumented
#Target(AnnotationTarget.FUNCTION)
#Retention(AnnotationRetention.RUNTIME)
#MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
Scopes:
#Scope
#Retention(AnnotationRetention.RUNTIME)
annotation class AppScope
#Scope
#Retention(AnnotationRetention.RUNTIME)
annotation class ActivityScope
#Scope
#Retention(AnnotationRetention.RUNTIME)
annotation class FragmentScope
#Scope
#Retention(AnnotationRetention.RUNTIME)
annotation class ContentScope
Qualifiers:
#Qualifier
#Retention(AnnotationRetention.RUNTIME)
annotation class ActivityContext
#Qualifier
#Retention(AnnotationRetention.RUNTIME)
annotation class ApplicationContext
App:
class App : Application(), HasActivityInjector {
companion object {
private var instance: App? = null
fun getAppComponent(): AppComponent?
= instance?.appComponent
fun getContentComponent(): ContentComponent?
= instance?.contentComponent
}
#Inject
lateinit var injector: DispatchingAndroidInjector<Activity>
private var appComponent: AppComponent? = null
get() {
if (field == null) field = createAppComponent()
return field
}
private fun createAppComponent(): AppComponent? =
DaggerAppComponent.builder()
.appModule(AppModule(this))
.build()
.also { it.inject(this#App); appComponent = it }
private var contentComponent: ContentComponent? = null
get() {
if (field == null) field = createContentComponent()
return field
}
private fun createContentComponent(): ContentComponent? =
DaggerContentComponent.builder()
.appComponent(appComponent)
.build()
.also { contentComponent = it }
override fun onCreate() {
super.onCreate()
createAppComponent()
AppInjector.init(let { instance = this; this })
}
}
object AppInjector {
fun init(app: App) =
app.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
fun handleActivity(activity: Activity) {
if (activity is FragmentActivity) {
activity.supportFragmentManager.registerFragmentLifecycleCallbacks(object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentAttached(fm: FragmentManager?, fragment: Fragment?, context: Context?) {
if (fragment is Injectable) {
AndroidSupportInjection.inject(fragment)
}
}
}, true)
}
if (activity is Injectable) AndroidInjection.inject(activity)
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) = handleActivity(activity)
override fun onActivityStarted(activity: Activity) {}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityStopped(activity: Activity) {}
override fun onActivityResumed(activity: Activity) {}
override fun onActivityPaused(activity: Activity) {}
override fun onActivityDestroyed(activity: Activity) {}
)
}
Manifest:
<application
android:name="project.App"
..
I added the similar project on the github: Project Link