Ilias Tsagklis

About Ilias Tsagklis

Ilias Tsagklis is a senior software engineer working in the telecom domain. He is an applications developer in a wide variety of applications/services. Ilias is co-founder and Executive Editor at Java Code Geeks.

Android Full App, Part 5: Launching new activities with intents

This is the fifth part of the “Android Full Application Tutorial” series. The complete application aims to provide an easy way of performing movies/actors searching over the internet. In the first part of the series (“Main Activity UI”), we created the Eclipse project and set up a basic interface for the main activity of the application. In the second part (“Using the HTTP API”), we used the Apache HTTP client library in order to consume an external HTTP API and integrate the API’s searching capabilities into our application. In the third part (“Parsing the XML response”) we saw how to parse the XML response using Android’s built-in XML parsing capabilities. In the fourth part (“Performing the API request asynchronously from the main activity”), we tied together the HTTP retriever and XML parser services in order to perform the API search request from our application’s main activity. The request was executed asynchronously in a background thread in order to avoid blocking the main UI thread. In this part, we will see how to launch a new Activity and how to transfer data from one Activity to another.

Google has published the Activity and Task Design Guidelines which describe the core principles of the Android application framework, from a high-level, user-centric perspective useful to interaction and application designers as well as application developers. You should take a look at the guidelines, but for now we should just mention a few things about Activities here:

  • Activities are the main building blocks of Android applications.
  • As the user moves through the user interface, they start activities one after the other.
  • Each activity has a life-cycle that is independent of the other activities in its application or task.
  • Android keeps a linear navigation history of activities the user has visited (activity stack).
  • Data can be transferred among activities using the Intent class.

Up until now in our tutorial, we performed the movies search inside the main activity and just notified the users about the results using Toasts. We are now going to take this a step further and use a new activity in which the results will be presented as a list. For this reason, the new activity will actually extend the ListActivity Android class, which is a special kind of activity that displays a list of items by binding to a data source such as an array or Cursor.

The new activity will be launched from the main one by means of an Intent. We will use the Intent constructor that involves a Context and a target Class. From the reference documentation, we read that this constructor “provides a convenient way to create an intent that is intended to execute a hard-coded class name, rather than relying on the system to find an appropriate class for you”. Since we know which activity we wish to handle the Intent, this is the suitable one.

In order to transfer data among the two activities, we will use one of the overloaded putExtra methods that Intent class offers. The transferable data have to be Serializable and since the ArrayList is by default, we also have to mark our model classes (Movie/Person/Image) with the mark interface. Finally, we use the startActivity method to launch the known new activity.

So, inside the “PerformMovieSearchTask” class, we use the following code in order to start the new activity that will handle the search results presentation:

...
Intent intent = 
 new Intent(MovieSearchAppActivity.this, MoviesListActivity.class);
intent.putExtra("movies", result);
startActivity(intent);
...

Do not forget to mark the model classes as serializable:

...
public class Movie implements Serializable
...
public class Person implements Serializable
...
public class Image implements Serializable
...

In order to have the system find the new activity, we have to declare it in our “AndroidManifest.xml” file. This is done very easily by adding the following line inside the “application” element:

...
<activity android:name=".MoviesListActivity" />
...

The new ListActivity will retrieve the data inside its onCreate method. In order to do so, we first take a reference of the intent that launched the activity by using the getIntent method that returns the original intent. From that, we retrieve the extended data via the getSerializableExtra method, where we use the name of the item that was previously added with the putExtra method. Since the method returns a Serializable object, we perform the necessary casting.

In order to present the search results in a list we need to use an ArrayAdapter, i.e. a ListAdapter that manages a ListView backed by an array of arbitrary objects. The ArrayAdapter will be initialized with the movies list retrieved from the original activity. We then use the setListAdapter method to associate our ListActivity with the ArrayAdapter.

We wish to handle click events on each of the list’s items and more specifically to open the browser in the movie’s IMDB page. Thus, we override the onListItemClick method, which is called when an item in the list is selected. Inside that method, we retrieve the Movie object that is associated with the list item using the ArrayAdapter, find the IMDB ID and then construct the IMDB page URL, which consists of the following:

Base URL: http://m.imdb.com/title/

and the corresponding movie ID. Then, we create a new Intent with its action set to ACTION_VIEW. This is the Activity Action which in general displays data to the user. In case of a URL, the default browser will be launched and redirected to the target page. The new activity which handles the intent is launched with the usual method startActivity.

