Dark Mode Musings: Beware of the Context 🌗

I’ve been working on getting Dark Mode in our app fully supported and I stumbled upon an interesting finding:

The Application#applicationContext does not keep information about the theme that you have set via AppCompatDelegate.setDefaultNightMode(), only a View or Activity context has this information stored. 

After reading through Chris Banes’ articles and watching some great talks about Styles, Themes & Dark Mode, I felt pretty comfortable that I knew quite a bit about the Theming system on Android. However, this particular issue was something that I was not expecting, which is why I decided to document this particular problem I faced. 

The Problem

I have the “Dark theme” set on in my system settings, but inside my app I am explicitly setting the theme to light mode (even though my device theme is set to Dark):

AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)

I have two bitmap resources: one located in drawable and one located in drawable-night

Despite my app theme overriding the mode to “light”, the resource from drawable-night was loaded instead of the one from thedrawable folder. 

Why was this happening? 🧐

I was using the Application#applicationContext to load up the Bitmap: 

class BitmapLoader(val context: Context) {

override fun loadBitmap(@RawRes resourceId: Int, bitmapConfig: Bitmap.Config): Bitmap? {
val options =
BitmapFactory.Options().apply { inPreferredConfig = bitmapConfig }
return BitmapFactory.decodeResource(
context.resources, resourceId, options
)
}
}
// This class was used in a similar way to the following usage:
val bitmapLoader = BitmapLoader(application.applicationContext)

The Solution

The fix in this case was to use the Activity#context method or View#context, instead of Application#applicationContext

The lesson here is that the Application#applicationContext should not be used for retrieving UI resources. The Application object will only have system-level information and not information that is set by AppCompatDelegate. AppCompatDelegate only works on the activity-level.

You need to be very careful of the context you use when obtaining resources that could change based on the theme. 


Thanks for reading, if you have any questions or comments — feel free to reach out on Twitter @riggaroo

Thanks to Chris Banes and Alan Viverette for confirming what I was experiencing is intentional.