2.1: Understanding Activities and Intents

Contents:

In this chapter you'll learn about activities, the major building blocks of your app's user interface, as well as using intents to communicate between activities.

About activities

An activity represents a single screen in your app with an interface the user can interact with. For example, an email app might have one activity that shows a list of new emails, another activity to compose an email, and another activity for reading individual messages. Your app is a collection of activities that you either create yourself, or that you reuse from other apps.

Although the activities in your app work together to form a cohesive user experience in your app, each one is independent of the others. This enables your app to start activities in other apps, and other apps can start your activities (if your app allows it). For example, a messaging app you write could start an activity in a camera app to take a picture, and then start the activity in an email app to let the user share that picture in email.

Your app can start different activities

Typically, one activity in an app is specified as the "main" activity, which is presented to the user when launching the application for the first time. Each activity can then start other activities in order to perform different actions.

Each time a new activity starts, the previous activity is stopped, but the system preserves the activity in a stack (the "back stack"). When the user is done with the current activity and presses the Back button, it is popped from the stack (and destroyed) and the previous activity resumes.

When an activity is stopped because a new activity starts, the first activity is notified of that change with the activity's lifecycle callback methods. The Activity lifecycle is the set of states an activity can be in, from when it is first created, to each time it is stopped or resumed, to when the system destroys it. You'll learn more about the activity lifecycle in the next chapter.

Creating activities

To implement an activity in your app, do the following:

  • Create an activity Java class.
  • Implement a user interface for that activity.
  • Declare that new activity in the app manifest.

When you create a new project for your app, or add a new activity to your app, in Android Studio (with File > New > Activity), template code for each of these tasks is provided for you.

Create the activity class

Activities are subclasses of the Activity class, or one of its subclasses. When you create a new project in Android Studio, your activities are, by default, subclasses of the AppCompatActivity class. The AppCompatActivity class is a subclass of Activity that lets you to use up-to-date Android app features such as the action bar and material design, while still enabling your app to be compatible with devices running older versions of Android.

Here is a skeleton subclass of AppCompatActivity:

public class MainActivity extends AppCompatActivity {
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
   }
}

The first task for you in your activity subclass is to implement the standard activity lifecycle callback methods (such as OnCreate()) to handle the state changes for your activity. These state changes include things such as when the activity is created, stopped, resumed, or destroyed. You'll learn more about the activity lifecycle and lifecycle callbacks in the next chapter.

The one required callback your app must implement is the onCreate() method. The system calls this method when it creates your activity, and all the essential components of your activity should be initialized here. Most importantly, the OnCreate() method calls setContentView() to create the primary layout for the activity.

You typically define the user interface for your activity in one or more XML layout files. When the setContentView() method is called with the path to a layout file, the system creates all the initial views from the specified layout and adds them to your activity. This is often referred to as inflating the layout.

You may often also want to implement the onPause() method in your activity class. The system calls this method as the first indication that the user is leaving your activity (though it does not always mean the activity is being destroyed). This is usually where you should commit any changes that should be persisted beyond the current user session (because the user might not come back). You'll learn more about onPause() and all the other lifecycle callbacks in the next chapter.

In addition to lifecycle callbacks, you may also implement methods in your activity to handle other behavior such as user input or button clicks.

Implement a user interface

the user interface for an activity is provided by a hierarchy of views, which controls a particular space within the activity's window and can respond to user interaction.

The most common way to define a user interface using views is with an XML layout file stored as part of your app's resources. Defining your layout in XML enables you to maintain the design of your user interface separately from the source code that defines the activity's behavior.

You can also create new views directly in your activity code by inserting new view objects into a ViewGroup, and then passing the root ViewGroup to setContentView(). After your layout has been inflated -- regardless of its source -- you can add more views in Java anywhere in the view hierarchy.

Declare the activity in the manifest