Let’s see the code for our “MoviesListActivity” class:

package com.javacodegeeks.android.apps.moviesearchapp;

import java.util.ArrayList;

import android.app.ListActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

import com.javacodegeeks.android.apps.moviesearchapp.model.Movie;

public class MoviesListActivity extends ListActivity {
    
    private static final String IMDB_BASE_URL = "http://m.imdb.com/title/";
    
    private ArrayList<Movie> moviesList;
    private ArrayAdapter<Movie> moviesAdapter;
    
    @SuppressWarnings("unchecked")
    @Override
    public void onCreate(Bundle savedInstanceState) {
        
        super.onCreate(savedInstanceState);
        setContentView(R.layout.movies_layout);
        
        moviesList = (ArrayList<Movie>) getIntent().getSerializableExtra("movies");
        
        moviesAdapter = new ArrayAdapter<Movie>(this, android.R.layout.simple_list_item_1, moviesList);
        
        setListAdapter(moviesAdapter);
        
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        
        super.onListItemClick(l, v, position, id);
        Movie movie = moviesAdapter.getItem(position);
        
        String imdbId = movie.imdbId;
        if (imdbId==null || imdbId.length()==0) {
            longToast(getString(R.string.no_imdb_id_found));
            return;
        }
        
        String imdbUrl = IMDB_BASE_URL + movie.imdbId;
        
        Intent imdbIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(imdbUrl));                
        startActivity(imdbIntent);
        
    }
    
    public void longToast(CharSequence message) {
        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
    }
    
}

Note that the ArrayAdapter uses the toString method of each of the encapsulated objects, so we should override the method and provide a more meaningful representation:

@Override
public String toString() {
 StringBuilder builder = new StringBuilder();
 builder.append("Movie [name=");
 builder.append(name);
 builder.append("]");
 return builder.toString();
}

The layout file that describes the content view for our activity needs to have a ListView declaration with the id “@+id/android:list”. Moreover, we provide a TextView with id “@+id/android:empty” that will appear in the case that the list includes no items. These views are included inside a LinearLayout, which is a layout that arranges its children in a single column or a single row. The XML file resides in the “res/layout” folder, it is named “movies_layout.xml” and has the following contents:

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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   >
   
 <ListView
     android:id="@+id/android:list"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     />
 <TextView
     android:id="@+id/android:empty"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:text="@string/no_movies_found"/>
    
</LinearLayout>

Now, run the Eclipse configuration, provide a query string, perform a Movie search and wait until the results are retrieved.

Upon retrieval, the “MoviesListActivity” class will be invoked and the results will be displayed in a list (invoking the toString method, as mentioned above).

You can scroll down (this is handled automatically by Android) and click the movie that you wish to find details for. This will trigger an event that will open the device’s browser and point to the movie’s IMDB page:

Great! Movie information straight to our smartphone! You can download here the Eclipse project created so far.

Related Articles :

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

