mirror of
https://github.com/LukeHagar/unicorn-utterances.git
synced 2025-12-07 04:21:57 +00:00
add blog posts from ddl site
This commit is contained in:
230
content/blog/introduction-to-android-framework/index.md
Normal file
230
content/blog/introduction-to-android-framework/index.md
Normal file
@@ -0,0 +1,230 @@
|
||||
---
|
||||
{
|
||||
title: "Introduction to Android: Contexts, Intents, and the Activity lifecycle",
|
||||
description: 'A basic overview of the main components of an Android app and how they interact with each other and the Android system',
|
||||
published: '2019-03-18T22:12:03.284Z',
|
||||
author: 'fennifith',
|
||||
tags: ['android'],
|
||||
attached: []
|
||||
}
|
||||
---
|
||||
|
||||
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"](https://developer.android.com/training/basics/firstapp/) 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"](https://developer.android.com/guide/topics/resources/localization) guide
|
||||
in the Android Developer Documentation.
|
||||
|
||||
For more about Drawables and other image assets, see
|
||||
["Drawable resources"](https://developer.android.com/guide/topics/resources/drawable-resource.html).
|
||||
|
||||
A general overview of app resources can be found
|
||||
[here](https://developer.android.com/guide/topics/resources/providing-resources).
|
||||
|
||||
## 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:
|
||||
|
||||
```java
|
||||
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:
|
||||
|
||||
```java
|
||||
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](https://issuetracker.google.com/issues/68393945)_ 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](https://developer.android.com/topic/libraries/architecture/index.html)
|
||||
suggest to use a [LiveData](https://developer.android.com/topic/libraries/architecture/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:
|
||||
|
||||
```java
|
||||
Intent intent = new Intent(context, ActivityClass.class);
|
||||
intent.putExtra("com.package.name.EXTRA_MESSAGE", "Hello world!"); // the data to send
|
||||
context.startActivity(intent);
|
||||
```
|
||||
|
||||
Now, when the new Activity is created (inside the `onCreate` method), you can obtain the provided
|
||||
data as such:
|
||||
|
||||
```java
|
||||
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](https://developer.android.com/reference/android/content/Intent).
|
||||
|
||||
### 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](https://developer.android.com/guide/components/activities/activity-lifecycle).
|
||||
|
||||
## 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](https://developer.android.com/reference/android/app/Service).
|
||||
|
||||
### 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](https://developer.android.com/reference/android/content/BroadcastReceiver).
|
||||
|
||||
## 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.
|
||||
Reference in New Issue
Block a user