Each activity in your app must be declared in the Android app manifest with the <activity> element, inside <application>. When you create a new project or add a new activity to your project in Android Studio, your manifest is created or updated to include skeleton activity declarations for each activity. Here's the declaration for the main activity.

<activity android:name=".MainActivity" >
   <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
   </intent-filter>
</activity>

The <activity> element includes a number of attributes to define properties of the activity such as its label, icon, or theme. The only required attribute is android:name, which specifies the class name for the activity (such as "MainActivity"). See the <activity> element reference for more information on activity declarations.

The <activity> element can also include declarations for intent filters. The intent filters specify the kind of intents your activity will accept.

 <intent-filter>
   <action android:name="android.intent.action.MAIN" />
   <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

Intent filters must include at least one <action> element, and can also include a <category> and optional <data>. The main activity for your app needs an intent filter that defines the "main" action and the "launcher" category so that the system can launch your app. Android Studio creates this intent filter for the main activity in your project:

The <action> element specifies that this is the "main" entry point to the application. The <category> element specifies that this activity should be listed in the system's application launcher (to allow users to launch this activity).

Other activities in your app can also declare intent filters, but only your main activity should include the "main" action.. You'll learn more about implicit intents and intent filters in a later section.

Add more activities to your project

The main activity for your app and its associated layout file comes with your project when you create it. You can add new activities to your project in Android Studio with the File > New > Activity menu. Choose the activity template you want to use, or open the Gallery to see all the available templates. Activity Gallery in Android Studio

When you choose an activity template, you'll see the same set of screens for creating the new activity that you did when you initially created the project. Android Studio provides these three things for each new activity in your app:

  • A Java file for the new activity with a skeleton class definition and onCreate() method. The new activity, like the main activity, is a subclass of AppCompatActivity.
  • An XML file containing the layout for the new activity. Note that the setContentView() method in the activity class inflates this new layout.
  • An additional <activity> element in the Android manifest that specifies the new activity. The second activity definition does not include any intent filters. If you intend to use this activity only within your app (and not enable that activity to be started by any other app), you do not need to add filters.

About intents

All Android activities are started or activated with an intent. Intents are message objects that make a request to the Android runtime to start an activity or other app component in your app or in some other app. You don't start those activities yourself;

When your app is first started from the device home screen, the Android runtime sends an intent to your app to start your app's main activity (the one defined with the MAIN action and the LAUNCHER category in the Android Manifest). To start other activities in your app, or request that actions be performed by some other activity available on the device, you build your own intents with the Intent class and call the startActivity() method to send that intent.

In addition to starting activities, intents are also used to pass data between activities. When you create an intent to start a new activity, you can include information about the data you want that new activity to operate on. So, for example, an email activity that displays a list of messages can send an intent to the activity that displays that message. The display activity needs data about the message to display, and you can include that data in the intent.

In this chapter you'll learn about using intents with activities, but intents are also used to start services and broadcast receivers. You'll learn about both those app components later on in the book.

Intent types

There are two types of intents in Android:

  • Explicit intents specify the receiving activity (or other component) by that activity's fully-qualified class name. Use an explicit intent to start a component in your own app (for example, to move between screens in the user interface), because you already know the package and class name of that component.
  • Implicit intents do not specify a specific activity or other component to receive the intent. Instead you declare a general action to perform in the intent. The Android system matches your request to an activity or other component that can handle your requested action. You'll learn more about implicit intents in a later chapter.

Intent objects and fields

An Intent object is an instance of the Intent class. For explicit intents, the key fields of an intent include the following:

  • The activity class (for explicit intents). This is the class name of the activity or other component that should receive the intent, for example, com.example.SampleActivity.class. Use the intent constructor or the intent's setComponent(), setComponentName() or setClassName() methods to specify the class.
  • The intent data. The intent data field contains a reference to the data you want the receiving activity to operate on, as a Uri object.
  • Intent extras. These are key-value pairs that carry information the receiving activity requires to accomplish the requested action.
  • Intent flags. These are additional bits of metadata, defined by the Intent class. The flags may instruct the Android system how to launch an activity or how to treat it after it's launched.

