Android: ¿Cómo hacer la inyección de componente de daga multimodulo?

Tengo androide gradle multi-module dagger aplicación.

Dentro de cada módulo Estoy haciendo inyección a través de su propio componente como este:

MainApp.appComponent.inject(this) para el módulo :app y BaseApp.appComponent.inject(this) para el módulo :app:base.

Ahora tratando de hacer SharedPreferences envoltura disponible dentro app:base módulo. Hasta este punto todo funcionó bien, pero después de esto me enfrenté a este error

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.alazar.tracker/com.alazar.tracker.MainActivity}: kotlin.UninitializedPropertyAccessException: lateinit property appComponent has not been initialized

Application.name propiedad especificada en Manifest.xml del :app:base módulo.

¿Qué estoy haciendo mal y cómo arreglar eso? Gracias por cualquier sugerencia.

MainActivity:

class MainActivity : BaseActivity(), View.OnClickListener {

    @Inject
    lateinit var userManager: UserManagerInterface

    @Inject
    lateinit var preferences: PreferenceProvider

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        MainApp.appComponent.inject(this)

        binding = ActivityMainBinding.inflate(layoutInflater)

        binding.btnSignOut.setOnClickListener(this)
        binding.btnStart.setOnClickListener(this)
        binding.btnStop.setOnClickListener(this)

        if (!userManager.isAuthenticated()) {
            openPostActivity.launch(Intent(this, AuthActivity::class.java))
        } else {
            setContentView(binding.root)
            changeStatus(preferences.getServiceStatus())
        }
    }

}

MainApp:

    @MainScope
@Component(
    dependencies = [
    ],
    modules = [
        MainModule::class,
    ]
)

interface MainAppComponent {
    fun inject(activity: MainActivity)
    fun inject(activity: MapActivity)
}

@Module(
    includes = [
        AuthUserModule::class,
        BaseModule::class,
    ]
)
class MainModule


class MainApp : Application() {

    override fun onCreate() {
        super.onCreate()

        appComponent = DaggerMainAppComponent
            .builder()
            .build()
    }

    companion object {
        lateinit var appComponent: MainAppComponent
    }
}

BaseApp:

    @Singleton
@Component(
    modules = [
        BaseModule::class,
    ]
)

interface BaseComponent {
    fun inject(baseApp: BaseApp)
    fun inject(wrapper: SharedPrefWrapper)
}

@Module
class BaseModule constructor(private val application: Application) {
    @Provides
    fun provideContext() : Context = application.applicationContext

    @Provides
    fun provideSharedPreferences() : PreferenceProvider = SharedPrefWrapper(application.applicationContext)
}

class BaseApp : Application() {

    override fun onCreate() {
        super.onCreate()

        appComponent = DaggerBaseComponent
            .builder()
            .baseModule(BaseModule(this))
            .build()
    }

    companion object {
        lateinit var appComponent: BaseComponent
    }
}

Envoltorio de preferencias compartido

    class SharedPrefWrapper @Inject constructor(private val context: Context) : PreferenceProvider {

    init {
        BaseApp.appComponent.inject(this)
        Log.d("************ CONTEXT", context.toString())
    }

    private var preferences: SharedPreferences = context.getSharedPreferences(
        context.getString(R.string.shared_preference_name),
        AppCompatActivity.MODE_PRIVATE
    )

    override fun saveServiceStatus(status: Boolean) {
        val editor = preferences.edit()
        editor.putBoolean(context.getString(R.string.preference_service_param), status)
        editor.apply()
    }

    override fun getServiceStatus(): Boolean {
        return preferences.getBoolean(context.getString(R.string.preference_service_param), false)
    }
}

Pregunta hecha hace 3 años, 5 meses, 0 días - Por codecrusaderx58e2


