Introduction to Android: Contexts, Intents, and the Activity lifecycle

August 22, 2019

1,809 words

Post contents

This is a basic summary of the different components of Android and what they can be used for. It is written with the assumption that you already have basic knowledge about Android development, such as Java programming and the basic construction of a simple Android app (e.g. Activity classes, the AndroidManifest.xml, and layout files).

If you are completely new to Android development, I would recommend following through Android's "Build your first app" tutorial before reading this article.

Contexts

In Android, a Context is a general class that... facilitates your app's interaction with the Android system? I'm not sure how to best explain it, but it essentially gives you access to everything in your application from string resources and fonts to starting new Activites.

The Application, Activity, and Service classes all extend Context, and View classes all require an instance of one to be displayed (you can obtain this instance by using the View's .getContext() method). This allows you to access information such as the device's screen orientation, locale, and obtain assets particular to this information. For example, locale-specific string resources (which are commonly defined in res/values/strings.xml) can be obtained by calling context.getString(R.string.string_name), while Drawables (a type of image asset) can be obtained using context.getDrawable(R.drawable.drawable_name).

The R class that is used to obtain these resources is a collection of static identifiers that is automatically generated by Android Studio at build/compile-time.

For more about translating strings, see the "Localize your app" guide in the Android Developer Documentation.

For more about Drawables and other image assets, see "Drawable resources".

A general overview of app resources can be found here.

Intents

Every component inside of an Android app is started by an Intent. Components declared in an app's manifest can typically be invoked from anywhere in the system, but you can define intent-filters to declare that they should be started by a specific type of "thing". Your app's main activity has a filter like android.intent.category.LAUNCHER, which is how the home screen knows to display and launch that specific activity when the user opens your app.

Assuming that you have an active Context, you can start other activities inside your application by firing an intent that references the classes directly, like:

context.startActivity(new Intent(context, ActivityClass.class));

This call to startActivity sends the Intent to the Android system, which is then in charge of creating and opening the activity that you have specified.

Starting an Unknown Activity

You do not always need to specify an explicit class to start a new activity, though. How would your app start an activity in another application? You don't know what its class name is, and if you did, you likely wouldn't be able to reference it since it isn't a part of your app. This is where intent-filters come in: they allow you to start an activity without explicitly stating which activity should be launched. Take a look at the following intent:

Intent intent = new Intent();intent.setAction(Intent.ACTION_VIEW);intent.setDataAndType(Uri.parse("content://..."), "image/*");context.startActivity(intent);

This intent will open any activity on the device that claims it is able to display image files from a URI. The first (or most prominent) activity that the Android system finds with a filter that contains the android.intent.action.VIEW action and accepts image/* data will be launched by the system. If you want to let the user choose which activity is launched that meets the criteria, you could open a "share menu" with context.startActivity(Intent.createChooser(intent, "Open with..."));. Part of the reason that Android's share menus are notorious for being so ridiculously slow is that in order to display these lists, it has to query every single activity on the device asking "will you accept this Intent?" to make a list for the user to choose from.

Sending Data to Activities

In order for an Activity to have any dynamic functionality, you will need to have some way of sending information to it. When you create an Intent to a new Activity, you should never have an instance of the created activity, as it is created and managed separately by the system. While under normal circumstances this may not present any obvious issues, there are situations where this would not be possible (for example, starting an activity in a different process or task hierarchy). However, you still need a reliable way to tell an activity what to display while abiding by the laws of the system. There are two main ways of doing this, both of which have their own advantages and disadvantages:

1. Create your own state / data provider.

This indirectly relies on having access to an instance of the activity, though it should not fail if it does not obtain an instance; rather than relying on the started activity being created, it acts as more of a general solution to managing the data or state across your application.

The Android Architecture Components suggest to use a LiveData observable data class for this purpose, which allows you to persist a set of information across your entire application and notify other parts of your app when it is changed. While this is a very robust solution that will make your application much easier to maintain in the long run, it can be a little bit complicated, especially if you are writing a simple application that only needs to manage a small amount of information.

2. Use Intent extras.

The other, much simpler method of transferring data between activities is to simply include the data in the Intent. This data will be passed with the Intent to the Android system when it starts the activity, which will then give it to the new Activity once it has been created. For example:

Intent intent = new Intent(context, ActivityClass.class);intent.putExtra("com.package.name.EXTRA_MESSAGE", "Hello world!"); // the data to sendcontext.startActivity(intent);

Now, when the new Activity is created (inside the onCreate method), you can obtain the provided data as such:

Bundle extras = getIntent().getExtras();if (extras != null) {    String message = extras.getString("com.package.name.EXTRA_MESSAGE");    // message == "Hello World!"}

Of course, this has its restrictions; since the data is passed to the system, there is a size limit on the amount of data that you can pass through an Intent - most primitive types and small data structures / serializable classes will be fine, but I would recommend against passing heavier classes such as Bitmaps or Drawables.

For more information about Intents, there is a more descriptive summary of them in the Android Developer Documentation.

Note: More about Contexts

When your application is opened by the system, the first components to be created will be the Application, then the Activity - which will have a different Context from the Application component. If you Activity contains a layout with a set of views, the context that a view has can probably be cast to an Activity (like (Activity) view.getContext()) without any problems, but it... isn't a very good idea to assume this, as there are weird situations where this might not work.

If your app needs to have a global "thing" shared between all of its components, or if you need to notify the parent activity of an event occurring in a view, then it is best to put that inside of your app's Application class (which should be referenced from the manifest) and have your Activities and other parts of your application look there for the information. The Application class can be obtained from any Context instance by calling context.getApplicationContext().

Activity Lifecycle

Activities are big and complicated things, and many events can occur during their use as a result of user interaction or just weird Android memory-saving things. However, it is important to know what state of the lifecycle your Activity is in when performing a task as things can go very wrong if you try to do something at the wrong time, or fail to stop at the right time.

You are probably familiar with the onCreate() method - this is the first event to happen in the Activity lifecycle. Here, you declare and inflate your Activity's layout and set up the UI. After this, onStart() and onResume() are called once your layout becomes visible and the user can interact with it. From here, a few different events can occur...

Let's say that another Activity comes into the foreground (but this activity is still visible behind it; imagine a popup or something that the user will return to your app from).

  • onPause() called; stop doing anything significant - playing music or any continuous task not running in another component (like a Service) should be ceased.

Then, if the user returns to the activity...

  • onResume() called; resume whatever was paused previously

If the user leaves your activity completely, then you will get:

  • onPause() called; probably stop doing stuff maybe
  • onStop() called; okay, REALLY stop doing stuff now

Then, if the user navigates back to your activity...

  • onRestart() called
  • onStart() called
  • onResume() called

When the application is completely closed by the user, then you will receive:

  • onPause() called
  • onStop() called
  • onDestroy() called

A more comprehensive overview of the Activity lifecycle can be found here.

More...

What about tasks that you want to exist beyond the Activity lifecycle? Maybe you want music to keep playing after the user leaves the app, or you just want to perform a short action without opening an activity when a certain event occurs. There are two other components that can receive intents for this purpose: Service and BroadcastReceiver.

Services

Services can run in the background without being attached to a user interface for longer periods of time, for tasks such as playing music, downloading large files, or other potentially lengthy operations that shouldn't be terminated when the user leaves the app.

See: Service documentation.

Broadcast Receivers

A broadcast receiver can be seen as more of an "event" that occurs once and is over. They can run independently from a UI, the same a Service, but only for a short period of time (I believe they are terminated by the system after ~10 seconds - citation needed). However, they are given a Context, and can fire an intent to start other components of the app if needed.

Broadcast receivers are a little special in that they don't have to be declared explicitly in the AndroidManifest.xml. While Activities and Services must be declared in order to be used, broadcast receivers can be registered dynamically when your application is running, and can be unregistered again when they are no longer needed.

See: BroadcastReceiver documentation.

Fin

That's all for now! This was not a very thorough overview of Android development, and I feel like I left a lot of holes and exceptions to what I mentioned here, but hopefully it is useful to someone.

Subscribe to our newsletter!

Subscribe to our newsletter to get updates on new content we create, events we have coming up, and more! We'll make sure not to spam you and provide good insights to the content we have.