For implicit intents, you may need to also define the intent action and category. You'll learn more about intent actions and categories in section 2.3.

Starting an activity with an explicit intent

To start a specific activity from another activity, use an explicit intent and the startActivity() method. Explicit intents include the fully-qualified class name for the activity or other component in the Intent object. All the other intent fields are optional, and null by default.

For example, if you wanted to start the ShowMessageActivity to show a specific message in an email app, use code like this.

Intent messageIntent = new Intent(this, ShowMessageActivity.class);
startActivity(messageIntent);

The Intent constructor takes two arguments for an explicit intent.

  • An application context. In this example, the activity class provides the content (here, this).
  • The specific component to start (ShowMessageActivity.class).

Use the startActivity() method with the new intent object as the only argument. The startActivity() method sends the intent to the Android system, which launches the ShowMessageActivity class on behalf of your app. The new activity appears on the screen, and the originating activity is paused.

The started activity remains on the screen until the user taps the back button on the device, at which time that activity closes and is reclaimed by the system, and the originating activity is resumed. You can also manually close the started activity in response to a user action (such as a button click) with the finish() method:

public void closeActivity (View view) {
    finish();
}

Passing data between activities with intents

In addition to simply starting one activity from another, you also use intents to pass information between activities. The intent object you use to start an activity can include intent data (the URI of an object to act on), or intent extras, which are bits of additional data the activity might need.

In the first (sending) activity, you:

  1. Create the Intent object.
  2. Put data or extras into that intent.
  3. Start the new activity with startActivity().

In the second (receiving) activity, you:

  1. Get the intent object the activity was started with.
  2. Retrieve the data or extras from the Intent object.

When to use intent data or intent extras

You can use either intent data and intent extras to pass data between the activities. There are several key differences between data and extras that determine which you should use.

