dcsimg

Hands-on: Multiple Activities and Data Persistence in Android

Building on our Android-based TicketResponder applicatoin, we add a new screen and data persistence.

Motivation

This article builds upon the work of our previous articles in building a real-world data collection application for Android. In this article we add an additional user interface screen to capture necessary configuration data which will be used by the application in a subsequent step. The code samples in this article demonstrate:

  • The creation of a new user interface screen (Activity) with multiple controls
  • Navigation between two different Activity classes
  • Creating and using a basic Intent
  • The retrieval/storage of configuration data via the program

These are some necessary steps along the path to building the full TicketResponder application.

Persistence Options

Before jumping directly into the code, I thought it might be helpful to take a quick tour of data persistence options in Android. Storing data in Android can be accomplished through a number of mechanisms. Which technique is right for your application depends on a number of factors including:

  • How much data is to be stored
  • How frequently the data is accessed
  • The kind of manipulations you need to perform on/with the data
  • Which applications need access to the data

If your application needs to store some small data elements such as strings or integer values, you might first consider SharedPreferences. SharedPreferences work in a manner similar to an “ini” file with key/value pairs often found on other desktop and server platforms. We will use this technique later in this article as we add a Settings screen to our application to store username, password, and a server internet address, or URL.

If you have larger amounts of data to manage, it is also possible to store data in text or binary files, much like you would use in a server or desktop application. The familiar java.io package is available for manipulating files including a variety of Stream-based classes familiar to Java programmers. One item to note with respect to working with files is that you must use a non-standard means of opening/creating the file to properly navigate the process/security context within the Linux-based Android environment. Your files should be referenced by way of your application’s context, as shown below:

FileOutputStream fos = this.context.openFileOutput("somefilename.xml", Context.MODE_PRIVATE);
fos.write("\n".getBytes());
fos.write("somedatagoeshere".getBytes());
fos.close();

The example above opens a file for output within its own directory. Once the file is opened, we can access it via a FileOutputStream or other type of file I/O class. Once the file is opened, we write two lines of text to the file and then close it the file. This technique of working with a file-based persistence method would be appropriate for storing a local-cache of application specific files for applications such as RSS readers, virus definition files, or perhaps a global high-scorers file downloaded from a gaming server on the Internet.

For more sophisticated data storage requirements, your application can leverage the powerful and flexible SQLite database. SQLite provides a significant subset of the popular Structured Query Langauge (SQL) found in more powerful (and costly) database technologies. Those familiar with relational databases such as MySQL, Microsoft SQL Server, Oracle and others will find that much of the terminology to be similar to what you are accustomed to — you work with databases, tables, fields/columns, etc. There are some differences with respect to how certain data types are treated but for a mobile platform the opportunity to have a real SQL database is just awesome. I’ve spent more hours than I care to think about navigating the RecordStores of the Research in Motion BlackBerry platform or the limited database structures of Palm OS. I’ve even written ISAM libraries for a mobile device but I’d rather not think about that right now. SQLite has become the defacto mobile database as it is now used in Android, iPhone, Palm webOS, and is the underlying data storage technology for HTML5 databases powered by WebKit.

Using an SQLite database is a good idea when you have larger data sources that you need to search or sort. Examples that come to mind include a list of client-specific data such as inventory data or a product list. An application can manipulate an SQLite database for its own purposes, however the application must know the schema of the data and in general, using an SQL database is a task aimed at developers building a closed application, meaning no other application knows or cares about your data per se. An SQLite database is “private” to the application which utilizes it and other applications cannot readily attach to it nor manipulate its data.

If your application intends to share data with a variety of other applications, you will want to consider a creating a ContentProvider. A ContentProvider is the preferred technique for sharing data amongst multiple applications. For example, the Contacts data (i.e. Address Book records) is accessed via a ContentProvider with standard queries.

Lastly, we would be remiss to omit the “network” as a place to store data. In fact, a mobile device with powerful Internet connectivity such as Android can leverage the storage facilities of a datacenter-hosted server on an as-needed basis. Over time we will see more of this kind of application — mobile users leveraging cloud services, including storage. In fact, this paradigm is so prevalent on the web today that it is often over-looked.

For example, Amazon has created a service called Amazon Simple Storage Service where they provide a simple API for storing and retrieving data. Amazon (and other providers) take care of the dirty work of running a massively web-scaled datacenter, allowing the developer to focus on application functionality and not get bogged down with database server maintenance. If a given application is wildly successful and needs more storage, they just crank up the volume with the storage provider — no need to go out and buy new hardware and software to support the load. As a developer first and a reluctant system admin second, I can appreciate the allure of such arrangements. The challenge of course is to make sure that your business model justifies the purchase of more storage. Remember, if you’re losing money, you cannot make it up in volume!

