53

On Android I want to make my application class a singleton.

Making it like this:

object MyApplication: Application(){}

won't work. The following error is thrown at runtime:

java.lang.IllegalAccessException: private com....is not accessible from class android.app.Instrumentation.

Doing this is also not possible:

class MyApp: Application() {

    private val instance_: MyApp

    init{
        instance_ = this
    }

    override fun onCreate() {
        super.onCreate()
        if (BuildConfig.DEBUG) {
            Timber.plant(Timber.DebugTree());
        }
    }

    companion object{
        fun getInstance() = instance_
    }
}

How can I get an instance of my application class everywhere in my app? I would like to use MyApp.instance() instead of (applicationContext as MyApp).

Also an explanation why I want this: I have classes in my app. For example, a SharedPreference Singleton which is initialised with a context, and as it’s a singleton, it can't have arguments.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
johnny_crq
  • 4,291
  • 5
  • 39
  • 64
  • please don't access the context statically, it's very bad and it could create problems later on. Instead context in Android should be accessed [on demand](https://www.fwd.cloud/commit/post/android-context-on-demand/) – Alessio Jul 22 '19 at 14:20

4 Answers4

121

You can do the same thing you would do in Java, i.e. put the Application instance in a static field. Kotlin doesn't have static fields, but properties in objects are statically accessible.

class MyApp: Application() {

    override fun onCreate() {
        super.onCreate()
        instance = this
    }

    companion object {
        lateinit var instance: MyApp
            private set
    }
}

You can then access the property via MyApp.instance.

Kirill Rakhman
  • 42,195
  • 18
  • 124
  • 148
  • 6
    Whats the use of `private set` at bottom ? – Manohar Jun 26 '18 at 05:45
  • 3
    @Redman because we want only MyApp can set the instance value – letroll Aug 23 '18 at 08:24
  • 3
    It gives "lateinit property instance has not been initialized" Error – Shlomi Fresko Feb 08 '19 at 10:06
  • @ShlomiFresko then you're accessing it before `onCreate` was called. – Kirill Rakhman Feb 09 '19 at 11:06
  • Won't this create a new object and now you have two Application objects in memory although you only need one? – Shaishav May 28 '19 at 12:51
  • @Shaishav no, you're only assigning the reference to the same object to a property. – Kirill Rakhman May 28 '19 at 14:16
  • 1
    @KirillRakhman one object will be created by the system at app launch and the other object will be due to `companion object`. One may hold reference to other if needed, as above but, both will have to survive as long as the app stays alive. Is this inference incorrect? – Shaishav May 29 '19 at 05:43
  • 1
    @Shaishav The companion object is of a different type than the outer class, you can look up in the bytecode. You will have one instance of the `MyApp` class and one insance of `MyApp$Companion` holding a reference to the `MyApp` instance. Try it out here: https://pl.kotl.in/Wm_sTSSQo – Kirill Rakhman May 30 '19 at 08:10
  • @KirillRakhman thanks, the example really helped. Also, the decompiled bytecode showed a different inner class as you suggested. – Shaishav May 30 '19 at 09:29
  • How about also including `@Volatile` (before `lateinit`) as explained here: https://stackoverflow.com/questions/106591/what-is-the-volatile-keyword-useful-for – Mark Feb 04 '20 at 13:52
  • The private set is used so that a value can’t be assigned from an external class. @ManoharReddy – Dino Sunny Apr 01 '20 at 06:26
  • Since it's Kotlin, can't you declare the instance val as a top-level variable? – zombiesauce May 03 '20 at 10:49
  • @VivekYadav then you can't use `lateinit` modifier. Which means either you have to initialize at declaration (which is not possible here) or you have to declare its type as `MyApp?` which then has to be accessed everywhere as `instance!!` which is quite ugly. – Sourav Kannantha B Apr 13 '21 at 12:00
50

If you want to use it to access some static properties you have there: You will only have one instance of your Application, so simply use the name you gave to the class. Don't worry about it not being an actual singleton, you can use it the same way.

Example:

class MyApp : Application() {

    companion object {
        const val CONSTANT = 12
        lateinit var typeface: Typeface
    }

    override fun onCreate() {
        super.onCreate()
        typeface = Typeface.createFromAsset(assets, "fonts/myFont.ttf")
    }

}

Then you can use MyApp.CONSTANT and MyApp.typeface anywhere in your app.

-

If what you want is to use it as an application context you can create an extension property for Context:

val Context.myApp: MyApp
        get() = applicationContext as MyApp

Then you can use myApp to get the the application context anywhere you have a context.

rpattabi
  • 9,984
  • 5
  • 45
  • 53
nitrico
  • 603
  • 6
  • 9
  • 2
    This looks good @nitrico, but i can't get the ´val Context.myApp: MyApp get() = applicationContext as MyApp´ part to work. Could you go into a little more detail? Where this line needs to go and how to access it in another class? – shredder Nov 14 '17 at 17:35
  • 1
    "get the the application context anywhere you have a context." - Can you explain why if I had a context I would need/want the application context? Seems like if I had a context of any kind, I would likely prefer to use that anyway. The use case where I want the application context is when I don't have a context otherwise, for example, a utility class that accesses a resource string. I'm confused. – Mitch Nov 29 '18 at 09:08
  • Wasn't it simpler in JAVA? – TheOnlyAnil Jan 04 '19 at 14:31
  • Won't this create a new object and now you have two Application objects in memory although you only need one? – Shaishav May 28 '19 at 12:47
  • @Mitch "Can you explain why if I had a context I would need/want the application context" The difference is that you can introduce a memory leak to Activity context, if you keep reference of object created using its context when the Activity is no longer needed. If you keep that object (such as image resource) Activity is never GC'd. Application exists during whole app lifespan, so there is no such leak happening. Application context should not be used for task like layout inflation etc. See https://medium.com/@banmarkovic/what-is-context-in-android-and-which-one-should-you-use-e1a8c6529652 – Pavel Lahoda Apr 21 '20 at 19:37
19
class AppController : Application() {

    init {
        instance = this
    }

    companion object {
        private var instance: AppController? = null

        fun applicationContext() : AppController {
            return instance as AppController
        }
    }

    override fun onCreate() {
        super.onCreate()
        
    }
}
Ryan M
  • 18,333
  • 31
  • 67
  • 74
Raja Jawahar
  • 6,742
  • 9
  • 45
  • 56
  • 1
    Thanks Raja Jawahar this is what I was looking for something concise and the best solution. – Fred Grott Aug 31 '18 at 13:48
  • I think this is the best solution. If we put instance = this in onCreate() method, it may cause some problem(ContentProvider may call create before Application's onCreate() method). – Charon Chui Jun 17 '21 at 13:33
  • Dear Raja: I have this code as AppController() class that contain a String and I have an activity and a Fragment. inside of fragment I am calling that string from AppController as `AppController.mytext2` but my app crashed and it returned `AppController$Companion.getMytext2(AppControler.kt:11)`. What is wrong? – C.F.G Jan 29 '23 at 10:10
3

You cannot do that because Android creates an Application instance using its parameterless constructor.

The problem you want to solve can be easily solved with DI. Just create instances with an injector so that the Context can be injected into objects as a dependency.

Michael
  • 53,859
  • 22
  • 133
  • 139
  • ya i always knew about that solution but was hoping there was another way. Thanks man – johnny_crq May 23 '16 at 13:39
  • Can you please clear my doubt? : If context is used as a static variable, then if the dependent class holds its reference, what will not be garbage collected : Application object or the context variable? And if we use DI, then the context is passed to the dependent class, now if that holds the reference, then will the object will be garbage collected? Two chained questions, I hope its clear. Thanks! – ashwin mahajan Jan 31 '19 at 22:53
  • Actually `Application` is `Context` and it won't be gc'ed while the application is running. – Michael Feb 01 '19 at 06:09