2.1: Activities and intents

Contents:

Introduction

In this chapter you learn about the Activity class, the major building block of your app's user interface (UI). You also learn about using an Intent to communicate from one activity to another.

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 probably a collection of activities that you create yourself, or that you reuse from other apps.

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

Typically, one Activity in an app is specified as the "main" activity, for example MainActivity. The user sees the main activity when they launch the app for the first time. Each activity can start other activities 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, the activity 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 by way of the activity lifecycle callback methods. The activity lifecycle is the set of states an Activity can be in: when the activity is first created, when it's stopped or resumed, and when the system destroys it. You learn more about the activity lifecycle in a later chapter.

Creating an Activity

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

  • Create an Activity Java class.
  • Implement a basic UI for the Activity in an XML layout file.
  • Declare the new Activity in the AndroidManifest.xml file.

When you create a new project for your app, or add a new Activity to your app by choosing File > New > Activity, the template automatically performs the steps listed above.

Create the Activity

When you create a new project in Android Studio and choose the Backwards Compatibility (AppCompat) option, the MainActivity is, by default, a subclass of the AppCompatActivity class. The AppCompatActivity class lets you use up-to-date Android app features such as the app 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 learn more about the Activity lifecycle and lifecycle callbacks in a different 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 UI 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. The system calls this method as the first indication that the user is leaving your Activity (though it does not always mean that 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 learn more about onPause() and all the other lifecycle callbacks in a later 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 the activity's UI

The UI for an activity is provided by a hierarchy of View elements, which controls a particular space within the activity window and can respond to user interaction.

The most common way to define a UI using View elements 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 UI separately from the source code that defines the activity behavior.

You can also create new View elements 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 View elements anywhere in the View hierarchy.

Declare the Activity in AndroidManifest.xml

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

<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 Intent 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 MainActivity 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 MainActivity in your project.

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

Each Activity in your app can also declare Intent filters, but only your MainActivity should include the "main" action. You learn more about how to use an implicit Intent and Intent filters in a later section.

Add another Activity to your project

The MainActivity for your app and its associated layout file is supplied by an Activity template in Android Studio such as Empty Activity or Basic Activity. You can add a new Activity to your project by choosing File > New > Activity. 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 see the same set of screens for creating the new activity that you did when you created the project. Android Studio provides 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 MainActivity, 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 AndroidManifest.xml file that specifies the new activity. The second Activity definition does not include any Intent filters. If you plan 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

Each activity is started or activated with an Intent, which is a message object that makes a request to the Android runtime to start an activity or other app component in your app or in some other app.

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 AndroidManifest.xml file). To start another activity in your app, or to request that some other activity available on the device perform an action, you build your own intent and call the startActivity() method to send the intent.

In addition to starting an activity, an intent can also be used to pass data between one activity and another. 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 learn about using intents with activities, but intents can also be used to start services or broadcast receivers. You learn how to use those app components in another practical.

Intent types

Intents can be explicit or implicit:

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

Intent objects and fields

For an explicit Intent, the key fields include the following:

  • The Activity class (for an explicit Intent). 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 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 an implicit Intent, you may need to also define the Intent action and category. You learn more about Intent actions and categories in another chapter.

Starting an Activity with an explicit Intent

To start a specific Activity from another Activity, use an explicit Intent and the startActivity() method. An explicit Intent includes 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 want 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 context (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 from one Activity to another

In addition to simply starting one Activity from another Activity, you also use an Intent to pass information from one Activity to another. 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 or Intent extras to pass data from one Activity to another. 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 geographic 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. A Bundle is a map, optimized for Android, in which a key is a string, and a value 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 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 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 = intent.getData();

To get the extras out of the Intent, you 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 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 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.

A return result Intent does 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. In the following, 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 canceled the operation.
  • RESULT_FIRST_USER: For defining your own result codes.

You 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 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 a different Activity 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 more than one Activity. As your users move around your app and from one Activity to another, 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.

  • Back (temporal) navigation, provided by the device Back button, and the back stack.
  • Up (ancestral) navigation, provided by you as an option in the app 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 each Activity 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 an Activity both inside and outside a single app, the back stack contains each Activity that has 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 each Activity 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 returns only to the Activity 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 an Activity deep within your app may be launched as its own task, with no back stack at all. You 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, each Activity is arranged in a hierarchy, and each "child" Activity shows a left-facing arrow in the app bar  Up button (in app bar) that returns the user to the "parent" Activity. The topmost Activity in the hierarchy is usually MainActivity, and the user cannot go up from there.  Up button for 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 your app.

Implement Up navigation with a parent Activity

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 the parent of that other Activity in the AndroidManifest.xml file.

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.

The following are the skeleton definitions in AndroidManifest.xml for both a main (parent) Activity (MainActivity) and a second (child) Activity (SecondActivity):

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <!-- The main 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>
    <!-- The child activity) -->
    <activity android:name=".SecondActivity"
       android:label = "Second Activity"
       android:parentActivityName=".MainActivity">
       <meta-data
          android:name="android.support.PARENT_ACTIVITY"
          android:value="com.example.android.twoactivities.MainActivity" />
       </activity>
</application>

You learn more about Up navigation and other user navigation features in another practical.

The related practical is 2.1: Activities and intents.

Learn more

Android Studio documentation:

Android developer documentation:

results matching ""

    No results matching ""