The intent data can hold only one piece of information. A URI representing the location of the data you want to operate on. That URI could be a web page URL (http://), a telephone number (tel://), a goegraphic location (geo://) or any other custom URI you define.

Use the intent data field:

  • When you only have one piece of information you need to send to the started activity.
  • When that information is a data location that can be represented by a URI.

Intent extras are for any other arbitrary data you want to pass to the started activity. Intent extras are stored in a Bundle object as key and value pairs. Bundles are a map, optimized for Android, where the keys are strings, and the values can be any primitive or object type (objects must implement the Parcelable interface). To put data into the intent extras you can use any of the Intent class's putExtra() methods, or create your own bundle and put it into the intent with putExtras().

Use the intent extras:

  • If you want to pass more than one piece of information to the started activity.
  • If any of the information you want to pass is not expressible by a URI.

Intent data and extras are not exclusive; you can use data for a URI and extras for any additional information the started activity needs to process the data in that URI.

Add data to the intent

To add data to an explicit intent from the originating activity, create the intent object as you did before:

Intent messageIntent = new Intent(this, ShowMessageActivity.class);

Use the setData() method with a Uri object to add that URI to the intent. Some examples of using setData() with URIs:

// A web page URL
messageIntent.setData(Uri.parse("http://www.google.com"));
// a Sample file URI
messageIntent.setData(Uri.fromFile(new File("/sdcard/sample.jpg")));
// A sample content: URI for your app's data model
messageIntent.setData(Uri.parse("content://mysample.provider/data"));
// Custom URI
messageIntent.setData(Uri.parse("custom:" + dataID + buttonId));

Keep in mind that the data field can only contain a single URI; if you call setData() multiple times only the last value is used. Use intent extras to include additional information (including URIs.)

After you've added the data, you can start the activity with the intent as usual.

startActivity(messageIntent);

Add extras to the intent

To add intent extras to an explicit intent from the originating activity:

  1. Determine the keys to use for the information you want to put into the extras, or define your own. Each piece of information needs its own unique key.
  2. Use the putExtra() methods to add your key/value pairs to the intent extras. Optionally you can create a Bundle object, add your data to the bundle, and then add the bundle to the intent.

The Intent class includes several intent extra keys you can use, defined as constants that begin with the word EXTRA_. For example, you could use Intent.EXTRA_EMAIL to indicate an array of email addresses (as strings), or Intent.EXTRA_REFERRER to specify information about the originating activity that sent the intent.

You can also define your own intent extra keys. Conventionally you define intent extra keys as static variables with names that begin with EXTRA_. To guarantee that the key is unique, the string value for the key itself should be prefixed with your app's fully qualified class name. For example:

public final static String EXTRA_MESSAGE = "com.example.mysampleapp.MESSAGE";
public final static String EXTRA_POSITION_X = "com.example.mysampleapp.X";
public final static String EXTRA_POSITION_Y = "com.example.mysampleapp.Y";

Create an intent object (if one does not already exist):

Intent messageIntent = new Intent(this, ShowMessageActivity.class);

Use a putExtra() method with a key to put data into the intent extras. The Intent class defines many putExtra() methods for different kinds of data:

messageIntent.putExtra(EXTRA_MESSAGE, "this is my message");
messageIntent.putExtra(EXTRA_POSITION_X, 100);
messageIntent.putExtra(EXTRA_POSITION_Y, 500);

Alternately, you can create a new bundle and populate that bundle with your intent extras. Bundle defines many "put" methods for different kinds of primitive data as well as objects that implement Android's Parcelable interface or Java's Serializable.

Bundle extras = new Bundle();
extras.putString(EXTRA_MESSAGE, "this is my message");
extras.putInt(EXTRA_POSITION_X, 100);
extras.putInt(EXTRA_POSITION_Y, 500);

After you've populated the bundle, add it to the intent with the putExtras() method (note the "s" in Extras):

messageIntent.putExtras(extras);

Start the activity with the intent as usual:

startActivity(messageIntent);

Retrieve the data from the intent in the started activity

When you start an activity with an intent, the started activity has access to the intent and the data it contains.

To retrieve the intent the activity (or other component) was started with, use the getIntent() method:

Intent intent = getIntent();

Use getData() to get the URI from that intent:

Uri locationUri = getData();

To get the extras out of the intent, you'll need to know the keys for the key/value pairs. You can use the standard Intent extras if you used those, or you can use the keys you defined in the originating activity (if they were defined as public.)

Use one of the getExtra() methods to extract extra data out of the intent object:

String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
int positionX = intent.getIntExtra(MainActivity.EXTRA_POSITION_X);
int positionY = intent.getIntExtra(MainActivity.EXTRA_POSITION_Y);

Or you can get the entire extras bundle from the intent and extract the values with the various Bundle methods:

Bundle extras = intent.getExtras();
String message = extras.getString(MainActivity.EXTRA_MESSAGE);

Getting data back from an activity

When you start an activity with an intent, the originating activity is paused, and the new activity remains on the screen until the user clicks the back button, or you call the finish() method in a click handler or other function that ends the user's involvement with this activity.

Sometimes when you send data to an activity with an intent, you would like to also get data back from that intent. For example, you might start a photo gallery activity that lets the user pick a photo. In this case your original activity needs to receive information about the photo the user chose back from the launched activity.

To launch a new activity and get a result back, do the following steps in your originating activity:

  1. Instead of launching the activity with startActivity(), call startActivityForResult() with the intent and a request code.
  2. Create a new intent in the launched activity and add the return data to that intent.
  3. Implement onActivityResult() in the originating activity to process the returned data.

You'll learn about each of these steps in the following sections.

Use startActivityForResult() to launch the activity

To get data back from a launched activity, start that activity with the startActivityForResult() method instead of startActivity().

startActivityForResult(messageIntent, TEXT_REQUEST);

The startActivityForResult() method, like startActivity(), takes an intent argument that contains information about the activity to be launched and any data to send to that activity. The startActivityForResult() method, however, also needs a request code.

The request code is an integer that identifies the request and can be used to differentiate between results when you process the return data. For example, if you launch one activity to take a photo and another to pick a photo from a gallery, you'll need different request codes to identify which request the returned data belongs to.

Conventionally you define request codes as static integer variables with names that include REQUEST. Use a different integer for each code. For example:

public static final int PHOTO_REQUEST = 1;
public static final int PHOTO_PICK_REQUEST = 2;
public static final int TEXT_REQUEST = 3;

Return a response from the launched activity

The response data from the launched activity back to the originating activity is sent in an intent, either in the data or the extras. You construct this return intent and put the data into it in much the same way you do for the sending intent. Typically your launched activity will have an onClick or other user input callback method in which you process the user's action and close the activity. This is also where you construct the response.

To return data from the launched activity, create a new empty intent object.

Intent returnIntent = new Intent();
Note: To avoid confusing sent data with returned data, use a new intent object rather than reusing the original sending intent object.

Return result intents do not need a class or component name to end up in the right place. The Android system directs the response back to the originating activity for you.

Add data or extras to the intent the same way you did with the original intent. You may need to define keys for the return intent extras at the start of your class.

public final static String EXTRA_RETURN_MESSAGE =
    "com.example.mysampleapp.RETURN_MESSAGE";

Then put your return data into the intent as usual. Here the return message is an intent extra with the key EXTRA_RETURN_MESSAGE.

messageIntent.putExtra(EXTRA_RETURN_MESSAGE, mMessage);

Use the setResult() method with a response code and the intent with the response data:

setResult(RESULT_OK,replyIntent);

The response codes are defined by the Activity class, and can be

  • RESULT_OK. the request was successful.
  • RESULT_CANCELED: the user cancelled the operation.
  • RESULT_FIRST_USER. for defining your own result codes.

You'll use the result code in the originating activity.

Finally, call finish() to close the activity and resume the originating activity:

finish();

Read response data in onActivityResult()

Now that the launched activity has sent data back to the originating activity with an intent, that first activity must handle that data. To handle returned data in the originating activity, implement the onActivityResult() callback method. Here is a simple example.

public void onActivityResult(int requestCode, int resultCode,  Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == TEXT_REQUEST) {
        if (resultCode == RESULT_OK) {
            String reply =
                data.getStringExtra(SecondActivity.EXTRA_RETURN_MESSAGE);
                // process data
        }
    }
}

