Activities - Android Application Development: A Beginner's Tutorial (2015)

Android Application Development: A Beginner's Tutorial (2015)

Chapter 2. Activities

In Chapter 1, “Getting Started” you learned to write a simple Android application. It is now time to delve deeper into the art and science of Android application development. This chapter discusses one of the most important component types in Android programming, the activity.

The Activity Lifecycle

The first application component that you need to get familiar with is the activity. An activity is a window containing user interface (UI) components that the user can interact with. Starting an activity often means displaying a window.

An activity is an instance of the android.app.Activity class. A typical Android application starts by starting an activity, which, as I said, loosely means showing a window. The first window that the application creates is called the main activity and serves as the entry point to the application. Needless to say, an Android application may contain multiple activities and you specify the main activity by declaring it in the application manifest file.

For example, the following application element in an Android manifest defines two activities, one of which is declared as the main activity using the intent-filter element. To make an activity the main activity of an application, its intent-filter element must contain the MAIN action andLAUNCHER category like so.

<application ... >

<activity

android:name="com.example.MainActivity"

android:label="@string/app_name" >

<intent-filter>

<action android:name="android.intent.action.MAIN"/>

<category

android:name="android.intent.category.LAUNCHER"/>

</intent-filter>

</activity>

<activity

android:name="com.example.SecondActivity"

android:label="@string/title_activity_second" >

</activity>

</application>

In the snippet above, it is not hard to see that the first activity is the main activity.

When the user selects an application icon from the Home screen, the system will look for the main activity of the application and start it. Starting an activity entails instantiating the activity class (which is specified in the android:name attribute of the activity element in the manifest) and calling its lifecycle methods. It is important that you understand these methods so you can write code correctly.

The following are the lifecycle methods of Activity. Some are called once during the application lifetime, some can be called more than once.

§ onCreate

§ onStart

§ onResume

§ onPause

§ onStop

§ onRestart

§ onDestroy

To truly understand how these lifecycle methods come into play, consider the diagram in Figure 2.1.

image

Figure 2.1: The activity lifecycle

The system begins by calling the onCreate method to create the activity. You should place the code that constructs the UI here. Once onCreate is completed, your activity is said to be in the Created state. This method will only be called once during the activity life time.

Next, the system calls the activity’s onStart method. When this method is called, the activity becomes visible. Once this method is completed, the activity is in the Started state. This method may be called more than once during the activity life time.

onStart is followed by onResume and once onResume is completed, the activity is in the Resumed state. How I wish they had called it Running instead of Resumed, because the fact is this is the state where your activity is fully running. onResume may be called multiple times during the activity life time.

Therefore, onCreated, onStart, and onResume will be called successively unless something goes awry during the process. Once in the Resumed state, the activity is basically running and will stay in this state until something occurs to change that, such as if the alarm clock sets off or the screen turns off because the device is going to sleep, or perhaps because another activity is started.

The activity that is leaving the Resumed state will have its onPause method called. Once onPause is completed, the activity enters the Paused state. onPause can be called multiple times during the activity life time.

What happens after onPause depends on whether or not your activity becomes completely invisible. If it does, the onStop method is called and the activity enters the Stopped state. On the other hand, if the activity becomes active again after onPause, the system calls the onResumemethod and the activity re-enters the Resumed state.

An activity in the Stopped state may be re-activated if the user chooses to go back to the activity or for some other reason it goes back to the foreground. In this case, the onRestart method will be called, followed by onStart.

Finally, when the activity is decommissioned, its onDestroy method is called. This method, like onCreate, can only be called once during the activity life time.

ActivityDemo Example

The ActivityDemo application accompanying this book demonstrates when the activity lifecycle methods are called. Listing 2.1 shows the manifest for this application.

Listing 2.1: The manifest for ActivityDemo

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.example.activitydemo"

android:versionCode="1"

android:versionName="1.0" >

<uses-sdk

android:minSdkVersion="8"

android:targetSdkVersion="21" />

