Skip to content

A Kotlin application that goes into detail on why you would want to use Singleton pattern which ensure a class only has one instance, and provide a global point of access to it (global object) over something like global variables.

Notifications You must be signed in to change notification settings

JPrendy/singleton-kotlin-application

Repository files navigation

Singleton Kotlin application

Description

A Kotlin application that goes into detail on why you would want to use Singleton pattern which ensure a class only has one instance, and provide a global point of access to it (global object) over something like global variables.

Contents

Setup Steps

A singleton’s two most common use cases are:

  • To share data between two otherwise unrelated areas of your project.

  • To share logic that doesn’t have associated state throughout the app. Keep in mind that singletons are not a data persistence solution. Data in singletons live only as long as your app is in memory.

Avoid overusing singletons: It’s tempting to use singletons as a solution for all your data sharing needs. While handy at first, overusing singletons will cause maintainability issues because many parts of your code will suddenly depend on a singleton. You’ll find that making one change will affect several unrelated parts of your project. Use singletons sparingly to save yourself this headache.

The following happens when we use Global variables over Singletons

  • Unit testing requires clean isolated environments. This can become a nightmare when there’s a global mutable state hanging around as it will only tie up the states together. You’ll never be sure if the global state was changed in the previous unit test. So, you’ll have to set all globals to predetermined values before every test. That’s an overhead.

  • Violates Encapsulation, encapsulation refers to restricting direct access to data. To learn more about encapsulation, read Encapsulation.

  • Keep global variables to a bare minimum in the worst-case scenario in order to avoid an unmanageable and chaotic codebase.

To shows off the advantages of Singletons, I created an example where we initialize a Singleton in MainActivity.kt and update a Singleton variable and then when we launch a new activity FinalActivity.kt with an intent we can use that updated Singleton variable. This is better than using global variables.

In Kotlin, unlike Java, classes do not have static methods. In most cases, it’s recommended to simply use package-level functions instead. Since, Kotlin doesn’t have static member for class, it’s mean that you can’t create static method and static variable in Kotlin class, we need to use Singletons.

Our Singleton Singleton.kt, will looks like the following

package com.example.singleton_kotlin_application

object Singleton {
    init {
        println("Singleton initialized")
    }

    var message = "Singletons rock"

    fun showMessage(): String {
        return message
        println(message)
    }
}

class Test {
    init {
        Singleton.showMessage()
    }
}

fun main() {
    Singleton.showMessage()
    Singleton.message = "Singletons are cool"

    val test = Test()
}

Then our attempt of a static class in Kotlin StaticClass.kt will look like the following

package com.example.singleton_kotlin_application

class StaticClass(score: Int) {
    var score = 0
    var staticScore = 5

    init {
        this.score = score
    }

    fun getPoints(): Int {
        return staticScore
    }
}

To test the Singleton and the Static Class, we will use the following files

package com.example.singleton_kotlin_application

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.AlarmClock.EXTRA_MESSAGE
import android.view.View
import android.widget.TextView

@Suppress("UNUSED_PARAMETER")
class MainActivity : AppCompatActivity() {

    var globalVariable = "global variable from Main Activity"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        println(Singleton.showMessage())

        val staticClassExample = StaticClass(10)
        println(staticClassExample.score)
        staticClassExample.staticScore = 10

        globalVariable =  "global variable from Main Activity, assigned with a new value to be used for intents"

        Singleton.message = "Singletons are cool, assigned value from Main Activity"

        val singletonValueTextView = findViewById<TextView>(R.id.singletonValue)
        singletonValueTextView.text = staticClassExample.getPoints().toString()

        val staticValueTextView = findViewById<TextView>(R.id.staticValue)
        staticValueTextView.text = Singleton.showMessage()

