Actualmente estoy desarrollando un módulo KMM simple, que necesita Context para realizar algunas operaciones. Soy consciente de las formas de lograrlo extendiendo la clase Application y haciendo una inyección de dependencia. ¿Qué estoy tratando de hacer ahora? Para que este módulo esté disponible sin necesidad de modificar manifest o hacer una inyección manual al inicio. Me pregunto si es una mala práctica hacer algo así:

@SuppressLint("StaticFieldLeak")
object SomeUtil {

    private val context = Activity().applicationContext

}

Dado que applicationContext devuelve el Context para toda la aplicación y la estamos inicializando una vez, ¿habrá una fuga? ¿O hay otros puntos para no hacerlo?

¿Quizás existen otras posibilidades para obtener el contexto de la aplicación desde el módulo? He visto algunos ejemplos de cómo recuperarlo de subprocesos, pero según tengo entendido, esto estará (o ya está) en desuso.

UPD: esto provoca un error. Activity() parece ser null. Entonces, ¿alguna idea de cómo lograr eso sin DI y "MyApplication"?

0
Petr Grigorev 19 feb. 2021 a las 03:02

3 respuestas

La mejor respuesta

Este es un problema común en las bibliotecas de Android: ¿cómo obtener el contexto de la aplicación sin tener acceso al código base de la aplicación? Es por eso que a menudo inicias bibliotecas con algo como SharedPrefHelper.init(applicationContext) en tu Application.onCreate()

Como el código compartido de KMM es una biblioteca, surge un problema similar. inicio de la aplicación de Android es una biblioteca de androidx creada para resolver esto (además de mejorar rendimiento de inicio).

Muestra aproximada (todo en código compartido):

// In androidMain
class MySqlDelightInitialiser : Initializer<SqlDriver> {
    override fun create(context: Context): SqlDriver {
        val driver = createDriver(context)
        MyLibraryObject.init(context, driver)
        return driver
    }

    override fun dependencies(): List<Class<out Initializer<*>>> {
        return emptyList()
    }
}

// In androidMain/AndroidManifest
<application>
    <provider
        android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.sql-delight-initialiser"
        android:exported="false"
        tools:node="merge"
        tools:replace="android:authorities"
        >
        <meta-data
            android:name="my.package.SqlDelightInitialiser"
            android:value="androidx.startup"
            />
    </provider>
</application>

1
enyciaa 2 mar. 2021 a las 23:35

Bueno, comenzaría diciendo que esto no es realmente una pregunta de KMM. Esto solo se aplica al código de Android.

Hasta donde yo sé, no, no hay forma de obtener acceso estático y globalmente al contexto de la aplicación sin algunas soluciones semi-pirateadas. Este es un problema de larga data que realmente no tiene una buena solución.

Crashlytics hace (¿hizo?) Algo extraño al registrar un ContentProvider cuyo único propósito es obtener la aplicación y ponerla a disposición. Suponiendo que está publicando como aar, registraría ContentProvider por usted.

https://firebase.googleblog.com/2016/12/how-does-firebase-initialize-on-android.html

No lo recomendaría. Prefiero en gran medida configurar el inicio del contexto de la biblioteca yo mismo, pero puede probar la ruta ContentProvider.

2
Kevin Galligan 19 feb. 2021 a las 16:38

Respuesta corta: Inyéctelo en el constructor o como parámetro de método:

class SomeUtil(private val context: Context) {
   ....
}

object SomeUtil {
   fun someMethod(context: Context) { .... }
}

Las instancias de contexto (pero también de Actividad, Aplicación, Servicio) son creadas y destruidas por el marco de Android y la creación de instancias manualmente (o simulando) probablemente funcionará en tiempo de compilación, pero causarán excepciones en tiempo de ejecución.

1
Giuseppe Giacoppo 19 feb. 2021 a las 16:57