<application

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme" >

<activity

android:name="com.example.activitydemo.MainActivity"

android:screenOrientation="landscape"

android:label="@string/app_name" >

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category

android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

</application>

</manifest>

This manifest is like the one in Chapter 1, “Getting Started.” It has one activity, the main activity. However, notice that I specify the orientation of the activity using the android:screenOrientation attribute of the activity element.

The main class for this application is printed in Listing 2.2. The class overrides all the lifecycle methods of Activity and prints a debug message in each lifecycle method.

Listing 2.2: The MainActivity class for ActivityDemo

package com.example.activitydemo;

import android.os.Bundle;

import android.app.Activity;

import android.util.Log;

import android.view.Menu;

public class MainActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

Log.d("lifecycle", "onCreate");

setContentView(R.layout.activity_main);

}

@Override

public boolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar

// if it is present.

getMenuInflater().inflate(R.menu.menu_main, menu);

return true;

}

@Override

public void onStart() {

super.onStart();

Log.d("lifecycle", "onStart");

}

@Override

public void onRestart() {

super.onRestart();

Log.d("lifecycle", "onRestart");

}

@Override

public void onResume() {

super.onResume();

Log.d("lifecycle", "onResume");

}

@Override

public void onPause() {

super.onPause();

Log.d("lifecycle", "onPause");

}

@Override

public void onStop() {

super.onStop();

Log.d("lifecycle", "onStop");

}

@Override

public void onDestroy() {

super.onDestroy();

Log.d("lifecycle", "onDestroy");

}

}

Note that if you override an activity’s lifecycle method, you must call the overridden method in the parent class.

Before you run this application, create a Logcat message filter to show only messages from the application, filtering out system messages, by following these steps.

Select Debug from the Log level drop-down list.

Type in the filter text, in this case “lifecycle,” in the search box. Figure 2.2 shows the Logcat window.

image

Figure 2.2: Creating a Logcat message filter

Run the application and notice the orientation of the application. It should be landscape. Now, try running another application and then switch back to the ActivityDemo application. Check the messages printed in Logcat.

Note that when you create a new application using Android Studio, the activity class may not extend Activity but ActionBarActivity. ActionBarActivity is a class in the Support Library that supports using the action bar in pre-3.0 Android devices. (The action bar is discussed in Chapter 6, “The Action Bar.”) If you are not using the action bar or do not plan on deploying to pre-3.0 Android devices, you can replace ActionBarActivity with Activity.

Changing the Application Icon

If you do not like the application icon you have chosen, you can easily change it by following these steps.

§ Save a jpeg or png file in res/drawable. Png is preferred because the format supports transparency.

§ Edit the android:icon attribute of the manifest to point to the new image. You can refer to the image file with this format: @drawable/fileName, where fileName is the name of the image file without the extension.

Using Android Resources

Android is rich, it comes with a bunch of assets (resources) you can use in your apps. To browse the available resources, open the application manifest in Android Studio and fill a property value by typing "@android:" followed by Ctrl+space. Android Studio will show the list of assets. (See Figure 2.3).

image

Figure 2.3: Using Android assets

For example, to see what images/icons are available, select @android:drawable/. To use a different icon for an application, change the value of the android:icon attribute.

android:icon="@android:drawable/ic_menu_day"

Starting Another Activity

The main activity of an Android application is started by the system itself, when the user selects the app icon from the Home screen. In an application with multiple activities, it is possible (and easy) to start another activity. In fact, starting an activity from another activity can be done simply by calling the startActivity method like this.

startActivity(intent);

where intent is an instance of the android.content.Intent class.

As an example, consider the SecondActivityDemo project that accompanies this book. It has two activities, MainActivity and SecondActivity. MainActivity contains a button that when clicked starts SecondActivity. This project also shows how you can write an event listener programmatically.

The manifest for SecondActivityDemo is given in Listing 2.3.

Listing 2.3: The manifest for SecondActivityDemo

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.example.secondactivitydemo"

