DEV Community

Cover image for Demystifying Android Context: The Gateway Between Your App and the System
Elamparithi Ezhilarasi Murugan (Elam)
Elamparithi Ezhilarasi Murugan (Elam)

Posted on • Originally published at Medium

Demystifying Android Context: The Gateway Between Your App and the System

Many Android beginners use Context blindly, and this often leads to confusion or even memory leaks later. Understanding Context helps you write safer, cleaner code. In this article, I am trying to explain and figure out myself what context really is. I do not want to start with the definition for a context. I want to find the definition based on the how we use context in the app development.

We have Classes for each Application components and class for Application in the Android. If you try to find a parent class of these classes, deep down you will get to the ContextWrapper class. This class implements the abstract class called Context. Basically this abstract class which has methods and states and it gives access to different things.

Let’s take a look in to the places where we are using context. These are most common and frequently used places but we often won’t notice how Context plays a role in it.

1. Launching Application components

To launch application components such as Activity, Service, Broadcast receiver, Content provider we are using context. Let’s say we are launching a new activity from HomeActivity, First we create an intent and then call startActivity method. what is startActivity method where it is created? When you go to the source class you can see startActivity is in the context class. Same with startService, sendBroadcast, registerReceiver, getContentResolver.

class MainActivity : AppCompatActivity() {
    /**
     * 1. startActivity,
     * 2. startService,
     * 3. sendBroadcast,
     * 4. registerReceiver,
     * 5. getContentResolver
     * all these methods provided by the Context class
     */
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // To start activity
        val homeActivityIntent = Intent(this, HomeActivity::class.java)
        startActivity(homeActivityIntent)
        // To start Service
        val musicService = Intent(this, MusicService::class.java)
        startService(musicService)
        // To send Broadcast
        val intent = Intent("com.example.snippets.ACTION_UPDATE_DATA").apply {
            putExtra("com.example.snippets.DATA", "data")
            setPackage("com.example.snippets")
        }
        sendBroadcast(intent)
        // To register broadcast receiver
        val broadCast = BroadCast()
        val filter = IntentFilter("com.example.snippets.ACTION_UPDATE_DATA")
        ContextCompat.registerReceiver(this, broadCast, filter, ContextCompat.RECEIVER_EXPORTED)
        // To talk to content provider
        val cursor = contentResolver.query(
            ContactsContract.Contacts.CONTENT_URI,
            null, null, null, null
        )
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Context gives Application wide access

Handling Resources

In android resources such as Strings, drawables, dimensions, layouts everything is stored in the res folder. They belong to the whole application, not just a single component. Context helps to access these resources.

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

        // To access string and drawables
        val appName = resources.getString(R.string.app_name)
        val icon = getDrawable(R.drawable.ic_launcher_background)    

        // To create a views
        val button = Button(this)   
    }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
// To get layout inflator  
    val view = LayoutInflater.from(parent.context).inflate(R.layout.item_view, parent, false)
    return MyViewHolder(view)
}
Enter fullscreen mode Exit fullscreen mode

Context not only gives access to resources but also ensures the correct variant is chosen based on configuration. In android, resources can (strings, drawables, layouts) have multiple variants.

  • Locale = values-en/strings.xml vs values-ta/strings.xml
  • Screen density = drawable-hdpi/ vs drawable-xxhdpi/
  • Orientation = layout-land vs layout

When we use context.getResources().getString(R.string.app_name), Context understands the current configurations and provides correct resource variant.

Internally context holds the configurations class which has access to current locale, orientations, theme, screen size, density so when you call getResources context uses this configuration class and resolve which resource file to use.

Shared Preference

Shared preference one of the common way to store primitive data in android development. Shared preference is not tied to single application component it is also a part of the application. To access shared preference we rely on context.

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

    // To access shared preference
    val preferences = getSharedPreferences("FileName", Context.MODE_PRIVATE)

    // getSharedPreferences is a method provided by the Context class.
}
Enter fullscreen mode Exit fullscreen mode

Access and store private files

Android system provides all the Apps to store files in a specific space in the internal storage. To access and store files in that space, we depend on Context.

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // To access file form internal storage
        val file = File(filesDir, "filename")
        val cacheFile = File(cacheDir, "filename")

        // To store file in the internal storage
        openFileOutput("fileName", MODE_PRIVATE).use {
            it.write("Hello world".toByteArray())
        }

        // To list available files
        val files: Array<String> = fileList()

        // To create directory
        getDir("dirName", Context.MODE_PRIVATE)

        // To create cache file
        File.createTempFile("filename", null, cacheDir)

        // To delete private file
        deleteFile("fileName")
}
Enter fullscreen mode Exit fullscreen mode

Database

To create and access database in android, we extend this class called SQLiteOpenHelper. SQLiteOpenHelper needs Context because the database is stored in your app’s private directory. Context provides that path. Without Context, the system wouldn’t know where in your app’s sandbox to place the database.

class MyDbHelper(context: Context) : SQLiteOpenHelper(context, "mydb", null, 1) {} { … }
Enter fullscreen mode Exit fullscreen mode

3. Access to system services

To access various services that not tied to application but with the system we are relied on context. Context has a method called getSystemService(serviceName). This method returns the system level service handle for a specific service. We use this to get access to different services. I have mentioned some of the services here.

  • Location Manager
  • Clipboard Manager
  • Power Manager
  • Storage Manager
  • Notification Manager
// To get notification handle
val notificationManager: NotificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
Enter fullscreen mode Exit fullscreen mode

In conclusion, Context is a gateway between the Application and the Android system which helps to perform various operations such as managing app components, using system services, handling resources, inflating views. Based on where we are in the application and our needs, we need to be careful about which Context we use. In the next article, I’ll dive into the different types of Context (Application, Activity, etc.) and when to use each safely.

Top comments (0)