Kotlin singletons with argument

object SomeSingleton
object SomeSingleton {
init {
println("init complete")
}
}
public final class SomeSingleton {
public static final SomeSingleton INSTANCE;

private SomeSingleton() {
INSTANCE = (SomeSingleton)this;
System.out.println("init complete");
}

static {
new SomeSingleton();
}
}
Singletons: forever alone

Passing an argument

An Android use case

  • Early initialization: All components are initialized by calling their public init functions at startup time in Application.onCreate() where the application Context is available, before (almost) any other code runs. This simple solution has the main downside of slowing down the application startup by blocking the main thread, initializing all components including those that are not going to be used immediately. Another less known issue is that content providers may be created before this method is called (as stated in the documentation), so if a content provider makes use of a global application component, that component must be able to be initialized before Application.onCreate() or your application may crash.
  • Lazy initialization: This is the recommended way. The component is a singleton and a function returning its instance takes a Context argument. The singleton instance will be created and initialized using this argument the first time it’s invoked. This requires some synchronization mechanism in order to be thread-safe.
    An example of standard Android component using this pattern is LocalBroadcastManager:
LocalBroadcastManager.getInstance(context).sendBroadcast(intent)

A reusable Kotlin implementation

open class SingletonHolder<out T: Any, in A>(creator: (A) -> T) {
private var creator: ((A) -> T)? = creator
@Volatile private var instance: T? = null

fun
getInstance(arg: A): T {
val i = instance
if
(i != null) {
return i
}

return synchronized(this) {
val
i2 = instance
if
(i2 != null) {
i2
} else {
val created = creator!!(arg)
instance = created
creator = null
created
}
}
}
}
class Manager private constructor(context: Context) {
init {
// Init using context argument
}

companion object : SingletonHolder<Manager, Context>(::Manager)
}
Manager.getInstance(context).doStuff()
@Database(entities = arrayOf(User::class), version = 1)
abstract class UsersDatabase : RoomDatabase() {

abstract fun userDao(): UserDao

companion object : SingletonHolder<UsersDatabase, Context>({
Room.databaseBuilder(it.applicationContext,
UsersDatabase::class.java, "Sample.db")
.build()
})
}
interface GitHubService {

companion object {
val instance: GitHubService by lazy {
val
retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build()
retrofit.create(GitHubService::class.java)
}
}
}

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store