Synchronized singleton in Kotlin - android

I am new to Kotlin. Still learning basic syntax.
I've heard about companion objects similar to static in Java. But don't know how to create synchronized singleton in Kotlin.

Just use
object Singleton {
// any members you need
}
It's already synchronized properly:
Object declaration's initialization is thread-safe.
Note that this doesn't guarantee calls on it thread-safe, but that's just as in Java.

There are also other ways, but those two are the most simple ones to generate a singleton class
Method 1- Kotlin Way
object SingletonObj {
init {
// do your initialization stuff
}
}
Method 2- Double Null Check Way in Kotlin
class SingletonObj {
private constructor(context: Context)
companion object {
#Volatile private var mInstance: SingletonObj? = null
public fun get(context: Context): SingletonObj =
mInstance ?: synchronized(this) {
val newInstance = mInstance ?: SingletonObj(context).also { mInstance = it }
newInstance
}
}
}

I think, little bit more research, and i found it . Here is how to do it . Please correct me if it can be done in better way.
companion object {
#Volatile private var INSTANCE: Singleton ? = null
fun getInstance(): Singleton {
if(INSTANCE == null){
synchronized(this) {
INSTANCE = Singleton()
}
}
return INSTANCE!!
}
}

Thread-safe and lazy:
class Singleton private constructor() {
companion object {
val instance: Singleton by lazy { Singleton() }
}
}
Double null check already implemented inside by lazy.

Related

Pass Application to singleton directly in Android

I'm a beginner to Android development.
When making a singleton in the Application Context, here is my code.
I pass the application context to the instantiation
class Blah{
companion object {
#Volatile
private var INSTANCE: Blah? = null
//Singleton
fun getInstance(applicationContext: Context): Blah =
INSTANCE ?: synchronized(this) {
INSTANCE ?: Blah(applicationContext).also { INSTANCE = it }
}
}
}
Can I pass the application directly to the instantiation? Like so:
class Blah{
companion object {
#Volatile
private var INSTANCE: Blah? = null
//Singleton
fun getInstance(application: Application): Blah =
INSTANCE ?: synchronized(this) {
INSTANCE ?: Blah(application).also { INSTANCE = it }
}
}
}
Will this present memory leaks?
Application is also a singleton. This doesn't cause a memory leak because there is only one instance of Application and when your app is running you have one and when your app isn't running there is nothing there, so go for it.

Singleton design pattern to get a RoomDatabase instance

I´m working with RoomDatabase and after setting up my DAO, Database, and Entity classes I need to find a way to get the RoomDatabase instance, but on the documentation, I see this:
If your app runs in a single process, you should follow the singleton
design pattern when instantiating an AppDatabase object. Each
RoomDatabase instance is fairly expensive, and you rarely need access
to multiple instances within a single process.
So from the Udacity courses I see there are various ways to do this the first one:
private lateinit var INSTANCE: MainDBForObjects
fun getDatabase(context: Context): MainDBForObjects{
if (!::INSTANCE.isInitialized){
INSTANCE = Room.databaseBuilder(
context,
MainDBForObjects::class.java, "database"
).fallbackToDestructiveMigration()
.build()
}
return INSTANCE
}
and the other one is with a companion object from the database abstract class:
companion object {
#Volatile
private var INSTANCE: SleepDatabase? = null
fun getInstance(context: Context): SleepDatabase {
synchronized(this) {
var instance = INSTANCE
if (instance == null) {
instance = Room.databaseBuilder(
context.applicationContext,
SleepDatabase::class.java,
"sleep_history_database"
)
.fallbackToDestructiveMigration()
.build()
INSTANCE = instance
}
return instance
}
}
}
So is there any important difference between these 2? is it one better than the other?
Added an implementation to create a Singleton object through double check locking.
This would ensure that a lock is only acquired when required (if the database object is uninitialized) as synchronization is generally expensive
The double check here refers to the null check again within synchronized block, this helps us in scenarios where a thread A had acquired the lock to initialize the object for the first time and other threads were also waiting for it (thread B, C) now as soon as thread A initializes the object and releases the lock all waiting threads immediately get the updated value.
The need for using a local variable is to ensure that partially initialized objects are not visible to threads leading to inconsistent state (related to language semantics, more info here --> https://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java)
companion object {
#Volatile
private var sInstance: SleepDatabase? = null
#JvmStatic
fun getInstance(context: Context): SleepDatabase {
val localInstance = sInstance
if (localInstance != null) {
return localInstance
}
return synchronized(this) {
var instance = sInstance
if (instance == null) {
instance = Room.databaseBuilder(
context.applicationContext,
SleepDatabase::class.java,
"sleep_history_database"
)
.fallbackToDestructiveMigration()
.build()
sInstance = instance
}
instance
}
}
}

How i can use context in Singleton?

I need in my Singleton -> Context. I know that I can't passing argument in constructor, because object hasn't constructor.
Then I call it from my Application class.
Here is the code:
object Singleton {
var userAgentInfo: String = UserAgentTools.buildUserAgent(context)
fun initializeSdk() {
AuthenticatorApiManager.initializeSdk(userAgentInfo)
}
}
Move the initialization of userAgentInfo to the initializeSDK method, and send the Context as an argument, make sure to send the ApplicationContext.
object Singleton {
var userAgentInfo: String? = null
fun initializeSdk(context: Context) {
userAgentInfo = UserAgentTools.buildUserAgent(context)
AuthenticatorApiManager.initializeSdk(userAgentInfo)
}
}
Make Application class and write below code.
companion object {
private lateinit var sInstance: ApplicationClass
fun getInstance(): ApplicationClass {
return sInstance
}
}
Use in object like below.
ApplicationClass.getInstance()
You can use context in your Singleton class using Application class instance.here it is

"singleton" pattern method best way in kotlin

Can someone help me to find a better way to create a single instance in Kotlin?
class Bar(private val blub) {
private lateinit var foo: FooService
private lateinit var doo: DooService
override fun getFooService(): FooService {
if (!::foo.isInitialized) {
foo = FooService(blub)
}
return foo
}
override fun getDooService(): DooService {
if (!::doo.isInitialized) {
doo = DooService(blub)
}
return doo
}
}
Edit:
I don't want to init every object at beginning - should be only done when needed.
Thanks in advance!
If your FooService doesn't take any parameters via the getter for its initialization, you can use an object for this:
object FooHolder {
val foo: FooService = FooService(...)
}
Otherwise, your pattern seems just fine, the alternative would be to use a nullable variable instead of lateinit, but they're essentially the same:
private var foo: FooService? = null
override fun getService(): FooService {
if (foo == null) {
foo = FooService(...)
}
return foo!!
}
Your class is not looking as singleton. Maybe you want something like this
class Bar(private val blub) {
var foo by lazy{ FooService(blub) }
}
OR you can put it in companion object to have the same instance between all objects

Using room as singleton in kotlin

I'm trying to use Room as singleton so I didn't have to invoke Room.databaseBuilder() -which is expensive- more than once.
#Database(entities = arrayOf(
Price::class,
StationOrder::class,
TicketPrice::class,
Train::class,
TrainCategory::class
), version = 2)
#TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun dao(): TrainDao
companion object {
fun createDatabase(context: Context): AppDatabase
= Room.databaseBuilder(context, AppDatabase::class.java, "trains.db").build()
}
}
Note:
Can't use Object because Room requires using abstract class.
singleton must be thread safe because multiple threads might access it at the same time.
must be able to take Context as an argument.
I have looked at all similar StackOverflow questions and none of them satisfy my requirements
Singleton with argument in Kotlin isn't thread-safe
Kotlin - Best way to convert Singleton DatabaseController in Android isn't thread-safe
Kotlin thread save native lazy singleton with parameter uses object
After some research, I found that I have two options.
Double-checked locking
Initialization-on-demand holder idiom
I considered implementing one of them, but this didn't felt right for Kotlin - too much boilerplate code.
After more research, I stumbled upon this great article which provides an excellent solution, which uses Double-checked locking but in an elegant way.
companion object : SingletonHolder<AppDatabase, Context>({
Room.databaseBuilder(it.applicationContext, AppDatabase::class.java, "train.db").build()
})
From the article:
A reusable Kotlin implementation:
We can encapsulate the logic to
lazily create and initialize a singleton with argument inside a
SingletonHolder class. In order to make that logic thread-safe, we
need to implement a synchronized algorithm and the most efficient
one — which is also the hardest to get right — is the double-checked
locking algorithm.
open class SingletonHolder<T, 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
}
}
}
}
Extra:
if you want Singleton with two arguments
open class SingletonHolder2<out T, in A, in B>(creator: (A, B) -> T) {
private var creator: ((A, B) -> T)? = creator
#Volatile private var instance: T? = null
fun getInstance(arg0: A, arg1: B): T {
val i = instance
if (i != null) return i
return synchronized(this) {
val i2 = instance
if (i2 != null) {
i2
} else {
val created = creator!!(arg0, arg1)
instance = created
creator = null
created
}
}
}
}
In this particular case I would resort to using Dagger 2, or some other dependency injection library like Koin or Toothpick. All three libraries allow to provide dependancies as singletons.
Here's the code for Dagger 2 module:
#Module
class AppModule constructor(private val context: Context) {
#Provides
#Singleton
fun providesDatabase(): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java,
"train.db")
.build()
}
}
AppComponent:
#Singleton
#Component(modules = arrayOf(
AppModule::class
))
interface AppComponent {
fun inject(viewModel: YourViewModel)
fun inject(repository: YourRepository)
}
Application class to provide injection:
class App : Application() {
companion object {
private lateinit var appComponent: AppComponent
val component: AppComponent get() = appComponent
}
override fun onCreate() {
super.onCreate()
initializeDagger()
}
private fun initializeDagger() {
component = DaggerAppComponent.builder()
.appModule(AppModule(this))
.build()
}
}
And then inject your database as singleton to wherever you need it (for example in your app's repository):
#Inject lateinit var appDatabase: AppDatabase
init {
App.component.inject(this)
}
Used #Volatile for thread safety.
public abstract class AppDatabase : RoomDatabase() {
abstract fun trainDao(): trainDao
companion object {
#Volatile
private var INSTANCE: AppDatabase? = null
fun getDatabase(context: Context): Db = INSTANCE ?: synchronized(this){
val instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase ::class.java,
"train-db"
).build()
INSTANCE = instance
instance
}
}
}
taken from : https://developer.android.com/codelabs/android-room-with-a-view-kotlin#7
You could make use of the Kotlin standard library's
fun <T> lazy(LazyThreadSafetyMode.SYNCHRONIZED, initializer: () -> T): Lazy<T>
companion object {
private lateinit var context: Context
private val database: AppDatabase by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
Room.databaseBuilder(context, AppDatabase::class.java, "trains.db").build()
}
fun getDatabase(context: Context): AppDatabase {
this.context = context.applicationContext
return database
}
}
Personally though, I would normally add ApplicationContext-dependent singletons inside the Application, e.g.
<!-- AndroidManifest.xml -->
<manifest>
<application android:name="MyApplication">
...
class MyApplication : Application() {
val database: AppDatabase by lazy {
Room.databaseBuilder(this, AppDatabase::class.java, "train.db").build()
}
}
You can even define an extension method for easy access as context.database.
val Context.database
get() =
generateSequence(applicationContext) {
(it as? ContextWrapper)?.baseContext
}.filterIsInstance<MyApplication>().first().database
Here's how i figured out...
#Database(entities = [MyEntity::class], version = dbVersion, exportSchema = true)
abstract class AppDB : RoomDatabase() {
// First create a companion object with getInstance method
companion object {
fun getInstance(context: Context): AppDB =
Room.databaseBuilder(context.applicationContext, AppDB::class.java, dbName).build()
}
abstract fun getMyEntityDao(): MyEntityDao
}
// This is the Singleton class that holds the AppDB instance
// which make the AppDB singleton indirectly
// Get the AppDB instance via AppDBProvider through out the app
object AppDBProvider {
private var AppDB: AppDB? = null
fun getInstance(context: Context): AppDB {
if (appDB == null) {
appDB = AppDB.getInstance(context)
}
return appDB!!
}
}
singleton in kotlin is real easy just do this
companion object {
#JvmStatic
val DATABASE_NAME = "DataBase"
#JvmField
val database = Room.databaseBuilder(App.context(), DataBase::class.java, DataBase.DATABASE_NAME).build()
}

Categories

Resources