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 aService
) 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 maybeonStop()
called; okay, REALLY stop doing stuff now
Then, if the user navigates back to your activity...
onRestart()
calledonStart()
calledonResume()
called
When the application is completely closed by the user, then you will receive:
onPause()
calledonStop()
calledonDestroy()
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.