- Kotlin singletons with argument
- object has its limits
- Passing an argument
- An Android use case
- A reusable Kotlin implementation
- iamnaran / Application.kt
- How to create a Singleton class in Kotlin?
- Singleton Class
- Properties of Singleton Class
- Rules for making a class Singleton
- Singleton Example
- Object extending a class
- Singleton class with Argument in Kotlin
Kotlin singletons with argument
object has its limits
Aug 26, 2017 · 5 min read
In Kotlin, the singleton pattern is used as a replacement for static members and fields that don’t exist in that programming language. A singleton is created by simply declaring an object .
Contrary to a class , an object can’t have any constructor, but init blocks are allowed if some initialization code is needed.
The object will be instantiated and its init blocks will be executed lazily upon first access, in a thread-safe way. To achieve this, a Kotlin object actually relies on a Java static initialization block. The above Kotlin object will be compiled to the following equivalent Java code:
T h is is the preferred way to implement singletons on a JVM because it enables thread-safe lazy initialization without having to rely on a locking algorithm like the complex double-checked locking. By simply using an object declaration in Kotlin, you are guaranteed to get a safe and efficient singleton implementation.
Passing an argument
But what if the initialization code requires some extra argument? Because a Kotlin object can’t have any constructor, you can’t pass any argument to it.
There are valid cases for which passing an argument to a singleton initialization block is the recommended pattern. The alternative requires the singleton class to be aware of some external component to be able to retrieve that argument, violating the principle of separation of concerns and making the code less reusable. To mitigate the issue, that external component could be a dependency injection system. It’s a valid solution but you don’t always want to use that kind of library and there are some cases where it can’t be used, like we’ll see in the following Android example.
Another scenario where you have to rely on a different way to manage a singleton in Kotlin is when the concrete implementation of the singleton is generated by an external tool or library (Retrofit, Room, …) and its instance is retrieved using a custom builder or factory method. In that case, you usually declare the singleton as an interface or abstract class and it can’t be an object .
An Android use case
On the Android platform, you often need to pass a Context instance to initialization blocks of singleton components so they can retrieve file paths, read settings or access services, but you also want to avoid keeping a static reference to it (even if a static reference to the application Context is technically safe). There are two ways to achieve that:
- 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 :
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.
Note that adding the @Volatile annotation to the instance field is required for the algorithm to work properly.
This may not be the most compact or elegant Kotlin code, but it’s the one producing the most efficient bytecode for the double-checked locking algorithm. Trust the Kotlin authors on this: this code is actually borrowed directly from the implementation of the lazy() function in the Kotlin standard library, which is synchronized by default. It has been modified to allow passing an argument to the creator function.
Given its relative complexity, it’s not the kind of code you want to write (or read) more than once, so the goal is to reuse that SingletonHolder class everytime you have to implement a singleton with argument.
The logical place to declare the getInstance() function is inside the companion object of the singleton class, which allows it to be called by using simply the singleton class name as qualifier, similarly to a static method in Java. One powerful feature that Kotlin companion objects offer is their ability to inherit from a base class like any other object , which enables something comparable to static-only inheritance. In this case, we want to use SingletonHolder as a base class for the companion object of the singleton, in order to reuse and automatically expose its getInstance() function on the singleton class.
As for the creator function passed as argument to the SingletonHolder constructor, a custom lambda could be declared inline but the common solution is to pass a reference to the private constructor of the singleton class. The final code for a singleton with argument would look like this:
Not as short as the object notation, but probably the next best thing.
The singleton may now be invoked using the following syntax and its initialization will be lazy and thread-safe:
You can also use this idiom when a singleton implementation is generated by an external library and the builder requires an argument. Here’s an example using the Room Persistence Library for Android:
Note: When the builder doesn’t require an argument, you can simply use a lazy delegated property instead:
I hope you found these code snippets useful. If you want to suggest other ways to solve this problem or have questions, don’t hesitate to start a discussion in the comments section. Thank you for reading!
Источник
iamnaran / Application.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
import android.app.Activity |
import android.app.Application |
import android.content.Context |
import android.content.SharedPreferences |
import android.os.Bundle |
import android.preference.PreferenceManager |
import android.util.Log |
class Application : Application (), Application.ActivityLifecycleCallbacks < |
companion object < |
private var instance : Application ? = null |
private var sharedPreferences : SharedPreferences ? = null |
private var isActive : Boolean = false |
fun applicationContext (): Context < |
return instance !! .applicationContext |
> |
fun getSharedPreference (): SharedPreferences ? < |
return sharedPreferences |
> |
fun isActivityVisible (): Boolean < |
return isActive |
> |
> |
override fun onCreate () < |
super .onCreate() |
// initialize for any |
// Use ApplicationContext. |
// example: SharedPreferences etc. |
instance = this |
val context : Context = Application .applicationContext() |
registerActivityLifecycleCallbacks( this ) |
sharedPreferences = PreferenceManager .getDefaultSharedPreferences(applicationContext) |
> |
override fun onActivityPaused ( activity : Activity ? ) < |
isActive = false |
> |
override fun onActivityResumed ( activity : Activity ? ) < |
isActive = true |
> |
override fun onActivityStarted ( activity : Activity ? ) < |
> |
override fun onActivityDestroyed ( activity : Activity ? ) < |
> |
override fun onActivitySaveInstanceState ( activity : Activity ? , outState : Bundle ? ) < |
> |
override fun onActivityStopped ( activity : Activity ? ) < |
> |
override fun onActivityCreated ( activity : Activity ? , savedInstanceState : Bundle ? ) < |
> |
> |
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Источник
How to create a Singleton class in Kotlin?
In Android App, for an object which is required to be created only once and use everywhere, we use the Singleton Pattern. Singleton Pattern is a software design pattern that restricts the instantiation of the class to only “one” instance. So, to implement the Singleton pattern in our project or software, we make a singleton class. In this blog, we will learn how to make a singleton class in Kotlin? So, let’s get started.
Singleton Class
A singleton class is a class that is defined in such a way that only one instance of the class can be created and used everywhere.
It is used where we need only one instance of the class like NetworkService, DatabaseService, etc.
Generally, it is done because it takes the resource of the system to create these objects again and again. So it is better to create only once and use again and again the same object.
Properties of Singleton Class
Following are the properties of a typical singleton class:
- Only one instance: The singleton class has only one instance and this is done by providing an instance of the class, within the class. Also, outer classes and subclasses should be prevented to create the instance.
- Globally accessible: The instance of the singleton class should be globally accessible so that each class can use it.
Rules for making a class Singleton
The following rules are followed to make a Singleton class:
- A private constructor
- A static reference of its class
- One static method
- Globally accessible object reference
- Consistency across multiple threads
Singleton Example
Following is the example of Singleton class in java:
When creating the instance to ensure that there is no thread interference, we use the synchronized keyword.
Let’s look at the Kotlin code for the same. Below is the Kotlin code for Singleton class:
Ok, what to do after this?
Nothing! Are you kidding? No, that’s the code for using Singleton class in Kotlin. Very simple? Don’t worry, let’s look at the explanation.
In Kotlin, we need to use the object keyword to use Singleton class. The object class can have functions, properties, and the init method. The constructor method is not allowed in an object so we can use the init method if some initialization is required and the object can be defined inside a class. The object gets instantiated when it is used for the first time.
Let’s have an example of the Singleton class in Kotlin.
Here, in the above example, we are having one function named printVarName() and one property named “variableName”. When A class is instantiated, then changes can be reflected in the object class. So, the output of the above code will be:
Object extending a class
You can use an object in Kotlin to extend some class or implement some interface just like a normal class. Let’s have an example of the same:
And the output of the above code will be:
So, you can use object class just like a normal class in most of the cases.
Singleton class with Argument in Kotlin
In the earlier part of the blog, we learned that we can’t have constructors in a singleton class. To initialize something, we can do so by using init in the singleton class. But what if you need to pass some argument for initialization just like in parameterized constructors? Since we can’t use constructors here. So, we need to find some other way of doing the same.
If we particularly talk about Android, we know that in Android we generally need to pass a context instance to init block of a singleton. This can be done using Early initialization and Lazy initialization. In early initialization, all the components are initialized in the Application.onCreate() using the init() functions. But this results in slowing down the application startup by blocking the main thread. So, it is generally advised to use the lazy initialization way. In lazy initialization, we use the context as an argument to a function returning the instance of the singleton. We can achieve this by using a SingletonHolder class. Also, to make it thread-safe, we need to have a way of synchronization and double-checked locking.
The above code is the most efficient code for double-checked locking system and the code is somehow similar to the lazy() function in Kotlin and that’s why it is called lazy initialization. So, whenever you want a singleton class with arguments then you can use the SingletonHolder class.
Here, in the above code, in place of the creator function which is passed as an argument to the SingletonHolder, a custom lambda can also be declared inline or we can pass a reference to the private constructor of the singleton class. So, the code will be:
Now, the singleton can be easily invoked and initialized by writing the below code and this is lazy as well as thread-safe 🙂
That’s it for the Singleton blog. Hope you like this blog. You can also refer to the Kotlin website. To learn more about some of the cool topics of Android, you can visit our blogging website and can join our journey of learning.
Источник