We will examine both SQLite, ContentProviders, and even Cloud connectivity in future Android articles. For now, we’re going to focus on expanding our TicketResponder application by adding a second Activity. This Activity presents a user interface for the user to manipulate configuration data along with the code for saving and restoring the configuration data. These data elements are necessary for the application to access a remote server. Let’s look at the bigger picture to better understand what our application must accomplish.

The Requirements

If you recall from a prior article, our application collects a couple of data elements from the user. The application takes as input a ticket number (never mind for now where that came from — we’ll get there eventually!) and a desired status such as “In Progress”, “Waiting on Client”, “Closed”, etc. as shown in the image below.

enterticket.png

Once the user selects the “Update Ticket” button it is time to package the data into a server-friendly format and then transmit it to the server. We will cover the actual communications code in a future article — for now we need to manage to navigate between our two Activity classes and learn how to store and retrieve the data elements, which in our case consists of three string values: server address, username, and password.

The Intent and the Activity

Recall that an Activity is essentially a user interface screen. The user interface elements are defined in a layout file which is an xml file. This file is created either “by hand” or through the use of WSYWIG tools in the Android Developer Tools plugin for Eclipse. I typically copy and paste from one file to the next and then tweak the new one as desired. For more information on creating layout resources, please see prior article. Once we’ve got the user interface defined for our second Activity (see settings.xml in the source code for layout details), we need to know how to launch this second Activity. The image below depicts our new settings screen user interface.

settingsscreen1.png

The most straight-forward means to launch one Activity from another is to use an explicit Intent — that is, an Intent which explicitly identifies another Activity class. Here is a snippet of code taken from TicketResponder.java which implements this new functionality.

final ImageView settingsImage = (ImageView) findViewById(R.id.ImageSettings);
settingsImage.setOnClickListener(new ImageView.OnClickListener() {
		public void onClick(View v) {
			try {
				Intent in = new Intent(v.getContext(),TRSettings.class);
				startActivity(in);
			}
			catch (Exception e){
				Log.i(tag,"Failed to launch Settings Activity " + e.getMessage());
			}
		}
}
);

We must also define in our application what the user interaction must be to perform this operation. Note in the prior screen shot the “gear-like” icon in the lower left portion of the screen. When this icon is clicked, we run the above code to launch the TRSettings Activity. This code is an anonymous onClick handler for an ImageView widget. It simply creates a new Intent which identifies the TRSettings class (defined in TRSettings.java) and then starts that Activity by passing the Intent to the startActivity() method. Let’s have a closer look at the functionality of TRSettings Activity.

Shared Preferences Example

The purpose of the TRSettings class is to:

  1. Retrieve three data elements from persistent storage
  2. Display those three data elements: user name, password, server address in EditText widgets
  3. Handle a Button press by capturing data from the EditText widgets
  4. Validate that the user entered appropriate data. For our purposes, we just make sure the fields are not empty
  5. Store the new values to persistent storage
  6. Close this Activity and return to the prior screen

We implement our data persistence with the previously mentioned SharedPreferences class. We simply create an instance of our SharedPreferences with a call to the getSharedPreferences() method passing in a name for the preferences we want to work with and an access mode. In this case we use our application name of “TicketResponder” and the MODE_PRIVATE flag. These preferences are private to our own application. Remember, SharedPreferences are appropriate for simple storage such as our example of username/server address kinds of data elements. Other uses might include saving state from one invocation of our application to another — this is helpful for pausing a game during an incoming phone call. You certainly wouldn’t want your user to have to go back to level 1 just because the phone rang! This might also look like remembering the unique identifier of a contact record or inventory part so the application can re-load that data the next time the user starts the application. Providing this kind of feature goes a long way in making your application more usable and building loyal fans.

We next create an instance of the Editor class with a call to the edit() method of the SharedPreferences class instance. Data is retrieved with a call to the getString() method. Data is stored with calls to the putString() method. When all manipulation of the data is complete and we’re ready to go, we call the commit() method of the Editor instance. This performs the actual persistence of our data to the SharedPreferences instance.

The final thing we do is call the finish() method which causes our Activity to close. Hitting the “back” button (or escape while running in the Android Emulator) causes the Activity to close without saving the configuration data. This is in essence a “cancel” operation. Here is the code for the Activity as found in TRSettings.java

package com.msi.linuxmagazine.ticketing;
import android.app.Activity;
import android.content.Context;
import android.view.View;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.util.Log;
import android.app.AlertDialog;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
public class TRSettings extends Activity {
	final String tag = "TRSettings";
	private SharedPreferences prefs = null;
	private Editor editor = null;
	public static final String SERVERADDRESS = "serveraddress";
	public static final String USERNAME = "username";
	public static final String PASSWORD = "password";
	public static final String PREFERENCESNAME = "TicketResponder";
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.settings);
        final EditText serverAddress = (EditText) findViewById(R.id.serverAddress);
        final EditText userName = (EditText) findViewById(R.id.userName);
        final EditText password = (EditText) findViewById(R.id.password);
        final Button saveButton = (Button) findViewById(R.id.saveButton);
        prefs = this.getSharedPreferences("TicketResponder", Context.MODE_PRIVATE);
        editor = prefs.edit();
        serverAddress.setText(prefs.getString(TRSettings.SERVERADDRESS, "http://servernamegoeshere/"));
        userName.setText(prefs.getString(TRSettings.USERNAME, "user"));
        password.setText(prefs.getString(TRSettings.PASSWORD,"password"));
        saveButton.setOnClickListener(new Button.OnClickListener() {
            public void onClick(View v) {
                try {
                	String server = serverAddress.getText().toString();
                	String user = userName.getText().toString();
                	String pword = password.getText().toString();
                	Log.i(tag,"server address is [" + server + "]");
                	Log.i(tag,"username is [" + user + "]");
                	Log.i(tag,"password address is [" + pword + "]");
                	// let's do some basic input filtering
                	if (user.trim().length() == 0 || server.trim().length() == 0 || pword.trim().length() == 0)
                	{
                		AlertDialog.Builder adb = new AlertDialog.Builder(v.getContext());
                        AlertDialog ad = adb.create();
                        ad.setMessage("All fields are required.");
                        ad.show();
                        return;
                	}
                	// let's store in a shared preference
                	editor.putString(TRSettings.SERVERADDRESS,server);
                	editor.putString(TRSettings.USERNAME,user);
                	editor.putString(TRSettings.PASSWORD,pword);
                	editor.commit();
                	finish();
                } catch (Exception e) {
                    Log.i(tag, "Error occurred [" + e.getMessage() + "]");
                }
            }
        });
    }
}

Updating AndroidManifest.xml

Running the code is easy — just launch the Android Emulator (or real device) through the Run menu in Eclipse as described in the previous article, which covered application debugging. If you do this, you’ll get an error in your application complaining that the application could not continue because the Activity doesn’t exist. Huh? After all that we cannot run our application to see the new Activity? What could it be? If you look in the Log you’ll see a message that looks like this:

08-31 23:05:11.130: INFO/TicketResponder(770): Failed to launch Settings Activity Unable to find explicit activity class {com.msi.linuxmagazine.ticketing/com.msi.linuxmagazine.ticketing.TRSettings}; have you declared this activity in your AndroidManifest.xml?

The problem is that we didn’t tell Android that we had an additional, runnable Activity class. In order to do this we need to modify our AndroidManifest.xml file to let Android know about our new Activity. Fortunately, this is a very simple thing to do — all we have to do is add a single line to our AndroidManifset.xml file: . Now our complete AndroidManifset.xml file has two Activity classes defined:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      android:versionCode="1"
      android:versionName="1.0" package="com.msi.linuxmagazine.ticketing">
    <application android:label="@string/app_name" android:icon="@drawable/network_services">
        <activity android:name=".TicketResponder"
                  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="TRSettings"></activity>
</application>
    <uses-sdk android:minSdkVersion="3" />
</manifest>

Our TicketResponder application now has two Activity classes that we can navigate between and the ability to store small amounts of data. The full source code is available on the Linux Magazine mobile source website in both a browsable & SVN checkout format and as a zip file. Coming up we’re going to look at adding some Location/GPS data to our application.

Comments on "Hands-on: Multiple Activities and Data Persistence in Android"

Here is a great Blog You may Obtain Fascinating that we encourage you to visit.

Here is a good Blog You may Uncover Fascinating that we encourage you to visit.

The time to read or stop by the material or web pages we have linked to beneath.

Here are some of the internet sites we advise for our visitors.

Usually posts some really interesting stuff like this. If you are new to this site.

Spot on with this write-up, I truly believe this website needs a great deal more attention. I’ll probably be back again to see more, thanks for the info!:)