android:versionCode="1"

android:versionName="1.0" >

<uses-sdk

android:minSdkVersion="8"

android:targetSdkVersion="19" />

<application

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme" >

<activity

android:name="com.example.secondactivitydemo.MainActivity"

android:label="@string/app_name" >

<intent-filter>

<action android:name="android.intent.action.MAIN"/>

<category android:name="android.intent.category.LAUNCHER"/>

</intent-filter>

</activity>

<activity

android:name="com.example.secondactivitydemo.SecondActivity"

android:label="@string/title_activity_second" >

</activity>

</application>

</manifest>

Unlike the previous application, this project has two activities, one of which is declared as the main activity.

The layout files for the main and second activities are listed in Listings 2.4 and 2.5, respectively.

Listing 2.4: The activity_main.xml file

<RelativeLayout

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:paddingBottom="@dimen/activity_vertical_margin"

android:paddingLeft="@dimen/activity_horizontal_margin"

android:paddingRight="@dimen/activity_horizontal_margin"

android:paddingTop="@dimen/activity_vertical_margin"

tools:context=".MainActivity" >

<TextView

android:id="@+id/textView1"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/first_screen" />

</RelativeLayout>

Listing 2.5: The activity_second.xml file

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:paddingBottom="@dimen/activity_vertical_margin"

android:paddingLeft="@dimen/activity_horizontal_margin"

android:paddingRight="@dimen/activity_horizontal_margin"

android:paddingTop="@dimen/activity_vertical_margin"

tools:context=".SecondActivity" >

<TextView

android:id="@+id/textView1"

android:layout_width="wrap_content"

android:layout_height="wrap_content" />

</RelativeLayout>

Both activities contain a TextView. Touching the TextView in the main activity starts the second activity and pass a message to the latter. The second activity displays the message in its TextView.

The activity class for the main activity is given in Listing 2.6.

Listing 2.6: The MainActivity class

package com.example.secondactivitydemo;

import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

import android.view.Menu;

import android.view.MotionEvent;

import android.view.View;

import android.view.View.OnTouchListener;

import android.widget.TextView;

public class MainActivity extends Activity implements

OnTouchListener {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

TextView tv = (TextView) findViewById(R.id.textView1);

tv.setOnTouchListener(this);

}

@Override

public boolean onCreateOptionsMenu(Menu menu) {

// Inflate the menu; this adds items to the action bar if it

// is present.

getMenuInflater().inflate(R.menu.menu_main, menu);

return true;

}

@Override

public boolean onTouch(View arg0, MotionEvent event) {

Intent intent = new Intent(this, SecondActivity.class);

intent.putExtra("message", "Message from First Screen");

startActivity(intent);

return true;

}

}

To handle the touch event, the MainActivity class has implemented the OnTouchListener interface and overridden its onTouch method. In this method, you create an Intent and put a message in it. You then call the startActivity method to start the second activity.

The SecondActivity class is given in Listing 2.7.

Listing 2.7: The SecondActivity class

package com.example.secondactivitydemo;

import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

import android.view.Menu;

import android.widget.TextView;

public class SecondActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_second);

Intent intent = getIntent();

String message = intent.getStringExtra("message");

((TextView) findViewById(R.id.textView1)).setText(message);

}

@Override

public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.menu_second, menu);

return true;

}

}

In the onCreate method of SecondActivity, you set the view content as usual. You then call the getIntent method and retrieve a message from its getStringExtra method, which you then pass to the setText method of the TextView. You retrieve the TextView by calling the findViewByIdmethod.

The main activity and the second activity are shown in Figures 2.4 and 2.5, respectively.

image

Figure 2.4: The main activity in SecondActivityDemo

image

Figure 2.5: The second activity in SecondActivityDemo

Activity-Related Intents

In the SecondActivityDemo project, you learned that you can start a new activity by passing an intent to the startActivity method. You can also call startActivityForResult if you want a result from the invoked activity.

Here is the code that activates an activity in the project:

