Android Logo MathCS.org - Android

Multiple Activities

java -> android -> multiple activities ...

An application is a collection of tasks, each of which is handled by a separate, small, well-defined activity. Thus, a single application consists of a collection of interconnected activities. For example a game could consist of six activities: a splash screen, a main menu screen, a set-options screen, a game-play screen, and a save-game screen, and a load-game screen. Each "screen" is controlled by its own activity.

Android maintains a stack of activities:

If you start an activity, it becomes visible because it is put on top of the stack. If your activity in turn starts another activity, that one is put on top and the original activity is right below. When the top activity quits, it is removed from the stack and the original one becomes visible again. That simple principle allows an easy and (usually) user-predictable transitions from one activity to the next.

There are three primary ways to start an activity, other than the obvious one where the user picks it from the home screen.

One activity calls another inside its package or project

To start a secondary activity, the parent activity calls:

 	startActivity(new Intent(this, NewActivity.class));

This will put the secondary activity on top of the stack, but as a child of the calling (parent) activity. For this to work the activity NewActivity must be present in the same package as the calling one, and it must have an appropriate entry in the manifest: inside the application tag, put as a minimum

  	<activity android:name="NewActivity"
        </activity>

The "inner" activity does not necessarily need its own icon or intent filter as the main activity does. The easiest way to do this is to edit the AndroidManifest.xml in graphics mode, switch to the "Application" tag, scroll down and "add" an appropriate new Activity. Note that the Intent in the method call could optionally include data to be passed to the child activity.

Example: Here are two activities. The first one is the main project activity, but the second one also has an entry in the manifest. The first activity is simply a button, which when clicked opens the second activity, which in turn consists of a simple editable text field. It does not need a "quit" button because the user can just click the deice BACK button (but you might add a "back" button to aid the user). For simplicity we get by without an XML layout file. We need three things: two source code classes (one per activity) and one manifest file:

Create a new project named "FirstTwoApps". Copy the default source code file FirstTwoApps.java to a new file SecondAct.java. Then edit the files as follows:

SecondAct.java

public class SecondAct extends Activity 
{
	private EditText input = null;
	
	public void onCreate(Bundle savedInstanceState) 
	{
	        super.onCreate(savedInstanceState);
	        
	        // initializing the field
	        input = new EditText(this);
	        input.setText("Enter text here ...");
        
	        // defining the layout in code instead of XML
	    	LinearLayout layout = new LinearLayout(this);
	    	layout.addView(input, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
    	
	    	// setting the view to our layout
    		setContentView(layout);
	}
}

FirstTwoApp.java

public class FirstTwoApps extends Activity 
{
    private class ButtonHandler implements View.OnClickListener
    {
	public void onClick(View v)
	{
		handleButtonClick();
	}	
    }
	
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);

        Button startAct = new Button(this);
        startAct.setText("Start Activity");
        
        // defining the layout in code instead of XML
    	LinearLayout layout = new LinearLayout(this);
    	layout.addView(startAct, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));

    	// activating the button
        startAct.setOnClickListener(new ButtonHandler());
        
       	// setting the view to our layout
        setContentView(layout);
    }
    
    private void handleButtonClick()
    {
    	startActivity(new Intent(this, SecondAct.class));
    }
}

The call to start the secondary activity occurs in the private method handleButtonClick.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="org.mathcs.twoapps"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".FirstTwoApps"
                  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="SecondAct">
	</activity>
    </application>
    <uses-sdk android:minSdkVersion="4" />
</manifest>

When you launch the first activity it will display a single button. When you click it, the secpnd activity is launched, displaying an input box. You return to the first activity by clicking BACK. Explore the following:

One activity calls another and expects a result

To start a secondary activity and expect a return notification when it is done, the parent activity calls:

 	startActivityForResult(Intent intent, int requestCode);

similar to before, but with a second integer parameter identifying the call. The result will come back through the inherited method in the parent activity:

 	onActivityResult(int requestCode, int resultCode, Intent data)

Just before the secondary activity exits, it should call setResult(int resultCode) or setResult(int resultCode, Intent data) to set the resultCode that the parent will pick up in onActivityResult as  well as any data that should be returned. The result code is usually either RESULT_CANCELED or RESULT_OK, but could be any custom value starting at RESULT_FIRST_USER. The data Intent can be used to return any additional data it wants, and even additional information such as who can manipulate that data. We will get into details about an Intent soon, for now we will just use it to transport data. All of this information, the resultCode and the data appears automatically back in the parent's onActivityResult, along with the requestCode originally supplied so that you could distinguish which child activity is returning data to you in case you start more than one activity. Note that when the child activity terminates because of the user pressing th BACK button, the resultCode is automatically set to RESULT_CANCELED. This all may be a little confusing so here is the short summary.