5 Responses to "Android Full App, Part 5: Launching new activities with intents"

  1. Mohamed Nabil Ahmed says:

    My Eclipse wants me to remove the Override notations in the main activity and the app crash without it i guess :S what should i do ?!

  2. Mohamed Nabil Ahmed says:

    these are the error messages i am getting 

    01-26 15:50:50.535: E/AndroidRuntime(336): FATAL EXCEPTION: AsyncTask #101-26 15:50:50.535: E/AndroidRuntime(336): java.lang.RuntimeException: An error occured while executing doInBackground()01-26 15:50:50.535: E/AndroidRuntime(336): at android.os.AsyncTask$3.done(AsyncTask.java:200)01-26 15:50:50.535: E/AndroidRuntime(336): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274)01-26 15:50:50.535: E/AndroidRuntime(336): at java.util.concurrent.FutureTask.setException(FutureTask.java:125)01-26 15:50:50.535: E/AndroidRuntime(336): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308)01-26 15:50:50.535: E/AndroidRuntime(336): at java.util.concurrent.FutureTask.run(FutureTask.java:138)01-26 15:50:50.535: E/AndroidRuntime(336): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)01-26 15:50:50.535: E/AndroidRuntime(336): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)01-26 15:50:50.535: E/AndroidRuntime(336): at java.lang.Thread.run(Thread.java:1019)01-26 15:50:50.535: E/AndroidRuntime(336): Caused by: java.lang.IllegalArgumentException: Illegal character in path at index 50: http://api.themoviedb.org/2.1/Movie.search/en/xml//Inception01-26 15:50:50.535: E/AndroidRuntime(336): at java.net.URI.create(URI.java:776)01-26 15:50:50.535: E/AndroidRuntime(336): at org.apache.http.client.methods.HttpGet.(HttpGet.java:75)01-26 15:50:50.535: E/AndroidRuntime(336): at com.moviesearchapp.services.HttpRetriever.retrieve(HttpRetriever.java:22)01-26 15:50:50.535: E/AndroidRuntime(336): at com.moviesearchapp.services.MovieSeeker.retrieveMoviesList(MovieSeeker.java:20)01-26 15:50:50.535: E/AndroidRuntime(336): at com.moviesearchapp.services.MovieSeeker.find(MovieSeeker.java:11)01-26 15:50:50.535: E/AndroidRuntime(336): at com.moviesearchapp.MovieSearchActivity$PerformMovieSearchTask.doInBackground(MovieSearchActivity.java:155)01-26 15:50:50.535: E/AndroidRuntime(336): at com.moviesearchapp.MovieSearchActivity$PerformMovieSearchTask.doInBackground(MovieSearchActivity.java:1)01-26 15:50:50.535: E/AndroidRuntime(336): at android.os.AsyncTask$2.call(AsyncTask.java:185)01-26 15:50:50.535: E/AndroidRuntime(336): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)01-26 15:50:50.535: E/AndroidRuntime(336): … 4 more01-26 15:50:54.076: E/WindowManager(336): Activity com.moviesearchapp.MovieSearchActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@4052c1c0 that was originally added here01-26 15:50:54.076: E/WindowManager(336): android.view.WindowLeaked: Activity com.moviesearchapp.MovieSearchActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@4052c1c0 that was originally added here01-26 15:50:54.076: E/WindowManager(336): at android.view.ViewRoot.(ViewRoot.java:258)01-26 15:50:54.076: E/WindowManager(336): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:148)01-26 15:50:54.076: E/WindowManager(336): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)01-26 15:50:54.076: E/WindowManager(336): at android.view.Window$LocalWindowManager.addView(Window.java:424)01-26 15:50:54.076: E/WindowManager(336): at android.app.Dialog.show(Dialog.java:241)01-26 15:50:54.076: E/WindowManager(336): at android.app.ProgressDialog.show(ProgressDialog.java:107)01-26 15:50:54.076: E/WindowManager(336): at android.app.ProgressDialog.show(ProgressDialog.java:95)01-26 15:50:54.076: E/WindowManager(336): at com.moviesearchapp.MovieSearchActivity.performSearch(MovieSearchActivity.java:121)01-26 15:50:54.076: E/WindowManager(336): at com.moviesearchapp.MovieSearchActivity.access$6(MovieSearchActivity.java:119)01-26 15:50:54.076: E/WindowManager(336): at com.moviesearchapp.MovieSearchActivity$2.onClick(MovieSearchActivity.java:59)01-26 15:50:54.076: E/WindowManager(336): at android.view.View.performClick(View.java:2485)01-26 15:50:54.076: E/WindowManager(336): at android.view.View$PerformClick.run(View.java:9080)01-26 15:50:54.076: E/WindowManager(336): at android.os.Handler.handleCallback(Handler.java:587)01-26 15:50:54.076: E/WindowManager(336): at android.os.Handler.dispatchMessage(Handler.java:92)01-26 15:50:54.076: E/WindowManager(336): at android.os.Looper.loop(Looper.java:123)01-26 15:50:54.076: E/WindowManager(336): at android.app.ActivityThread.main(ActivityThread.java:3683)01-26 15:50:54.076: E/WindowManager(336): at java.lang.reflect.Method.invokeNative(Native Method)01-26 15:50:54.076: E/WindowManager(336): at java.lang.reflect.Method.invoke(Method.java:507)01-26 15:50:54.076: E/WindowManager(336): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)01-26 15:50:54.076: E/WindowManager(336): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)01-26 15:50:54.076: E/WindowManager(336): at dalvik.system.NativeStart.main(Native Method)

  3. Sam Mathew says:

    works perfect..thank you guys soooooo much.

  4. Jack Edkins says:

    Can you explain the use of SuppressWarnings(“unchecked”) in MoviesListActivity class?

  5. robi says:

    Good explanation……..Have a look at………..http://androidtutorialsrkt.blogspot.in/

Leave a Reply


1 − = zero



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close