with sharedinstance parameter context companion android kotlin

android - sharedinstance - singleton kotlin context



Singleton con parametro en Kotlin (7)

• Solución segura para subprocesos:

Puede crear una clase que implemente la lógica de singleton y mantenga la instancia de singleton. Crea una instancia de la instancia mediante una doble verificación en un bloque sincronizado para eliminar la posibilidad de una condición de carrera en un entorno de múltiples hilos.

Singleton.kt

open class Singleton<out T, in A>(private val constructor: (A) -> T) { @Volatile private var instance: T? = null fun getInstance(arg: A): T { return when { instance != null -> instance!! else -> synchronized(this) { if (instance == null) instance = constructor(arg) instance!! } } } }

Luego, en la clase objetivo que debería ser singleton, escriba un companion object que se extienda por encima de la clase. Singleton clase Singleton es genérica y acepta los tipos de clase objetivo y su parámetro necesario como parámetros genéricos. También necesita una referencia al constructor de la clase de destino que se utiliza para crear instancias:

class TasksLocalDataSource private constructor(context: Context) : TasksDataSource { ... companion object : Singleton<TasksLocalDataSource, Context>(::TasksLocalDataSource) }

Estoy tratando de convertir una aplicación de Android de Java a Kotlin. Hay algunos singletons en la aplicación. Utilicé un objeto compañero para los singletons sin parámetros de constructor. Hay otro singleton que toma un parámetro constructor.

Código de Java:

public class TasksLocalDataSource implements TasksDataSource { private static TasksLocalDataSource INSTANCE; private TasksDbHelper mDbHelper; // Prevent direct instantiation. private TasksLocalDataSource(@NonNull Context context) { checkNotNull(context); mDbHelper = new TasksDbHelper(context); } public static TasksLocalDataSource getInstance(@NonNull Context context) { if (INSTANCE == null) { INSTANCE = new TasksLocalDataSource(context); } return INSTANCE; } }

Mi solución en kotlin:

class TasksLocalDataSource private constructor(context: Context) : TasksDataSource { private val mDbHelper: TasksDbHelper init { checkNotNull(context) mDbHelper = TasksDbHelper(context) } companion object { lateinit var INSTANCE: TasksLocalDataSource private val initialized = AtomicBoolean() fun getInstance(context: Context) : TasksLocalDataSource { if(initialized.getAndSet(true)) { INSTANCE = TasksLocalDataSource(context) } return INSTANCE } } }

¿Me estoy perdiendo algo? ¿Hilo seguro? Pereza ?

Hubo algunas preguntas similares, pero no me gustan las respuestas :)


Aquí hay una buena alternativa del código de ejemplo de componentes de arquitectura de Google, que utiliza la función also :

class UsersDatabase : RoomDatabase() { companion object { @Volatile private var INSTANCE: UsersDatabase? = null fun getInstance(context: Context): UsersDatabase = INSTANCE ?: synchronized(this) { INSTANCE ?: buildDatabase(context).also { INSTANCE = it } } private fun buildDatabase(context: Context) = Room.databaseBuilder(context.applicationContext, UsersDatabase::class.java, "Sample.db") .build() } }


No estoy completamente seguro de por qué necesitaría ese código, pero aquí está mi mejor oportunidad:

class TasksLocalDataSource private constructor(context: Context) : TasksDataSource { private val mDbHelper = TasksDbHelper(context) companion object { private var instance : TasksLocalDataSource? = null fun getInstance(context: Context): TasksLocalDataSource { if (instance == null) // NOT thread safe! instance = TasksLocalDataSource(context) return instance!! } } }

Esto es similar a lo que escribiste, y tiene la misma API.

Algunas notas:

  • No use lateinit aquí. Tiene un propósito diferente, y una variable anulable es ideal aquí.

  • ¿Qué hace checkNotNull(context) ? context nunca es nulo aquí, esto está garantizado por Kotlin. Todas las comprobaciones y afirmaciones ya están implementadas por el compilador.

ACTUALIZAR:

Si todo lo que necesita es una instancia de la clase TasksLocalDataSource inicializada de manera TasksLocalDataSource , solo use un grupo de propiedades perezosas (dentro de un objeto o en el nivel del paquete):

val context = .... val dataSource by lazy { TasksLocalDataSource(context) }


Puede declarar un objeto Kotlin, sobrecargando el operador "invocar" .

object TasksLocalDataSource: TasksDataSource { private lateinit var mDbHelper: TasksDbHelper operator fun invoke(context: Context): TasksLocalDataSource { this.mDbHelper = TasksDbHelper(context) return this } }

De todos modos, creo que deberías inyectar TasksDbHelper a TasksLocalDataSource en lugar de inyectar Contexto


Si el único parámetro que necesita es el Context la aplicación, entonces puede inicializarlo a un nivel superior val , al principio en un ContentProvider , como lo hace Firebase SDK.

Dado que declarar un ContentProvider es un poco engorroso, hice una biblioteca que proporciona una propiedad de nivel superior llamada appCtx para todos los lugares donde no necesita una Actividad u otro contexto especial vinculado al ciclo de vida.


Si quiere pasar un parámetro al singleton de una manera más fácil, creo que esto es mejor y más corto.

object SingletonConfig { private var retrofit: Retrofit? = null private const val URL_BASE = "https://jsonplaceholder.typicode.com/" fun Service(context: Context): Retrofit? { if (retrofit == null) { retrofit = Retrofit.Builder().baseUrl(URL_BASE) .addConverterFactory(GsonConverterFactory.create()) .build() } return retrofit }

}

y lo llamas de esta manera fácil

val api = SingletonConfig.Service(this)?.create(Api::class.java)


Singletons

Los Singletons se usan con la frecuencia suficiente para que exista una forma más sencilla de crearlos. En lugar de la instancia estática habitual, el método getInstance () y un constructor privado, Kotlin usa la notación de objetos. Por coherencia, la notación de objetos también se utiliza para definir métodos estáticos.

object CommonApiConfig { private var commonApiConfig: CommonApiConfig? = null fun getInstance(): CommonApiConfig { if (null == commonApiConfig) { commonApiConfig = CommonApiConfig } return CommonApiConfig.commonApiConfig!! } }