All Android Developers come across the Android Context daily even for the most basic applications, but because the Context can be used under various circumstances for different reasons, it can be hard to absorb, resulting in lots of developers forming varying impressions around it.
Consequently, many developers will just “pass a context” when needed through the Application or Activity objects without further thinking since their code will build and run; a tactic that may lead to memory leaks and nasty app crashes due to poor usage of the Context in the application code.
Also, ambiguity about what the Context is, or why it is used, prevents developers from having a clear picture of the code they write, and the libraries they use.
But much of the confusion around the Context begins by not realizing how closely the Manifest is related to the Context, and by not knowing that by looking at them together, can expose much about how Apps are coupled with the Android System.
This article will approach the Manifest and the Context in respect to the underlying Android architecture, for the reader to understand what they semantically represent from an application’s mechanics standpoint.
For that, this article will intentionally avoid code technicalities, and stick to what the Context really represents, and the problems it is called to solve because understanding should precede learning and not the other way around.
The Application & the Android System
When you install an application, it is given its own unique Linux User ID and runs in its own instance of the Android Runtime. Thus, each application is completely isolated with all its resources inaccessible to every other app.
Then, when you run an application, it is restricted from constructing and managing its own components and accessing even its own resources by direct code. Instead, it depends on requesting the Android System to do all that on its behalf.
Overall, this isolation approach assures a more secure, better structured, and less error-prone application due to leaving much of the responsibility to the Operating System, rather than to developer code.
But if Android is that restrictive to its apps, then two issues need to be addressed.
- The applications need a way to communicate with the Android System, so they can request it to construct and provide any components and resources that are otherwise inaccessible directly by code.
- The Android System should keep track of all installed applications and their components, in order to launch or provide them back to the caller when receiving a request that fits their existence.
To address the above problems the Context and the Manifest respectively come to the rescue, and as you can see there is an obvious correlation between them.
When an application is created, the Android System really knows nothing about it, unless posted in the application’s
Even to have the app’s icon at the App Drawer, an activity needs to be declared as the launching activity via an implicit intent declaration (using an
intent-filter) in the manifest.
- Package Information of the application, for the OS to add the app to the Application Layer of the Android Software Stack and assign a unique user to run it, so the app can become reachable after installation
- Permissions using the
<permission>tag for Android to know what restricted hardware and software resources should be enabled during app execution
- and any custom subclass implementation of the Four Application Components for Android to be able to provide them upon request. These are
<provider>for Content Providers, and
<receiver>for Broadcast Receivers.
Now, there is a reason we call these four classes “The Four Application Components”. That’s because out of all the custom classes created in our code, only subclasses of these four components can make it inside the manifest (through the mentioned above tags).
But why does Android need subclasses of these four types to be declared at the manifest?
Let’s start with intents…
When you make an explicit intent from some activity to open another activity, you never directly create the new activity object and pass variables to its constructor. Instead, you use an explicit intent to ask Android to create and launch the target activity (specified by its class name) on your behalf and pass a bundle with data (rather than passing constructor parameters). And this is what an explicit intent really is.
In explicit intents, the OS knows where to locate the component you explicitly requested by class name because it indexed it during app installation, thanks to the associated manifest declaration.
Because the Android Operating System has already indexed all the components by package AND class name during app installation — thanks to the associated manifest declarations — , it knows where to find the component you requested by class name, in order to construct and launch it on behalf of the caller.
On the other hand, when you make an implicit intent, you don’t really know what activity will handle your request. The Android System will check all the Manifest declarations that fit the request’s intent-filter of every installed app, to figure what set of activities (or other component types) can handle your “intention”.
As a side note, it should now make sense why bundles can only contain a fixed set of data types. It is because in intents it is the Operating System that will construct the components on behalf of the caller, and therefore it should also receive and forward the caller’s parameters to the new component. So bundles is a mechanism to pass compressed parameters between two components with the Operating System sitting in between. For Android to pack the caller’s data (or serialize it) and reconstruct that data at the target component (or deserialize it), it needs to use data types it knows about (or combinations of such types), and this is the reason behind fixed data types in bundles.
“Intents” is Android’s way to have a Context (which is discussed later) ask the OS to to construct and deliver application components on behalf of the caller app (like starting other activities, services, or delivering a broadcast).
(See also Pending Intents)
Application Components in the Manifest
- Activities & Manifest: Android gets aware of all the activities so it can create and launch them explicitly or implicitly upon intent requests.
- Services & Manifest: Android gets aware of all the available services so it knows what background operations it can run.
- Content Providers & Manifest: Android is aware of all the content providers, so it can provide content to your app or other apps when needed.
- Broadcast Receivers & Manifest: Android is aware of all receivers registered to listen to a broadcast (via manifest declaration), so even when the app is not running, the receivers that are registered to a specific broadcast will be notified.
Side Note on Receivers:
It is not obligatory to define Receivers in the manifest. Apps can receive broadcasts either by “manifest-declared receivers” (using the
<receiver> tag at manifest) where the registered receiver will listen to broadcasts permanently, or by “dynamically-declared receivers” at the runtime (using the
Context.registerReceiver() method) where the receiver “listen” to broadcasts during the time you asked it to “listen”.
So, these four application components when declared at the manifest, let the Android OS know they are there, so it can take action when some request by your app (or some other app) fits their existence, explicitly or implicitly.
What is Android Manifest — TL;DR
The manifest notifies the Android System of the application’s own existence, any permissions needed, and the components of your app, so Android can construct them, launch them, update their lifecycle status, or interact with them on your behalf.
This approach yields a potentially richer app experience due to other applications’ components becoming available to your app by their manifest declarations, either by fitting to your implicit intent’s intent-filter or by providing you content through their Content Providers.
Context & Non-Context Manifest Components
Before talking about the Context (which follows next), we should make clear that not all manifest entries refer to
Context Manifest Components
are Context class descendant objects that perform UI or background operations. They are directly constructed and launched by the OS and because of that, they have a Lifecycle whose state is administered by the OS.
These manifest components are:
Non-Context manifest components
are not Context class descendant objects, however, they use a provided context to get a standardized job done.
These manifest components are:
- Content Providers
That is, with Content Providers, we use the Application Context as a communication vehicle to be provided with the desired content.
- Broadcast Receivers
That is, from a broadcast, which was delivered to the Android System via a context (by your app, another app, or system service), the Android will notify all the receivers registered to listen to that broadcast
Let’s start with the official Context definition that even if it might appear odd initially, it should make perfect sense by the end of the article…
Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.
Having a better grasp of the manifest, you apprehend that with so much burden falling to the Android System, your app needs a handle to the Android to pass its requests. That handle comes in the form of an Android Context.
As seen from the Google definition, context is an abstract class, which means you cannot find any direct Context objects, but you can find Context subclasses, such as Application, Activities, Services, etc. So when dealing with these classes, you are dealing with a Context.
And since the Context implementation is provided by Android, to create and launch Context objects, the construction needs to be conducted by OS managed construction mechanisms, such as intents, rather than by direct code with classic constructors.
But if the Context implementation is provided by Android, that means the Context can act as the intermediary between your App and Android, and thus as a window to Global Device Information. Like if your phone is using a light or dark theme, if it is in portrait or landscape mode, but also allow for seamless integrations to global settings, like allowing Android to automatically apply the correct font size to a custom
TextView by joining the device’s global font-size setting with your custom SP value during layout inflation, or by returning the correct variation of a resource’s value.
Yes, Resources too are provided by the Android System through the Context, and not by direct application code. Android provides resources by a [key/value] based mechanism that uses a context to request a resource’s value.
This is not only secure as you don’t get filesystem access to the “res” folder, but it is also smart, as the Android System can even decide between returning different variations of a resource’s values based on Global Settings, like the device language preference to return the appropriate value of string resource kept in multiple language versions, or returning appropriate variation of an image, based on the pixel density and size of your device’s screen.
So, the Android Context binds your app with the Android System, for anything that cannot be done or retrieved without going through the Android System
But since the above classes have different lifecycles, passing for example an activity’s context as the android handle to something related to the entire app, your activity wouldn’t ever be allowed to be garbage collected as we hold a reference to itself, which translates to a “memory leak”.
Why not just always use the ApplicationContext since all we need is a handle to android and forget about memory leaks? Why use different life cycled contexts?
- Activity inherits from
ContextThemeWrapper, while Service and Application do not (see that “Theme” word in that class name?). As ApplicationContext is not UI related, it cannot be used to inflate layouts, start activities, or display dialogues. If you attempt to affect the UI using a Context that is not UI related, you will only crash your application.
- So, depending on what context we pass, we get access to different information about the application’s environment, like with two different UI Context objects at the same application that use a different theme (take a look at this awesome video from Coding in Flow — also look at this article about ContextThemeWrapper).
- Now for other tasks, like for a non-UI singleton object that will be used throughout the application, and requires context (like access to a database) you will use ApplicationContext to avoid memory leaks because you will hold the same database reference throughout the application lifetime.
So, whenever you try to do or fetch something that needs to go through the Android System, a context will be required.
The roles of Context in method calls
Passing a context in method calls in Android is very frequent. However, the fact that you may pass a context for completely different reasons, is a major source of confusion for developers, as the context’s role is not clear.
So, when you include a context in a method call, the context might play one of the following three roles:
- Passive role: To ask the OS for some value (eg: what is the value of a resource).
- Active role: To ask the OS to perform some action on your behalf (eg: launching a component through an Intent, inflate some layout, display a message, etc).
- Binding role: To connect your app to a distant entity or mechanism managed by the OS (eg: connecting to an SQLite database, where that binding will be used in both “active” and “passive” roles to read or update data in the database — in such cases, you almost always use an Application Context, as the binding should exist throughout the lifetime of the entire app).
In any case, a context is always the OS intermediary that stands between your code and the Android System, delivering requests in order for some information to be retrieved or for some action to take place.
So the next time you use a “context” in a method call, you will know it is the vehicle that will reach the OS and ask it to execute or retrieve your method’s request.
The role of Context as an object?
On top of the three roles mentioned above, where the context is used actively through method calls as a way for the app to accomplish something through the Android OS, there is another role that represents the Context as an object itself.
Component role: the role that represents a custom launchable component. These custom launchable components belong to the Application Layer of the Software Stack but extend or use classes defined in the Application Framework, and as such serve as a bridge between the application and the operating system.
When these Context descendant components are created and launched by the Android OS with mechanisms like “intents” (rather than directly through constructors), they obtain Lifecycle managed by the OS.
For example, for a class that extends the Activity class, if you try to instantiate it by normal means (
MyActivity ma = new MyActivity();) the
onCreate() method WILL NOT be called. Only if you start the Activity with an Intent, will the method be called (source).
So the role of Context as object, is to bridge its newly constructed subclass component (eg. Activity) with the Android System, so the latter can provide and update the Lifecycle of that component.
What is Android Context — TL;DR
The Context is a class provided by Android, and as such, its job is to bridge your application code with the Android System. Through your Application class and other custom components that inherit from Context, your application gains the ability to access resources and functionalities reachable only by the Operating System.
When objects of such classes get instantiated by the Operating System (through an OS controlled instantiation mechanism, like “intents”), they become administered by the Operating System, and as such, they obtain lifecycle.
For anything else, passing a context as a parameter in method calls, allows this method to use the context as a channel of communication with the OS, in order to reach the OS and ask it to perform some action or return some resource.
Visualizing the Manifest & the Context
A good analogy to visualize the concept of the Manifest and the Context could be an old fashioned calling centre switchboard:
- The base is the Application Framework where all wires that connect every application component with the Android System emerge from.
- Each application through its manifest declarations exposes plug-holes for every declared component to the Android System, so it can construct them and put a context wire in order to manage them.
- And finally, each wire is the Android Context part of the constructed launchable component which binds that application component with the Android System.
So the manifest adds Applications to the Application Layer of the Software Stack and creates plugholes for each application and its components. When the Android System constructs a component that inherits from Context, a Context wire is automatically plugged into the newly constructed component’s plughole so the OS can link to it and manage it.
You can assume that during component destruction, its wire gets unplugged. While when another component gets constructed, a new wire emerges and connects to the corresponding manifest-declared plughole.
Testing & Design Patterns
Now you know why the context exists, you will appreciate the difference between unit and instrumented tests.
In a nutshell, unit tests do not require any interaction with the Android OS, and run your test code directly at the JVM, while the integration tests assume the involvement of the Android OS with your code at some point and thus they need to run on an emulator or real device, making them significantly slower and cumbersome than local unit tests.
So, instrumented tests are usually required when you need to perform something that involves heavily the OS and obviously involves a Context, or if you are testing custom components directly (like an Activity which is Context subclass).
(As a side-note, for simple cases where you just use a context that doesn’t require deep involvement of the OS, there are ways to mock a context and avoid running instrumented tests — but this is out of this article’s scope).
With all that in mind, if with design patterns like the MVVM (Model-View-ViewModel), which is endorsed by Google, you can keep all the Business Logic in the ViewModel, and keep that logic away from UI related code and from Data Layers (by providing mock data repositories with sample data), then your Business Logic should be clear of Context items, so you can test your ViewModels with local rather than instrumented tests, leading to faster testing, and a much clearer, and easier to debug code.
But to isolate the business logic it is necessary to understand when your code involves the Android OS, and when it doesn’t.
So, understanding how your apps are coupled with the Android System can help you to acknowledge even further the reasons behind choosing Design Patterns (such as MVC or MVVM) that promote the Separation of Concerns, and maybe even adjust the way you approach and write code!
Find more about testing here: https://developer.android.com/training/testing/fundamentals
The significance of Context
As we saw from the “Testing” section of the article, if the context didn’t exist, every test could be run as a unit test, and thus the Android would only be libraries built on top of the JVM.
So, if the context didn’t exist at all, you wouldn’t have an Android App. You would have a Java App or a Kotlin App that would simply require some additional libraries to work at any operating system with JRE installed.
So with all that, we can argue that it is due to the Context, your app is an “Android App”.