The three arguments to the onActivityResult() contain all the information you need to handle the return data.

  • Request code. The request code you set when you launched the activity with startActivityForResult(). If you launch different activities to accomplish different operations, use this code to identify the specific data you're getting back.
  • Result code: the result code set in the launched activity, usually one of RESULT_OK or RESULT_CANCELED.
  • Intent data. the intent that contains the data returned from the launch activity.

The example method shown above shows the typical logic for handling the request and response codes. The first test is for the TEXT_REQUEST request, and that the result was successful. Inside the body of those tests you extract the return information out of the intent. Use getData() to get the intent data, or getExtra() to retrieve values out of the intent extras with a specific key.

Activity navigation

Any app of any complexity that you build will include multiple activities, both designed and implemented by you, and potentially in other apps as well. As your users move around your app and between activities, consistent navigation becomes more important to the app's user experience. Few things frustrate users more than basic navigation that behaves in inconsistent and unexpected ways. Thoughtfully designing your app's navigation will make using your app predictable and reliable for your users.

Android system supports two different forms of navigation strategies for your app.

  • Temporal or Back navigation, provided by the device back button, and the back stack.
  • Ancestral, or Up navigation, provided by you as an option in the app's action bar.

Back navigation, tasks, and the back stack

Back navigation allows your users to return to the previous activity by tapping the device back button Device back button  . Back navigation is also called temporal navigation because the back button navigates the history of recently viewed screens, in reverse chronological order.

