Search code examples
androidfunctionclasskotlinscreen-density

I don't know how to get the screen density for use in a class


I'm teaching myself how to program in Kotlin and I've run into a problem when I've tried to make a function in a class to handle some math for me.

The problem I've run into is that I don't know how to get the screen density as a var/val in the class that is out side of the MainActivity.kt

I've searched a lot for the answer and I can't seem to come up with the right term to get the resources that I need and it's driving me crazy. Every answer I've found either doesn't work or Android Studio says it's going to cause a memory leak.

I know how to get the density in the MainActivity.kt by using this code.

val scale: Float = resources.displayMetrics.density

Thanks for any help. I'm really enjoying learning Kotlin and how to make apps.


Solution

  • You need to access resources through a Context, as in context.resources.displayMetrics.density. An Activity is a Context, which is why you can just access resources directly (technically it's this.resources, where this is the Activity)

    If you're calling that directly within an Activity, it shouldn't be a problem! If it's in another class, you'll have to get a Context from somewhere - ideally passing one in (e.g. if an Activity is calling a function, it can pass itself as a Context parameter).

    There are a few things that are Contexts, but in this case specifically, you need to be careful:

    After Build.VERSION_CODES#R, Resources must be obtained by Activity or Context created with Context.createWindowContext(int, Bundle). Application#getResources() may report wrong values in multi-window or on secondary displays.

    Basically the Context needs to have an idea of how big the app window is, which an Activity will, but something like applicationContext might not.


    The main reason to be careful is that Activities are big, so you don't want to hold onto them when they're being passed as a Context, because you might keep them in memory after the Activity has been discarded - that would be a memory leak, and it might be what you're getting warned about!

    Typically if you did need to hold onto the Context for future use, you'd do something like myContext = passedContext.applicationContext - so you're not holding on to whatever object was passed in, you're plucking out the shared application context which is always in memory anyway, so it doesn't matter if you keep it. That's a safe way to store one.

    But like the docs mentioned above, that's no good here, so ideally your function would:

    • use the Activity context to calculate whatever within the function, and use/store that value, discarding the context, or
    • access something within the context (resources, displayMetrics etc) and store that if you need to access it later

    that way you're storing what you need to, and not holding onto the Activity when you don't need it


    Contexts are weird when you're starting out, but they're basically something that provides access to information about the environment the code is running in, and system functions. And some are more specific - like an Activity is a visible component in your app, so it can provide info like the dimensions of its window, things like that.

    So they're really important for Android apps, and you end up passing them around a lot (you'll see them as parameters in a lot of functions), but you also have to be careful of keeping Activitys in memory by storing references to them in components that outlive the Activity itself. Hope that makes some sense!