I am trying to convert an Android app from Java to Kotlin. There are a few singletons in the app. I used a companion object for the singletons without constructor parameters
Here's a neat alternative from Google's architecture components sample code, which uses the also
function:
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()
}
}
Thread-Safe Solution
# Write Once; Use Many;
It's a good solution to create a class implementing the logic of singleton which also holds the singleton instance, like the following.
It instantiates the instance using Double-Check Locking in a synchronized block to eliminate possibility of race condition in multi-threaded environments.
SingletonHolder.kt
open class SingletonHolder<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!!
}
}
}
}
Usage
Now in each class that you want to be singleton, write a companion object
extending the above class. SingletonHolder
is a generic class that accepts type of target class and its requiring parameter as generic params. It also needs a reference to the constructor of target class which is used for instantiating an instance:
class MyManager private constructor(context: Context) {
fun doSomething() {
...
}
companion object : SingletonHolder<MyManager, Context>(::MyManager)
}
Finally:
MyManager.getInstance(context).doSomething()
If the only parameter you need is the application Context
, then you can initialize it to a top level val
, early in a ContentProvider
, like the Firebase SDK does.
Since declaring a ContentProvider
is a bit cumbersome, I made a library that provides a top level property named appCtx for all places where you don't need an Activity or other special lifecycle bound context.
The method synchronized()
is marked as deprecated in the common standard library so an alternative would be this:
class MySingleton private constructor(private val param: String) {
companion object {
@Volatile
private var INSTANCE: MySingleton? = null
@Synchronized
fun getInstance(param: String): MySingleton = INSTANCE ?: MySingleton(param).also { INSTANCE = it }
}
}
Singletons
Singletons are used often enough for a simpler way of creating them to exist. Instead of the usual static instance, getInstance() method and a private constructor, Kotlin uses the object notation. For consistency, object notation is also used to define static methods.
object CommonApiConfig {
private var commonApiConfig: CommonApiConfig? = null
fun getInstance(): CommonApiConfig {
if (null == commonApiConfig) {
commonApiConfig = CommonApiConfig
}
return CommonApiConfig.commonApiConfig!!
}
}