The back stack is the set of activities that the user has visited and that can be returned to by the user with the back button. Each time a new activity starts, it is pushed onto the back stack and takes user focus. The previous activity is stopped but is still available in the back stack. The back stack operates on a "last in, first out" mechanism, so when the user is done with the current activity and presses the Back button, that activity is popped from the stack (and destroyed) and the previous activity resumes.

Because an app can start activities both inside and outside a single app, the back stack contains all the activities that have been launched by the user in reverse order. Each time the user presses the Back button, each activity in the stack is popped off to reveal the previous one, until the user returns to the Home screen. The activity back stack

Android provides a back stack for each task. A task is an organizing concept for all the activities the user interacts with when performing an operation, whether they are inside your app or across multiple apps. Most tasks start from the Android home screen, and tapping an app icon starts a task (and a new back stack) for that app. If the user uses an app for a while, taps home, and starts a new app, that new app launches in its own task and has its own back stack. If the user returns to the first app, that first task's back stack returns. Navigating with the back button only returns to the activities in the current task, not for all tasks running on the device. Android enables the user to navigate between tasks with the overview or recent tasks screen, accessible with the square button on lower right corner of the device Device task button . Recent Tasks Screen

In most cases you don't have to worry about managing either tasks or the back stack for your app—the system keeps track of these things for you, and the back button is always available on the device.

There may, however, be times where you may want to override the default behavior for tasks or for the back stack. For example, if your screen contains an embedded web browser where users can navigate between web pages, you may wish to use the browser's default back behavior when users press the device's Back button, rather than returning to the previous activity. You may also need to change the default behavior for your app in other special cases such as with notifications or widgets, where activities deep within your app may be launched as their own tasks, with no back stack at all. You'll learn more about managing tasks and the back stack in a later section.

Up navigation

Up navigation, sometimes referred to as ancestral or logical navigation, is used to navigate within an app based on the explicit hierarchical relationships between screens. With Up navigation, your activities are arranged in a hierarchy, and "child" activities show a left-facing arrow in the action bar Up button (in action bar) that returns the user to the "parent" activity. The topmost activity in the hierarchy is usually your main activity, and the user cannot go up from there. Up Navigation

For instance, if the main activity in an email app is a list of all messages, selecting a message launches a second activity to display that single email. In this case the message activity would provide an Up button that returns to the list of messages.

The behavior of the Up button is defined by you in each activity based on how you design your app's navigation. In many cases, Up and Back navigation may provide the same behavior: to just return to the previous activity. For example, a Settings activity may be available from any activity in your app, so "up" is the same as back -- just return the user to their previous place in the hierarchy.

Providing Up behavior for your app is optional, but a good design practice, to provide consistent navigation for the activities in your app.

Implement up navigation with parent activities

With the standard template projects in Android Studio, it's straightforward to implement Up navigation. If one activity is a child of another activity in your app's activity hierarchy, specify that activity's parent in the Android Manifest.

Beginning in Android 4.1 (API level 16), declare the logical parent of each activity by specifying the android:parentActivityName attribute in the <activity> element. To support older versions of Android, include <meta-data> information to define the parent activity explicitly. Use both methods to be backwards-compatible with all versions of Android.

Here are the skeleton definitions for both a main (parent) activity and a second (child) activity:

<application ... >
    <!-- The main/home activity (it has no parent activity) -->
    <activity
        android:name=".MainActivity" ...>
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>

    </activity>
    <!-- A child of the main activity -->
    <activity android:name=".SecondActivity"
        android:label="@string/activity2_name"
        android:parentActivityName=".MainActivity">
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.example.android.twoactivities.MainActivity" />
    </activity>
</application>

The related exercises and practical documentation is in Android Developer Fundamentals: Practicals.

Learn more

results matching ""

    No results matching ""