Intent intent = new Intent(this, SecondActivity.class);

startActivity(intent);

And often you want to pass additional information to the invoked activity, which you can do by attaching the information to the intent. In the previous example, you did so by calling the putExtra method on the Intent:

Intent intent = new Intent(this, SecondActivity.class);

intent.putExtra("message", "Message from First Screen");

startActivity(intent);

An intent that is constructed by passing to it an activity class is called an explicit intent. The Intent in SecondActivityDemo is such an example.

You can also create an implicit intent, in which case you do not specify an activity class. Rather, you pass to the Intent class’s constructor an action, such as ACTION_SEND, and let the system decide which activity to start. If there is more than one activities that can handle the intent, the system will normally ask the user to choose.

ACTION_SEND is a constant in the Intent class. Table 2.1 shows a list of actions that can start an activity as defined in the Intent class.

Action

Description

ACTION_MAIN

Start the activity as a main entry point.

ACTION_VIEW

View the data attached to the intent.

ACTION_ATTACH_DATA

Attach the data that has been added to the intent to some other place.

ACTION_EDIT

Edit the data attached to the intent.

ACTION_PICK

Pick an item from the data.

ACTION_CHOOSER

Displays all applications that can handle the intent.

ACTION_GET_CONTENT

Allow the user to select a particular kind of data and return it.

ACTION_DIAL

Dial the number attached to the intent.

ACTION_CALL

Call the person specified in the intent.

ACTION_SEND

Send the data attached to the intent.

ACTION_SENDTO

Send a message to the person specified in the intent data.

ACTION_ANSWER

Answer the incoming call.

ACTION_INSERT

Insert an empty item into the specified container.

ACTION_DELETE

Delete the specified data from its container.

ACTION_RUN

Run the attached data.

ACTION_SYNC

Perform a data synchronization.

ACTION_PICK_ACTIVITY

Select an activity from a set of activities.

ACTION_SEARCH

Perform a search using the specified string as the search key.

ACTION_WEB_SEARCH

Perform a web search using the specified string as the search key.

ACTION_FACTORY_TEST

Indicate this is the main entry point for factory tests.

Table 2.1: Intent actions for starting an activity

Not all intents can be used to start an activity. To make sure an Intent can revolve to an activity, call its resolveActivity method before passing it to startActivity:

if (intent.resolveActivity(getPackageManager()) != null) {

startActivity(intent);

}

An intent that cannot resolve to an action will throw an exception if passed to startActivity.

For example, here is an Intent to send an email.

Intent intent = new Intent(Intent.ACTION_SEND);

intent.setType("message/rfc822"); // required

intent.putExtra(Intent.EXTRA_EMAIL,

new String[] {"walter@example.com"}); // optional

intent.putExtra(Intent.EXTRA_SUBJECT, "subject"); // optional

intent.putExtra(Intent.EXTRA_TEXT , "body"); // optional

// Verify that the intent will resolve to an activity

if (intent.resolveActivity(getPackageManager()) != null) {

startActivity(intent);

} else {

Toast.makeText(this, "No email client found.",

Toast.LENGTH_LONG).show();

}

If multiple applications can handle an Intent, the user will be able to decide whether to always use the selected application in the future or to use it just for this occasion. You can force a chooser to appear each time (regardless of whether or not the user has decided to use the same app), by using this code:

startActivity(Intent.createChooser(intent, dialogTitle));

where dialogTitle is the title of the Chooser dialog.

As another example, the following code sends an ACTION_WEB_SEARCH intent. Upon receiving the message, the system will open the default web browser and tell the browser to google the search key.

String searchKey = "Buffalo";

Intent intent = new Intent(Intent.ACTION_WEB_SEARCH );

intent.putExtra(SearchManager.QUERY, searchKey);

startActivity(intent);

Summary

In this chapter you learned about the activity lifecycle and created two applications. The first application allowed you to observe when each of the lifecycle methods was called. The second application showed how to start an activity from another activity.