Example: We'll use the previous activities FirstTwoApp and SecondAct. The second activity gets an "Okay" button in addition to the text field. If the user clicks on that button, the secondary activity should set its return data to the string entered, its return code to "okay", and close. The parent activity should display what happened in its child activity via a toast, i.e. it should receive the text the user entered in the child activity. Note that the manifest file already includes a reference to both activities, so we are fine there (otherwise we'd have to add an activity tag to the manifest).

First let's change the method handleButtonClick in FirstTwoApp to call startActivityForResult instead of startActivity. We also override onActivityResult to potentially receive and interpret any results:

 	private void handleButtonClick()
	{
		startActivityForResult(new Intent(this, SecondAct.class), INPUT_REQUEST);
	}

	protected void onActivityResult(int requestCode, int resultCode, Intent data)
	{
		super.onActivityResult(requestCode, resultCode, data); 
		if (resultCode == Activity.RESULT_CANCELED) 
			Toast.makeText(this, "activity canceled", Toast.LENGTH_SHORT).show(); 
		else if (resultCode == Activity.RESULT_OK) 
			Toast.makeText(this, "activity ok", Toast.LENGTH_SHORT).show(); 
	}

Here INPUT_REQUEST is an integer constant that we define as 0, or really any value (code not listed). This will already work without any changes to SecondAct - try it. When the child activity is done (via the BACK button) the parent shows the answer as "canceled".

Next we add an Okay button with appropriate handler to the SecondAct class, where we set the return result to OK and close the activity when the button is clicked:

public class SecondAct extends Activity 
{
    private EditText input = null;
    private Button okay = null;
	
    private class ButtonOkayHandler implements View.OnClickListener
    {
	public void onClick(View v)
	{
		handleOkayButton();	
	}	
    }
	
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        
        // initializing the fields
        input = new EditText(this);
        input.setText("Enter text here ...");
        okay = new Button(this);
        okay.setText("Okay");
     
        // defining the layout in code instead of XML
    	LinearLayout layout = new LinearLayout(this);
    	layout.setOrientation(LinearLayout.VERTICAL);
    	layout.addView(input, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
    	layout.addView(okay, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
    
    	// activating the button
        okay.setOnClickListener(new ButtonOkayHandler());

    	// setting the view to our layout
    	setContentView(layout);
    }
    
    private void handleOkayButton()
    {
    	setResult(Activity.RESULT_OK);
    	finish();
    }
}

This would pass different return codes to the parent (try it) but no extra data. So, we finally use an intent to transfer data from the text field of the second activity to the parent activity and display it in a toast (unless the secondary activity was canceled). In the SecondAct class we create a new Intent, set its "extra" fields to whatever was entered in the input text field (as a String!), and add that intent to the returned results via the second version of setResult.

Note that an Intent has many "putExtra" methods, one for every basic data type and arrays, and for each there is a corresponding "getXXXExtra". Specifically we change handleOkayButton in SecondAct to:

 	private void handleOkayButton()
	{
	    	Intent data = new Intent();
    		data.putExtra("input", input.getText().toString());
    	
	    	setResult(Activity.RESULT_OK, data);
    		finish();
	}

And on the other side in FirstTwoApp we change handleButton to:

	protected void onActivityResult(int requestCode, int resultCode, Intent data)
	{
		super.onActivityResult(requestCode, resultCode, data);
		if (resultCode == Activity.RESULT_CANCELED)
			Toast.makeText(this, "activity canceled", Toast.LENGTH_SHORT).show();
		else if (resultCode == Activity.RESULT_OK)
		{
			String input = data.getStringExtra("input");
			Toast.makeText(this, "input = " + input, Toast.LENGTH_SHORT).show();
		}
	}

An activity broadcasts a request to the OS to start a suitable activity

The third way to start a child activity is to define a suitable Intent object and broadcast it system-wide so that it can be picked up by whatever activity is capable of handling that intent. We can for example create an intent with a phone number ad a request to dial it, broadcast it and let the system figure out who exactly will handle the dialing. This idea, actually, is one of higlights For that to work we need to request permission in the manifest to use such system activities via a "uses-permission" entry.

We will pick this up in the next section!