        val intentExampleTextView = findViewById<TextView>(R.id.intentValue)
        intentExampleTextView.text = globalVariable
    }

    fun changeActivity(view: View) {
        // This is a way of switching to a new activity without passing any other information.
        // val intent = Intent(this, FinalActivity::class.java)

        // This intent passes value from a global variable
        val intent = Intent(this, FinalActivity::class.java).apply {
            putExtra(EXTRA_MESSAGE, globalVariable)
        }
        startActivity(intent)
    }
}

and

package com.example.singleton_kotlin_application

import android.os.Bundle
import android.provider.AlarmClock.EXTRA_MESSAGE
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

class FinalActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_final)

        //Important Intent, we getting the value we passed over from `MainActivity`
        var message = intent.getStringExtra(EXTRA_MESSAGE)

        val staticClassFinalExample = StaticClass(2)

        println(Singleton.showMessage())

        val staticFinalExampleTextView = findViewById<TextView>(R.id.staticFinalExample)
        staticFinalExampleTextView.text = "This value should be 10, but isn't " + staticClassFinalExample.getPoints().toString()

        val singletonFinalExampleTextView = findViewById<TextView>(R.id.singletonFinalExample)
        singletonFinalExampleTextView.text = Singleton.showMessage()

        val intentFinalExampleTextView = findViewById<TextView>(R.id.intentFinalExample)
        intentFinalExampleTextView.text = message
    }
}

When we run the following code, we will notice our changes we make in our Singleton is carried over to the FinalActivity.kt, while our changes we made with our Static class are not, since static classes don't work in Kotlin, which means that we had to initialize the class again returning it back to its initial state.

We also want our AndroidManifest.xml to be something like the following

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.singleton_kotlin_application">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Singletonkotlinapplication">

        <activity android:name=".FinalActivity"
            android:parentActivityName=".MainActivity"/>

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

How to run the project locally

To run the unit tests locally.

./gradlew testdebugUnitTest

To run the ui tests locally, but first we need an emulator to be open.

./gradlew connectedCheck

Tools

Linter: we use the following linter link.

Uploading Artifacts: we use the following way to upload Artifacts, they allow you to persist data like test results after a job has completed, see the following documentation link.

Creating images/icons: we use Figma to create images and icon. Figma makes it very easy to create designs in many different formats.

Creating a Mock Server: we use a mock server with Postman to quickly test apis, to see how to create a mock server, see the following video link.

Converting JSON to Code: A tool to generate models and serializers from JSON, schema, and GraphQL for working with data quickly & safely in any programming language link.

Mobile Specific Tools:

Fastlane: Fastlane allows us to automate our development and release process link.

App Center: App Center is used to distribute an app, making it very easy to test on a physical device by using a fastlane plugin link.

Proxyman: we use Proxyman to view HTTP/HTTPS requests as they happen, it is easier to debug network connections on mobile on Proxyman where we can test and mock specific network responses, see the following documentation link. Proxyman also allows you intercept and edit requests/responses, see the following documentation link.

Update Dependencies

Npm: How to update a npm package.

Gemfile: How to update a Gemfile package.

Documentations

Git Squash: How to Git Squash with VS Code link.

Git Worktree: How to use Git Worktree link.

Git Empty Commit: How to use Git Empty Commit link.

Common Design Patterns and App Architectures for Mobile: link and link.

Releases

How to manage releases in a repository link.

Helpful resources

The following link provides a helpful tutorial on how to use the object keyword in Kotlin to define singleton, companion and anonymous objects and to ensure Java interoperability.

The following link provides an example of using a static variable in Java, we would not want to repeat this in other projects due to possible issues.

The following links to a helpful video that explains Singleton Pattern - Design Patterns.

The following links to a helpful article explaining that Kotlin does not have static variables and we need to use Singletons classes instead.

The following links to a helpful article explaining how Singletons work with Kotlin with included examples.

The following links to a comment on how passing values between files with Singletons.

The following links to a list of reason on why using global variables are bad.

About

A Kotlin application that goes into detail on why you would want to use Singleton pattern which ensure a class only has one instance, and provide a global point of access to it (global object) over something like global variables.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages