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.
Related
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
}
}
}
Sorry if my title did not match to what my questions is.
I have created a Android Library, in which I have a Room database, As there should be only one instance of Room database, I have OfflineDatabaseManager getInstance method which provides the instance to the Android project which accesses it by passing the context. I have context within the Android project and I can pass it.
I want to listen to changes happening on the database table within the library so I can do something with it, I have written a class OfflineDataChangeListener within the library but to get the instance of the database I need to pass the context, how can I do that within the library please.
Library - OfflineDatabaseManager
class OfflineDatabaseManager private constructor(private val dp: LibraryDatabase) {
fun getOfflineData() : Flow<List<OfflineData>> {
return dp.getOfflineDataDao().getOfflineData()
}
suspend fun insertOfflineData(offlineData: OfflineData) {
dp.getOfflineDataDao().insertOfflineData(offlineData)
}
companion object {
#Volatile
private var INSTANCE: OfflineDatabaseManager? = null
fun getInstance(context: Context): OfflineDatabaseManager {
return INSTANCE ?: synchronized(this) {
INSTANCE ?: run {
val db = Room.databaseBuilder(
context,
LibraryDatabase::class.java, "database-name"
).build()
OfflineDatabaseManager(db).also { INSTANCE = it }
}
}
}
}
}
Library - OfflineDataChangeListener - HOW CAN I PASS CONTEXT TO GET THE INSTANCE OF DB
class OfflineDataChangeListener: CoroutineScope {
private var job: Job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
fun observeOfflineDataChanges() {
launch {
OfflineDatabaseManager.getInstance(HOW TO GET CONTEXT HERE).getOfflineData().collect {
Log.d("dbChangeListener", "I am listening to databas echanges")
}
}
}
}
Android project -
Within my android project this is how I access and listen to changes
fun getOfflineData() {
launch {
OfflineDatabaseManager.getInstance(app.applicationContext).getOfflineData().collect {
Timber.d( "observing offline data" + it.toString())
}
}
}
I want to do the same but within the library.
Thanks
R
You can provide some function to the clients which passes the Context to your library. In your library create an object class:
object Library {
lateinit var context: Context
fun init(ctx: Context) {
context = ctx.applicationContext
}
}
Clients must call your init() function, for example, in their Application's onCreate() method:
Library.init(this)
And in the library you can access to it like this:
OfflineDatabaseManager.getInstance(Library.context)
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
companion object {
#Volatile
private lateinit var instance: ExampleDatabase
fun getInstance(context: Context): ExampleDatabase {
synchronized(this) {
if(!::instance.isInitialized) {
instance = Room.databaseBuilder(
context.applicationContext, // Why does this require context?
LottoDatabase::class.java,
"lotto_database"
)
.fallbackToDestructiveMigration()
.build()
}
return instance
}
}
}
The above code is the general way of creating singleton of the room database.
I wonder why Room.databaseBuilder function requires a context as the parameter. I know this question might be stupid cuz I'm lack understanding of the Context in Android.
What argument should I pass in that parameter?
What can be different if I pass in the Activity context or application?
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.