2 Respuestas:

  • El error que estás experimentando ("java.lang.RuntimeException: Unable to start activity ComponentInfo{com.alazar.tracker/com.alazar.tracker.MainActivity}: kotlin.UninitializedPropertyAccessException: lateinit property appComponent has not been initialized") se debe a que estás intentando acceder a la propiedad appComponent de BaseApp o MainApp antes de que haya sido inicializada.

    La solución a este problema es asegurarte de que las instancias de BaseAppComponent y MainAppComponent sean inicializadas previamente antes de hacer la inyección en las clases que las requieran.

    En el caso de SharedPrefWrapper, estás intentando hacer la inyección en el constructor a través de BaseApp.appComponent.inject(this). Para asegurarte de que BaseApp.appComponent esté inicializada previamente, puedes mover la inyección a un método separado y llamar a ese método cuando la inyección sea realmente necesaria.

    Por ejemplo, podrías modificar tu clase SharedPrefWrapper de la siguiente manera:

    kotlin
    class SharedPrefWrapper @Inject constructor(private val context: Context) : PreferenceProvider {
    
        private var preferences: SharedPreferences
    
        init {
            preferences = context.getSharedPreferences(
                context.getString(R.string.shared_preference_name),
                AppCompatActivity.MODE_PRIVATE
            )
        }
    
        fun inject() {
            BaseApp.appComponent.inject(this)
            Log.d("NoneNoneNone CONTEXT", context.toString())
        }
    
        override fun saveServiceStatus(status: Boolean) {
            val editor = preferences.edit()
            editor.putBoolean(context.getString(R.string.preference_service_param), status)
            editor.apply()
        }
    
        override fun getServiceStatus(): Boolean {
            return preferences.getBoolean(context.getString(R.string.preference_service_param), false)
        }
    }
    

    Y luego, en tu clase BaseApp, puedes llamar al método inject() después de que BaseAppComponent se haya construido:

    kotlin
    class BaseApp : Application() {
    
        override fun onCreate() {
            super.onCreate()
    
            appComponent = DaggerBaseComponent
                .builder()
                .baseModule(BaseModule(this))
                .build()
    
            // Llamar al método inject de SharedPrefWrapper
            SharedPrefWrapper(applicationContext).inject()
        }
    
        companion object {
            lateinit var appComponent: BaseComponent
        }
    }
    

    De esta manera, te aseguras de que BaseAppComponent esté inicializado antes de realizar la inyección en SharedPrefWrapper. Asegúrate de aplicar un enfoque similar en otros lugares donde se realicen inyecciones de componentes en tu código.

    Respondida el Dec 18, 2020 a las 19:03 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • Problema:

    El error que estás encontrando se debe a que la propiedad appComponent en MainActivity no se está inicializando correctamente. Esto se debe a que la llamada a MainApp.appComponent.inject(this) se está realizando antes de que MainApp se haya inicializado.

    Solución:

    Para solucionar este problema, puedes mover la llamada a MainApp.appComponent.inject(this) al método onAttach() de MainActivity, que se llama después de que se haya creado la instancia de la actividad.

    MainActivity modificada:

    kotlin
    class MainActivity : BaseActivity(), View.OnClickListener {
    
        @Inject
        lateinit var userManager: UserManagerInterface
    
        @Inject
        lateinit var preferences: PreferenceProvider
    
        private lateinit var binding: ActivityMainBinding
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(binding.root)
    
            changeStatus(preferences.getServiceStatus())
        }
    
        override fun onAttach() {
            super.onAttach()
            MainApp.appComponent.inject(this)
        }
    
        ...
    }
    

    Nota:

    También es importante asegurarse de que la aplicación se está inicializando correctamente en BaseApp. En particular, debes asegurarte de que BaseModule(this) se esté pasando al crear BaseComponent.

    BaseApp modificada:

    kotlin
    class BaseApp : Application() {
    
        override fun onCreate() {
            super.onCreate()
    
            appComponent = DaggerBaseComponent
                .builder()
                .baseModule(BaseModule(this))
                .build()
        }
    
        ...
    }
    

    Otras recomendaciones:

    Considera utilizar la anotación @AndroidEntryPoint en MainActivity para simplificar la inyección de componentes. Divide tu base de datos en módulos más pequeños para mejorar la modularidad y la mantenibilidad.

    • Utiliza herramientas como Hilt o Koin para simplificar aún más la inyección de dependencias.

    Espero que esto solucione tu problema. Avísame si tienes alguna otra pregunta.

    Respondida el Dec 18, 2020 a las 19:09 - por Gemini

    Votos positivos: 0 | Votos negativos: 0