That could be the finish of this article. Here you will come across some web sites that we assume you will enjoy, just click the hyperlinks.

We came across a cool internet site which you may possibly enjoy. Take a search in the event you want.

Very informative blog article.Really looking forward to read more. Want more.

Check beneath, are some absolutely unrelated web sites to ours, nonetheless, they are most trustworthy sources that we use.

Usually posts some very exciting stuff like this. If you are new to this site.

It is really a great and helpful piece of information. I’m glad that you shared this helpful information with us. Please keep us up to date like this. Thanks for sharing.

Yes! Finally someone writes about krogerfeedback.

The time to read or go to the content or web sites we have linked to beneath.

Usually posts some really intriguing stuff like this. If you?re new to this site.

Here are a few of the web pages we advise for our visitors.

Wonderful story, reckoned we could combine some unrelated data, nonetheless definitely worth taking a search, whoa did 1 understand about Mid East has got more problerms at the same time.

We came across a cool web site that you just may possibly love. Take a search in the event you want.

The information and facts talked about inside the post are a few of the ideal accessible.

That will be the end of this post. Here you will find some internet sites that we think you will enjoy, just click the hyperlinks.

Although web-sites we backlink to below are considerably not connected to ours, we feel they’re essentially worth a go by, so possess a look.

What’s Going down i am new to this, I stumbled upon this I have found It absolutely useful and it has helped me out loads. I am hoping to contribute & aid other customers like its helped me. Great job.

That would be the end of this report. Here you?ll discover some web pages that we think you will enjoy, just click the links.

Just beneath, are many totally not related sites to ours, even so, they are surely really worth going over.

Wow, this article is nice, my sister is analyzing these sorts of things, so I will inform her.

Take a look at my web site … MarcoPTurvey

Although websites we backlink to beneath are considerably not connected to ours, we really feel they may be basically worth a go by means of, so have a look.

Wonderful story, reckoned we could combine a few unrelated information, nonetheless really really worth taking a look, whoa did one particular study about Mid East has got extra problerms also.

Every after in a while we pick blogs that we study. Listed below are the most recent sites that we pick out.

Aw, this was an exceptionally good post. Taking the
time and actual effort to make a superb article… but what can I say… I procrastinate a lot and don’t manage to get anything done.

The facts mentioned in the report are a number of the best accessible.

Very couple of internet websites that occur to become in depth below, from our point of view are undoubtedly nicely worth checking out.

We prefer to honor lots of other web web-sites on the internet, even if they aren?t linked to us, by linking to them. Underneath are some webpages really worth checking out.

Please visit the websites we comply with, like this one particular, because it represents our picks through the web.

Usually posts some very fascinating stuff like this. If you?re new to this site.

Just beneath, are various absolutely not connected web pages to ours, even so, they’re surely worth going over.

Please visit the web-sites we comply with, such as this 1, because it represents our picks through the web.

We came across a cool web site that you might love. Take a search if you want.

We prefer to honor lots of other online sites on the internet, even though they aren?t linked to us, by linking to them. Beneath are some webpages worth checking out.

You possess made some really good points there. I checked on the web to find out more in regards to the issue and located many people
goes together with your thoughts about this site.

Also visit my blog post; SamualZColao

Always a major fan of linking to bloggers that I adore but really don’t get a good deal of link love from.

Here is a good Weblog You may Find Intriguing that we encourage you to visit.

Good day! Do you know if they make any plugins to assist with SEO? I’m trying to get my blog to rank for some targeted keywords but I’m not seeing very good gains. If you know of any please share. Thanks!

Below you will find the link to some websites that we believe you must visit.

Patrick Heck, el nuevo director del SREL, ha entregado una lista de teléfonos a la fiscalía que investiga las irregularidades del servicio en la que aparece uno de Lébedev, el empresario ruso enfrentado a Vladímir Putin. Entre otros números entregados por el SREL a la fiscalía, hay un número atribuido a un tal Lébedev, pero no tenemos más información de la identidad de esta persona”, responde una fuente oficial del SREL. Aseguró que fue el servicio británico, a través de un tal Erskine, el que pidió la colaboración del SREL.

We came across a cool internet site that you simply may appreciate. Take a look when you want.

Here are some hyperlinks to websites that we link to simply because we think they may be worth visiting.

En el supuesto de que haya que llevarse su aparato Ariston averiado, disponemos de talleres especializados y preparados para llevar a cabo una reparacion exhaustiva y precisa del mismo.

One of our guests not too long ago encouraged the following website.

Leave a Reply