News:
- In an internet driven environment, it is imperative for a company to keep its product or business to the forefront of potential customers' minds. The ...
- A successful enterprise is all about constantly reinventing ways to work more efficiently. In today’s techno age, this translates to testing new too...
- The IT industry plays a pivotal role in providing application development solutions and custom software development to a wide range of industries, i...
- Royal Victorian Eye and Ear Hospital, 2010 Atcomm has been contracted to implement a complex IOP glaucoma management tool which will be distribut...
- We are pleased to announce another implementation of a CMS system for a large Melbourne based fitness center - Star Plate Studio. Atcomm has depl...
Thursday, 29 July 2010 01:12
Compiled and Noncompiled Android Resources
Android supports a number of other resources in addition to string resources and layout resources. The general process of creating and using these various resources is similar.
However, it is worthwhile to consider some differences. Android supports all these resources through XML files, bitmap files for images, and raw files (examples of which could include audio and video). Within the set of XML files, you’ll find two types: one gets compiled into binary format, and the other gets copied as is. The examples you have seen so far—the
string-resource XML files and the layout-resource XML files—get compiled into binary format before becoming part of the installable package. You can also place raw XML files in the /res/ xml/ subdirectory to have them compiled into binary format. But if you place files, including XML files, in the /res/raw/ directory instead, they don’t get compiled into binary format. You
must use explicit stream-based APIs to read these files.
As we mentioned in Table 2-1 in the previous chapter, resource files are housed in various subdirectories based on their type. Here are some important subdirectories in the /res folder and the types of resources they host:
• anim: Compiled animation files
• drawable: Bitmaps
• layout: UI/view definitions
• values: Arrays, colors, dimensions, strings, and styles
• xml: Compiled arbitrary raw XML files
• raw: Noncompiled raw files
The resource compiler in the Android Asset Packaging Tool (AAPT) compiles all the resources except the raw resources and places them into the final .apk file. This file, which contains the Android application’s code and resources, correlates to Java’s .jar file (“apk” stands for “Android Package”). The .apk file is what gets installed onto the device. In addition to gathering
raw assets into a final compressed file, the AAPT also parses resource definitions into binary asset data.
Enumerating Key Android Resources
Now that we’ve been through the basics of resources, we’ll enumerate some of the other key resources that Android supports, their XML representations, and the way they’re used in Java code. (You can use this section as a quick reference as you write resource files for each resource.) To start with, take a quick glance at the types of resources and what they are used
for .
Each of the resources specified in this table are further elaborated in the following sections with XML and Java code snippets.
Color resources
As you can do with string resources, you can use reference identifiers to indirectly reference colors. Doing this enables Android to localize colors and apply themes. Once you’ve defined and identified colors in resource files, you can access them in Java code through their IDs. Whereas string-resource IDs are available under the.R.string namespace, the
color IDs are available under the.R.color namespace. See Listing 3-3 for some examples of specifying color in an XML resource file.
Listing 3-3. XML Syntax for Defining Color Resources
#f00
#0000ff
#f0f0
#ffffff00
The entries in Listing 3-3 need to be in a file residing in the /res/values subdirectory. The name of the file is arbitrary, meaning the file name can be anything you choose. Listing 3-4 shows an example of using a color resource in Java code.
Listing 3-4. Color Resources in Java code
int mainBackGroundColor
= activity.getResources.getColor(R.color.main_back_ground_color);
Listing 3-5 shows how you would use a color resource in a view definition.
Listing 3-5. Using Colors in View Definitions
android:layout_height="wrap_content"
android:textColor="@color/ red"
android:text="Sample Text to Show Red Color"/>
More on String resources
We covered string resources briefly when we introduced resources at the beginning of this chapter. Let us revisit them in order to provide some more detail. We will show you how to define and use HTML strings, as well as how to substitute variables in string resources.
Let us start by showing how you can define normal strings, quoted strings, HTML strings, and substitutable strings in an XML resource file (see Listing 3-6).
Listing 3-6. XML Syntax for Defining String Resources
simple string
"quoted'string"
\"double quotes\"
hello %2$s java format string. %1$s again
Hello Slanted Android, You are bold.
This XML string-resource file needs to be in the /res/values subdirectory. The name of the file is arbitrary.
Notice how quoted strings need to be either escaped or placed in alternate quotes. The string definitions also allow standard Java string-formatting sequences.
Android also allows child XML elements such as , , and other simple text-formatting HTML within the node. You can use this compound HTML string to style the text before painting in a text view.
The Java examples in Listing 3-7 illustrate each usage.
Listing 3-7. Using String Resources in Java Code
//Read a simple string and set it in a text view
String simpleString = activity.getString(R.string.simple_string);
textView.setText(simpleString);
//Read a quoted string and set it in a text view
String quotedString = activity.getString(R.string.quoted_string);
textView.setText(quotedString);
//Read a double quoted string and set it in a text view
String doubleQuotedString = activity.getString(R.string.double_quoted_string);
textView.setText(doubleQuotedString);
//Read a Java format string
String javaFormatString = activity.getString(R.string.java_format_string);
//Convert the formatted string by passing in arguments
String substitutedString = String.format(javaFormatString, "Hello" , "Android");
//set the output in a text view
textView.setText(substitutedString);
//Read an html string from the resource and set it in a text view
String htmlTaggedString = activity.getString(R.string.tagged_string);
//Convert it to a text span so that it can be set in a text view
//android.text.Html class allows painting of "html" strings
//This is strictly an Android class and does not support all html tags
Spanned textSpan = android.text.Html.fromHtml(htmlTaggedString);
//Set it in a text view
textView.setText(textSpan);
Once you’ve defined the strings as resources, you can set them directly on a view such as TextView in the XML layout definition for that TextView. Listing 3-8 shows an example where an HTML string is set as the text content of a TextView.
Listing 3-8. Using String Resources in XML
android:layout_height="wrap_content"
android:textAlign="center"
android:text="@string/tagged_string"/>
TextView automatically realizes that this string is an HTML string, and honors its formatting accordingly. This is nice because you can quickly set attractive text in your views as part of the layout.
Dimension resources
Pixels, inches, and points are all examples of dimensions that can play a part in XML layouts or Java code. You can use these dimension resources to style and localize Android UIs without changing the source code.
Listing 3-9 shows how you can use dimension resources in XML.
Listing 3-9. XML Syntax for Defining Dimension Resources
1px
5dp
100sp
You could specify the dimensions in any of the following units:
• px: Pixels
• in: Inches
• mm: Millimeters
• pt: Points
• dp: Density-independent pixels based on a 160-dpi (pixel density per inch) screen
(dimensions adjust to screen density)
• sp: Scale-independent pixels (dimensions that allow for user sizing; helpful for use in
fonts)
In Java, you need to access your Resources object instance to retrieve a dimension. You can do this by calling getResources on an activity object (see Listing 3-10).
Listing 3-10. Using Dimension Resources in Java Code
float dimen = activity.getResources().getDimension(R.dimen.mysize_in_pixels);
As in Java, the resource reference for a dimension in XML uses dimen as opposed to the full word “dimension” (see Listing 3-11).
Listing 3-11. Using Dimension Resources in XML
android:layout_height="wrap_content"
android:textSize="@dimen/medium_size"/>
Image resources
Android generates resource IDs for image files placed in the /res/drawable subdirectory. The supported image types include .gif, .jpg, and .png. Each image file in this directory generates a unique ID from its base file name. If the image file name is sample_image.jpg, for example, then the resource ID generated will be R.drawable.sample_image.
You can then reference these images in other XML layout definitions, as shown in Listing 3-12.
Listing 3-12. Using Image Resources in XML
android:id="@+id/button1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Dial"
android:background="@drawable/sample_image"
/>
You can also retrieve the image programmatically and set it yourself in Java (see Listing 3-13).
Listing 3-13. Using Image Resources in Java
//Call getDrawable to get the image
BitmapDrawable d = activity.getResources().getDrawable(R.drawable.sample_image);
//You can use the drawable then to set the background
button.setBackgroundDrawable(d);
//or you can set the background directly from the Resource Id
button.setBackgroundResource(R.drawable.icon);
Android also supports a special type of image called a stretchable image. This is simply a kind of .png where parts of the image can be specified as static and stretchable. Android provides a tool called the Draw 9-patch tool to specify these regions. (You can read more about it at http://developer.android.com/guide/developing/tools/draw9patch.html.) Once the .png image is made available, you can use it as any other image. It comes in handy when used as background for buttons where the button has to stretch itself to accommodate the text.
Color-Drawable resources
In Android, an image is one type of a drawable resource. Android supports another drawable resource called a color-drawable resource; it’s essentially a colored rectangle.
To define one of these color rectangles, you define an XML element by the node name of drawable in any XML file in the /res/values subdirectory. Listing 3-14 shows a couple of colordrawable resource examples.
Listing 3-14. XML Syntax for Defining Color-Drawable Resources
#f00
#0000ff
#f0f0
Listings 3-15 and 3-16 show how you can use a color-drawable resource in Java and XML,
respectively.
Listing 3-15. Using Color-Drawable Resources in Java Code
// Get a drawable
ColorDrawble redDrawable =
(ColorDrawable)
activity.getResources().getDrawable(R.drawable.red_rectnagle);
//Set it as a background to a text view
textView.setBackground(redDrawable);
Listing 3-16. Using Color-Drawable Resources in XML Code
android:layout_height="wrap_content"
android:textAlign="center"
android:background="@drawable/red_rectangle"/>
To achieve the rounded corners in your drawable, you can use the currently undocumented
tag. However, this tag needs to reside in a file by itself in the /res/drawable
directory. Listing 3-17 shows how you can use the tag to define a rounded rectangle in
a file called /res/drawable/my_rounded_rectangle.xml.
Listing 3-17. Defining a Rounded Rectangle
android:right="10dp" android:bottom="10dp" />
You can then use this drawable resource as a background of the previous text-view
example:
// Get a drawable
GradientDrawable roundedRectangle =
(GradientDrawable)
activity.getResources().getDrawable(R.drawable.red_rectnagle);
//Set it as a background to a text view
textView.setBackground(roundedRectangle);
Working with Arbitrary XML Resource Files
Android also allows arbitrary XML files as resources. This approach offers three distinct advantages. First, it provides a quick way to reference these files based on their generated resource IDs. Second, the approach allows you to localize these resource XML files. Third, you can compile and store these XML files on the device efficiently.
XML files that need to be read in this fashion are stored under the /res/xml subdirectory. Here is an example XML file called /res/xml/test.xml:
<rootelem1>
<subelem1>
Hello World from an xml sub element
</subelem1>
</rootelem1>
As it does with other Android XML resource files, the AAPT will compile this XML file before placing it in the application package. You will need to use an instance of XmlPullParser if you want to parse these files. You can get an instance of the XmlPullParser implementation using this code from any context (including activity):
Resources res = activity.getResources();
XmlResourceParser xpp = res.getXml(R.xml.test);
The returned XmlResourceParser is an instance of XmlPullParser, and it also implements java.util.AttributeSet. Listing 3-18 shows a more complete code snippet that reads the test.xml file.
Listing 3-18. Using XmlPullParser
private String getEventsFromAnXMLFile(Activity activity)
throws XmlPullParserException, IOException
{
StringBuffer sb = new StringBuffer();
Resources res = activity.getResources();
XmlResourceParser xpp = res.getXml(R.xml.test);
xpp.next();
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT)
{
if(eventType == XmlPullParser.START_DOCUMENT)
{
sb.append("******Start document");
}
else if(eventType == XmlPullParser.START_TAG)
{
sb.append("\nStart tag "+xpp.getName());
}
else if(eventType == XmlPullParser.END_TAG)
{
sb.append("\nEnd tag "+xpp.getName());
}
else if(eventType == XmlPullParser.TEXT)
{
sb.append("\nText "+xpp.getText());
}
eventType = xpp.next();
}//eof-while
sb.append("\n******End document");
return sb.toString();
}//eof-function
In Listing 3-18, you can see how to get XmlPullParser, how to use XmlPullParser to navigate the XML elements in the XML document, and how to use additional methods of XmlPullParser to access the details of the XML elements. If you want to run this code, you must create an XML file as shown earlier and call the getEventsFromAnXMLFile function from any menu item or button click. It will return a string, which you can print out to the log stream using the Log.d debug method.
Working with Raw Resources
Android also allows raw files in addition to raw XML files. These raw resources, placed in /res/ raw, are arbitrary file resources such as audio, video, or text files that require localization or references through resource IDs. Unlike the raw XML files placed in /res/xml, these files are not compiled but moved to the application package as is. However, each file will have an identifier
generated in R.java. If you were to place a text file at /res/raw/test.txt, you would be able to read that file using the code in Listing 3-19.
Listing 3-19. Reading a Raw Resource
String getStringFromRawFile(Activity activity)
{
Resources r = activity.getResources();
InputStream is = r.openRawResource(R.raw.test);
String myText = convertStreamToString(is);
is.close();
return myText;
}
String convertStreamToString(InputStream is)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int i = is.read();
while (i != -1)
{
baos.write(i);
i = baos.read();
}
return baos.toString();
}
Working with Assets
Android offers one more directory where you can keep files to be included in the package:/assets. It’s at the same level as /res, meaning it’s not part of the /res subdirectories. The files in /assets do not generate IDs in R.java; you must specify the file path to read them. The file path is a relative path starting at /assets. You will use the AssetManager class to access these files:
//Note: Exceptions are not shown in the code
String getStringFromAssetFile(Activity activity)
{
AssetManager am = activity.getAssets();
InputStream is = am.open("test.txt");
String s = convertStreamToString(is);
is.close();
return s;
}
Reviewing the Resources Directory Structure
In summary, here is a quick look at the overall resources directory structure:
/res/values/strings.xml
/colors.xml
/dimens.xml
/attrs.xml
/styles.xml
/drawable/*.png
/*.jpg
/*.gif
/*.9.png
/anim/*.xml
/layout/*.xml
/raw/*.*
/xml/*.xml
/assets/*.*/*.*
Let us conclude this section on resources by quickly enumerating what you have learned about resources so far. You know the types of resources supported in Android and you know how to create these resources in XML files. You know how resource IDs are generated and how to use them in Java code. You also learned that resource ID generation is a convenient scheme
that simplifies resource usage in Android. Finally, you learned how to work with raw resources and assets. With that, we will now turn our attention to the section on content providers, where you will learn to work with data on Android.
However, it is worthwhile to consider some differences. Android supports all these resources through XML files, bitmap files for images, and raw files (examples of which could include audio and video). Within the set of XML files, you’ll find two types: one gets compiled into binary format, and the other gets copied as is. The examples you have seen so far—the
string-resource XML files and the layout-resource XML files—get compiled into binary format before becoming part of the installable package. You can also place raw XML files in the /res/ xml/ subdirectory to have them compiled into binary format. But if you place files, including XML files, in the /res/raw/ directory instead, they don’t get compiled into binary format. You
must use explicit stream-based APIs to read these files.
As we mentioned in Table 2-1 in the previous chapter, resource files are housed in various subdirectories based on their type. Here are some important subdirectories in the /res folder and the types of resources they host:
• anim: Compiled animation files
• drawable: Bitmaps
• layout: UI/view definitions
• values: Arrays, colors, dimensions, strings, and styles
• xml: Compiled arbitrary raw XML files
• raw: Noncompiled raw files
The resource compiler in the Android Asset Packaging Tool (AAPT) compiles all the resources except the raw resources and places them into the final .apk file. This file, which contains the Android application’s code and resources, correlates to Java’s .jar file (“apk” stands for “Android Package”). The .apk file is what gets installed onto the device. In addition to gathering
raw assets into a final compressed file, the AAPT also parses resource definitions into binary asset data.
Enumerating Key Android Resources
Now that we’ve been through the basics of resources, we’ll enumerate some of the other key resources that Android supports, their XML representations, and the way they’re used in Java code. (You can use this section as a quick reference as you write resource files for each resource.) To start with, take a quick glance at the types of resources and what they are used
for .
Each of the resources specified in this table are further elaborated in the following sections with XML and Java code snippets.
Color resources
As you can do with string resources, you can use reference identifiers to indirectly reference colors. Doing this enables Android to localize colors and apply themes. Once you’ve defined and identified colors in resource files, you can access them in Java code through their IDs. Whereas string-resource IDs are available under the
color IDs are available under the
Listing 3-3. XML Syntax for Defining Color Resources
The entries in Listing 3-3 need to be in a file residing in the /res/values subdirectory. The name of the file is arbitrary, meaning the file name can be anything you choose. Listing 3-4 shows an example of using a color resource in Java code.
Listing 3-4. Color Resources in Java code
int mainBackGroundColor
= activity.getResources.getColor(R.color.main_back_ground_color);
Listing 3-5 shows how you would use a color resource in a view definition.
Listing 3-5. Using Colors in View Definitions
android:textColor="@color/ red"
android:text="Sample Text to Show Red Color"/>
More on String resources
We covered string resources briefly when we introduced resources at the beginning of this chapter. Let us revisit them in order to provide some more detail. We will show you how to define and use HTML strings, as well as how to substitute variables in string resources.
Let us start by showing how you can define normal strings, quoted strings, HTML strings, and substitutable strings in an XML resource file (see Listing 3-6).
Listing 3-6. XML Syntax for Defining String Resources
hello %2$s java format string. %1$s again
Hello Slanted Android, You are bold.
This XML string-resource file needs to be in the /res/values subdirectory. The name of the file is arbitrary.
Notice how quoted strings need to be either escaped or placed in alternate quotes. The string definitions also allow standard Java string-formatting sequences.
Android also allows child XML elements such as , , and other simple text-formatting HTML within the
The Java examples in Listing 3-7 illustrate each usage.
Listing 3-7. Using String Resources in Java Code
//Read a simple string and set it in a text view
String simpleString = activity.getString(R.string.simple_string);
textView.setText(simpleString);
//Read a quoted string and set it in a text view
String quotedString = activity.getString(R.string.quoted_string);
textView.setText(quotedString);
//Read a double quoted string and set it in a text view
String doubleQuotedString = activity.getString(R.string.double_quoted_string);
textView.setText(doubleQuotedString);
//Read a Java format string
String javaFormatString = activity.getString(R.string.java_format_string);
//Convert the formatted string by passing in arguments
String substitutedString = String.format(javaFormatString, "Hello" , "Android");
//set the output in a text view
textView.setText(substitutedString);
//Read an html string from the resource and set it in a text view
String htmlTaggedString = activity.getString(R.string.tagged_string);
//Convert it to a text span so that it can be set in a text view
//android.text.Html class allows painting of "html" strings
//This is strictly an Android class and does not support all html tags
Spanned textSpan = android.text.Html.fromHtml(htmlTaggedString);
//Set it in a text view
textView.setText(textSpan);
Once you’ve defined the strings as resources, you can set them directly on a view such as TextView in the XML layout definition for that TextView. Listing 3-8 shows an example where an HTML string is set as the text content of a TextView.
Listing 3-8. Using String Resources in XML
android:textAlign="center"
android:text="@string/tagged_string"/>
TextView automatically realizes that this string is an HTML string, and honors its formatting accordingly. This is nice because you can quickly set attractive text in your views as part of the layout.
Dimension resources
Pixels, inches, and points are all examples of dimensions that can play a part in XML layouts or Java code. You can use these dimension resources to style and localize Android UIs without changing the source code.
Listing 3-9 shows how you can use dimension resources in XML.
Listing 3-9. XML Syntax for Defining Dimension Resources
You could specify the dimensions in any of the following units:
• px: Pixels
• in: Inches
• mm: Millimeters
• pt: Points
• dp: Density-independent pixels based on a 160-dpi (pixel density per inch) screen
(dimensions adjust to screen density)
• sp: Scale-independent pixels (dimensions that allow for user sizing; helpful for use in
fonts)
In Java, you need to access your Resources object instance to retrieve a dimension. You can do this by calling getResources on an activity object (see Listing 3-10).
Listing 3-10. Using Dimension Resources in Java Code
float dimen = activity.getResources().getDimension(R.dimen.mysize_in_pixels);
As in Java, the resource reference for a dimension in XML uses dimen as opposed to the full word “dimension” (see Listing 3-11).
Listing 3-11. Using Dimension Resources in XML
android:textSize="@dimen/medium_size"/>
Image resources
Android generates resource IDs for image files placed in the /res/drawable subdirectory. The supported image types include .gif, .jpg, and .png. Each image file in this directory generates a unique ID from its base file name. If the image file name is sample_image.jpg, for example, then the resource ID generated will be R.drawable.sample_image.
You can then reference these images in other XML layout definitions, as shown in Listing 3-12.
Listing 3-12. Using Image Resources in XML
android:id="@+id/button1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Dial"
android:background="@drawable/sample_image"
/>
You can also retrieve the image programmatically and set it yourself in Java (see Listing 3-13).
Listing 3-13. Using Image Resources in Java
//Call getDrawable to get the image
BitmapDrawable d = activity.getResources().getDrawable(R.drawable.sample_image);
//You can use the drawable then to set the background
button.setBackgroundDrawable(d);
//or you can set the background directly from the Resource Id
button.setBackgroundResource(R.drawable.icon);
Android also supports a special type of image called a stretchable image. This is simply a kind of .png where parts of the image can be specified as static and stretchable. Android provides a tool called the Draw 9-patch tool to specify these regions. (You can read more about it at http://developer.android.com/guide/developing/tools/draw9patch.html.) Once the .png image is made available, you can use it as any other image. It comes in handy when used as background for buttons where the button has to stretch itself to accommodate the text.
Color-Drawable resources
In Android, an image is one type of a drawable resource. Android supports another drawable resource called a color-drawable resource; it’s essentially a colored rectangle.
To define one of these color rectangles, you define an XML element by the node name of drawable in any XML file in the /res/values subdirectory. Listing 3-14 shows a couple of colordrawable resource examples.
Listing 3-14. XML Syntax for Defining Color-Drawable Resources
Listings 3-15 and 3-16 show how you can use a color-drawable resource in Java and XML,
respectively.
Listing 3-15. Using Color-Drawable Resources in Java Code
// Get a drawable
ColorDrawble redDrawable =
(ColorDrawable)
activity.getResources().getDrawable(R.drawable.red_rectnagle);
//Set it as a background to a text view
textView.setBackground(redDrawable);
Listing 3-16. Using Color-Drawable Resources in XML Code
android:textAlign="center"
android:background="@drawable/red_rectangle"/>
To achieve the rounded corners in your drawable, you can use the currently undocumented
directory. Listing 3-17 shows how you can use the
a file called /res/drawable/my_rounded_rectangle.xml.
Listing 3-17. Defining a Rounded Rectangle
You can then use this drawable resource as a background of the previous text-view
example:
// Get a drawable
GradientDrawable roundedRectangle =
(GradientDrawable)
activity.getResources().getDrawable(R.drawable.red_rectnagle);
//Set it as a background to a text view
textView.setBackground(roundedRectangle);
Working with Arbitrary XML Resource Files
Android also allows arbitrary XML files as resources. This approach offers three distinct advantages. First, it provides a quick way to reference these files based on their generated resource IDs. Second, the approach allows you to localize these resource XML files. Third, you can compile and store these XML files on the device efficiently.
XML files that need to be read in this fashion are stored under the /res/xml subdirectory. Here is an example XML file called /res/xml/test.xml:
<rootelem1>
<subelem1>
Hello World from an xml sub element
</subelem1>
</rootelem1>
As it does with other Android XML resource files, the AAPT will compile this XML file before placing it in the application package. You will need to use an instance of XmlPullParser if you want to parse these files. You can get an instance of the XmlPullParser implementation using this code from any context (including activity):
Resources res = activity.getResources();
XmlResourceParser xpp = res.getXml(R.xml.test);
The returned XmlResourceParser is an instance of XmlPullParser, and it also implements java.util.AttributeSet. Listing 3-18 shows a more complete code snippet that reads the test.xml file.
Listing 3-18. Using XmlPullParser
private String getEventsFromAnXMLFile(Activity activity)
throws XmlPullParserException, IOException
{
StringBuffer sb = new StringBuffer();
Resources res = activity.getResources();
XmlResourceParser xpp = res.getXml(R.xml.test);
xpp.next();
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT)
{
if(eventType == XmlPullParser.START_DOCUMENT)
{
sb.append("******Start document");
}
else if(eventType == XmlPullParser.START_TAG)
{
sb.append("\nStart tag "+xpp.getName());
}
else if(eventType == XmlPullParser.END_TAG)
{
sb.append("\nEnd tag "+xpp.getName());
}
else if(eventType == XmlPullParser.TEXT)
{
sb.append("\nText "+xpp.getText());
}
eventType = xpp.next();
}//eof-while
sb.append("\n******End document");
return sb.toString();
}//eof-function
In Listing 3-18, you can see how to get XmlPullParser, how to use XmlPullParser to navigate the XML elements in the XML document, and how to use additional methods of XmlPullParser to access the details of the XML elements. If you want to run this code, you must create an XML file as shown earlier and call the getEventsFromAnXMLFile function from any menu item or button click. It will return a string, which you can print out to the log stream using the Log.d debug method.
Working with Raw Resources
Android also allows raw files in addition to raw XML files. These raw resources, placed in /res/ raw, are arbitrary file resources such as audio, video, or text files that require localization or references through resource IDs. Unlike the raw XML files placed in /res/xml, these files are not compiled but moved to the application package as is. However, each file will have an identifier
generated in R.java. If you were to place a text file at /res/raw/test.txt, you would be able to read that file using the code in Listing 3-19.
Listing 3-19. Reading a Raw Resource
String getStringFromRawFile(Activity activity)
{
Resources r = activity.getResources();
InputStream is = r.openRawResource(R.raw.test);
String myText = convertStreamToString(is);
is.close();
return myText;
}
String convertStreamToString(InputStream is)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int i = is.read();
while (i != -1)
{
baos.write(i);
i = baos.read();
}
return baos.toString();
}
Working with Assets
Android offers one more directory where you can keep files to be included in the package:/assets. It’s at the same level as /res, meaning it’s not part of the /res subdirectories. The files in /assets do not generate IDs in R.java; you must specify the file path to read them. The file path is a relative path starting at /assets. You will use the AssetManager class to access these files:
//Note: Exceptions are not shown in the code
String getStringFromAssetFile(Activity activity)
{
AssetManager am = activity.getAssets();
InputStream is = am.open("test.txt");
String s = convertStreamToString(is);
is.close();
return s;
}
Reviewing the Resources Directory Structure
In summary, here is a quick look at the overall resources directory structure:
/res/values/strings.xml
/colors.xml
/dimens.xml
/attrs.xml
/styles.xml
/drawable/*.png
/*.jpg
/*.gif
/*.9.png
/anim/*.xml
/layout/*.xml
/raw/*.*
/xml/*.xml
/assets/*.*/*.*
Let us conclude this section on resources by quickly enumerating what you have learned about resources so far. You know the types of resources supported in Android and you know how to create these resources in XML files. You know how resource IDs are generated and how to use them in Java code. You also learned that resource ID generation is a convenient scheme
that simplifies resource usage in Android. Finally, you learned how to work with raw resources and assets. With that, we will now turn our attention to the section on content providers, where you will learn to work with data on Android.
Published in
Java
Thursday, 29 July 2010 00:03
Using resources, Content Providers, and Intents
In Chapter 2, you got an overview of an Android application and a quick look at some of its underlying concepts. Among these, resources, content providers, and intents form the three primary pillars of Android UI programming. Android depends on resources for look-and-feel flexibility, content providers for abstracting data into services, and intents for interoperability
and UI reuse. You must fully understand these three concepts in order to build successful Android applications, so we’ll discuss them in depth here.
Understanding Resources
Resources are critical to the Android architecture. In this section, you’ll learn what resources are and how to create them using resource files. You’ll find out that resources are declarative, and that Android creates resource IDs for convenient use in your Java programs. You’ll also see how the R.java source file mediates the generation and usage of these resource IDs. Then
you’ll learn how to define resources in XML files, reuse resources in other resource XML definitions, and reuse resources in Java programs. In addition to these XML-based resources, this chapter also covers two other types of resources: raw resources and assets.
String Resources
A resource in Android is a file (like a music file) or a value (like the title of a dialog box) that is bound to an executable application. These files and values are bound to the executable in such a way that you can change them without recompiling and redeploying the application.
Resources play a part in many, if not all, familiar UI frameworks. Familiar examples of resources include strings, colors, and bitmaps. Instead of hard-coding strings in an application, for example, you can use their IDs instead. This indirection lets you change the text of the string resource without changing the source code.
Let’s start with strings and see how they are used as resources. Android allows you to define multiple strings in one or more XML resource files. These XML files containing string-resource definitions reside in the /res/values subdirectory. The names of the XML files are arbitrary, although you will commonly see the file name as strings.xml. Listing 3-1 shows an example of
a string-resource file.
Listing 3-1. Example strings.xml File
hello
hello appname
When this file is created or updated, the Eclipse ADT plug-in will automatically update a Java class in your application’s root package called R.java with unique IDs for the two string resources specified.
For the string-resource file in Listing 3-1, the updated R.java file would have these entries:
public final class R {
...other entries depending on your project and application
public static final class string
{
...other entries depending on your project and application
public static final int hello=0x7f040000;
public static final int app_name=0x7f040001;
...other entries depending on your project and application
}
...other entries depending on your project and application
}
Let’s focus on the static definition for static final class string. R.java creates this inner static class as a namespace to hold string-resource IDs. The two static final ints defined with variable names hello and app_name are the resource IDs that represent the corresponding string resources. You could use these resource IDs anywhere in the source code through the following code structure:
R.string.hello
Note that these generated IDs point to ints rather than strings. Most methods that take strings also take these resource identifiers as inputs. Android will resolve those ints to strings where needed.
It is merely a convention that most sample applications define all strings in one strings. xml file. Android takes any number of arbitrary files as long as the structure of the XML file looks like Listing 3-1 and the file resides in the /res/values subdirectory.
The structure of this file is easy to follow. You have the root node of followed by one or more of its child elements of . Each element or node has a property called name that will end up as the id attribute in R.java and the actual text for that string ID.
To see that multiple string-resource files are allowed in this subdirectory, you can place another file with the following content in the same subdirectory and call it strings1.xml:
hello 1
hello appname 1
The Eclipse ADT plug-in will validate the uniqueness of these IDs at compile time and place them in R.java as two additional constants: R.string.hello1 and R.string.app_name1.
Layout Resources
A layout resource is another key resource commonly used in Android programming. In Android, the view for a screen is often loaded from an XML file as a resource. These XML files are called layout resources. Consider this code segment for a sample Android activity:
public class HelloWorldActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView tv = (TextView)this.findViewById(R.id.text1);
tv.setText("Try this text instead");
}
………
}
The line setContentView(R.layout.main) points out that there is a static class called R.layout, and within that class there is a constant called main (an integer) pointing to a View defined by an XML layout-resource file. The name of the XML file would be main.xml, which needs to be placed in the resources’ layout subdirectory. In other words, this statement would expect the programmer to create the file /res/layout/main.xml and place the necessary layout definition in that file. The contents of the main.xml layout file could look like Listing 3-2.
Listing 3-2. Example main.xml Layout File
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@+string/hello"
/>
The layout file in Listing 3-2 defines a root node called LinearLayout, which contains a TextView followed by a Button. A LinearLayout lays out its children vertically or horizontally— vertically, in this example.
You will need to define a separate layout file for each screen. More accurately, each layout needs a dedicated file. If you are painting two screens, you will likely need two layout files such as /res/layout/screen1_layout.xml and /res/layout/screen2_layout.xml.
For example, if you have two files under /res/layout/ called file1.xml and file2.xml, you’ll have the following entries in R.java:
public static final class layout {
.... any other files
public static final int file1=0x7f030000;
public static final int file2=0x7f030001;
}
The views defined in these layout files are accessible in code if you reference their IDs from R.java:
TextView tv = (TextView)this.findViewById(R.id.text1);
tv.setText("Try this text instead");
In this example, you locate the TextView by using the findViewById method of the Activity class. The constant R.id.text1 corresponds to the ID defined for the TextView. The id for the TextView in the layout file is as follows:
..
The attribute value for the id attribute indicates that a constant called text1 will be used to uniquely identify this view among other views hosted by that activity. The plus sign (+) in @+id/text1 means that text1 will be created if it doesn’t exist already. There is more to this syntax, in which ids are assigned to resources. We’ll talk about that next.
Resource-Reference Syntax
Irrespective of the type of resource, all Android resources are identified (or referenced) by their id in Java source code. The syntax you use to allocate an id to a resource in the XML file is called resource-reference syntax. The id attribute syntax in the previous example @+id/text1 has the following formal structure:
@[package:]type/name
The type corresponds to one of the resource-type namespaces available in R.java, such as
the following:
• R.drawable
• R.id
• R.layout
• R.string
• R.attr
The corresponding types in XML resource-reference syntax are as follows:
• drawable
• id
• layout
• string
• attr
The name part in the resource reference @[package:]type/name is the name given to the resource; it also gets represented as an int constant in R.java. Now we have come to the important part of this syntax: the package. If you don’t specify any package, then the pair type/ name will be resolved based on local resources and the application’s local R.java package.
If you specify android:type/name, on the other hand, the reference will look in the package identified by android: the android.R.java file, to be precise. So you can use any Java package name in place of the package placeholder to locate the right R.java file to resolve the reference. Based on this information, let’s analyze a few examples:
// Compile error, as id will not take raw text strings
// wrong syntax. It is missing a type name
// you will get an error "No Resource type specified
//Error: No Resource found that matches id "text"
//Unless you have taken care to define "text" before
// Error: Resource is not public
// indicating that there is no such id in android.R.id
// Of course this would be valid if Android R.java were to define
// an id with this name
//Success: Creates an id called "text" in the local package
Defining Your Own Resource IDs for Later Use
The general pattern for allocating an id is either to create a new one or to use the one created by the Android package. However, it is possible to create ids beforehand and use them later in your own packages.
The line in the preceding code segment indicates that an id named text is going to be used if one already exists. If the id doesn’t exist, then a new one is going to be created. So when might an id such as text already exist in R.java for it to be reused?
You might be inclined to put a constant like R.id.text in R.java, but R.java is not editable. Even if it were, it gets regenerated every time something gets changed, added, or deleted in the /res/* subdirectory. However, you can use a resource tag called item to define an id without attaching to any particular resource. Here is an example:
The type refers to the type of resource—an id in this case. Once this id is in place, the following
View definition would work:
..
and UI reuse. You must fully understand these three concepts in order to build successful Android applications, so we’ll discuss them in depth here.
Understanding Resources
Resources are critical to the Android architecture. In this section, you’ll learn what resources are and how to create them using resource files. You’ll find out that resources are declarative, and that Android creates resource IDs for convenient use in your Java programs. You’ll also see how the R.java source file mediates the generation and usage of these resource IDs. Then
you’ll learn how to define resources in XML files, reuse resources in other resource XML definitions, and reuse resources in Java programs. In addition to these XML-based resources, this chapter also covers two other types of resources: raw resources and assets.
String Resources
A resource in Android is a file (like a music file) or a value (like the title of a dialog box) that is bound to an executable application. These files and values are bound to the executable in such a way that you can change them without recompiling and redeploying the application.
Resources play a part in many, if not all, familiar UI frameworks. Familiar examples of resources include strings, colors, and bitmaps. Instead of hard-coding strings in an application, for example, you can use their IDs instead. This indirection lets you change the text of the string resource without changing the source code.
Let’s start with strings and see how they are used as resources. Android allows you to define multiple strings in one or more XML resource files. These XML files containing string-resource definitions reside in the /res/values subdirectory. The names of the XML files are arbitrary, although you will commonly see the file name as strings.xml. Listing 3-1 shows an example of
a string-resource file.
Listing 3-1. Example strings.xml File
When this file is created or updated, the Eclipse ADT plug-in will automatically update a Java class in your application’s root package called R.java with unique IDs for the two string resources specified.
For the string-resource file in Listing 3-1, the updated R.java file would have these entries:
public final class R {
...other entries depending on your project and application
public static final class string
{
...other entries depending on your project and application
public static final int hello=0x7f040000;
public static final int app_name=0x7f040001;
...other entries depending on your project and application
}
...other entries depending on your project and application
}
Let’s focus on the static definition for static final class string. R.java creates this inner static class as a namespace to hold string-resource IDs. The two static final ints defined with variable names hello and app_name are the resource IDs that represent the corresponding string resources. You could use these resource IDs anywhere in the source code through the following code structure:
R.string.hello
Note that these generated IDs point to ints rather than strings. Most methods that take strings also take these resource identifiers as inputs. Android will resolve those ints to strings where needed.
It is merely a convention that most sample applications define all strings in one strings. xml file. Android takes any number of arbitrary files as long as the structure of the XML file looks like Listing 3-1 and the file resides in the /res/values subdirectory.
The structure of this file is easy to follow. You have the root node of
To see that multiple string-resource files are allowed in this subdirectory, you can place another file with the following content in the same subdirectory and call it strings1.xml:
The Eclipse ADT plug-in will validate the uniqueness of these IDs at compile time and place them in R.java as two additional constants: R.string.hello1 and R.string.app_name1.
Layout Resources
A layout resource is another key resource commonly used in Android programming. In Android, the view for a screen is often loaded from an XML file as a resource. These XML files are called layout resources. Consider this code segment for a sample Android activity:
public class HelloWorldActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView tv = (TextView)this.findViewById(R.id.text1);
tv.setText("Try this text instead");
}
………
}
The line setContentView(R.layout.main) points out that there is a static class called R.layout, and within that class there is a constant called main (an integer) pointing to a View defined by an XML layout-resource file. The name of the XML file would be main.xml, which needs to be placed in the resources’ layout subdirectory. In other words, this statement would expect the programmer to create the file /res/layout/main.xml and place the necessary layout definition in that file. The contents of the main.xml layout file could look like Listing 3-2.
Listing 3-2. Example main.xml Layout File
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
android:layout_height="wrap_content"
android:text="@string/hello"
/>
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@+string/hello"
/>
The layout file in Listing 3-2 defines a root node called LinearLayout, which contains a TextView followed by a Button. A LinearLayout lays out its children vertically or horizontally— vertically, in this example.
You will need to define a separate layout file for each screen. More accurately, each layout needs a dedicated file. If you are painting two screens, you will likely need two layout files such as /res/layout/screen1_layout.xml and /res/layout/screen2_layout.xml.
For example, if you have two files under /res/layout/ called file1.xml and file2.xml, you’ll have the following entries in R.java:
public static final class layout {
.... any other files
public static final int file1=0x7f030000;
public static final int file2=0x7f030001;
}
The views defined in these layout files are accessible in code if you reference their IDs from R.java:
TextView tv = (TextView)this.findViewById(R.id.text1);
tv.setText("Try this text instead");
In this example, you locate the TextView by using the findViewById method of the Activity class. The constant R.id.text1 corresponds to the ID defined for the TextView. The id for the TextView in the layout file is as follows:
The attribute value for the id attribute indicates that a constant called text1 will be used to uniquely identify this view among other views hosted by that activity. The plus sign (+) in @+id/text1 means that text1 will be created if it doesn’t exist already. There is more to this syntax, in which ids are assigned to resources. We’ll talk about that next.
Resource-Reference Syntax
Irrespective of the type of resource, all Android resources are identified (or referenced) by their id in Java source code. The syntax you use to allocate an id to a resource in the XML file is called resource-reference syntax. The id attribute syntax in the previous example @+id/text1 has the following formal structure:
@[package:]type/name
The type corresponds to one of the resource-type namespaces available in R.java, such as
the following:
• R.drawable
• R.id
• R.layout
• R.string
• R.attr
The corresponding types in XML resource-reference syntax are as follows:
• drawable
• id
• layout
• string
• attr
The name part in the resource reference @[package:]type/name is the name given to the resource; it also gets represented as an int constant in R.java. Now we have come to the important part of this syntax: the package. If you don’t specify any package, then the pair type/ name will be resolved based on local resources and the application’s local R.java package.
If you specify android:type/name, on the other hand, the reference will look in the package identified by android: the android.R.java file, to be precise. So you can use any Java package name in place of the package placeholder to locate the right R.java file to resolve the reference. Based on this information, let’s analyze a few examples:
// Compile error, as id will not take raw text strings
// wrong syntax. It is missing a type name
// you will get an error "No Resource type specified
//Error: No Resource found that matches id "text"
//Unless you have taken care to define "text" before
// Error: Resource is not public
// indicating that there is no such id in android.R.id
// Of course this would be valid if Android R.java were to define
// an id with this name
//Success: Creates an id called "text" in the local package
Defining Your Own Resource IDs for Later Use
The general pattern for allocating an id is either to create a new one or to use the one created by the Android package. However, it is possible to create ids beforehand and use them later in your own packages.
The line
You might be inclined to put a constant like R.id.text in R.java, but R.java is not editable. Even if it were, it gets regenerated every time something gets changed, added, or deleted in the /res/* subdirectory. However, you can use a resource tag called item to define an id without attaching to any particular resource. Here is an example:
The type refers to the type of resource—an id in this case. Once this id is in place, the following
View definition would work:
..
Published in
Java
Wednesday, 28 July 2010 23:32
Examining the Application Lifecycle
The lifecycle of Android applications differs greatly from the lifecycle of web-based J2EE applications. J2EE apps are loosely managed by the container they run in. For example, a J2EE container can remove an application from memory if it sits idle for a predetermined time period. But the container generally won’t move applications in and out of memory based on load and/or available resources. In other words, it’s up to the application owners to ensure that resources are available.
The lifecycle of an Android application, on the other hand, is strictly managed by the system, based on the user’s needs, available resources, and so on. A user might want to launch a web browser, for example, but the system ultimately decides whether to start the application.Although the system is the ultimate manager, it adheres to some defined and logical guidelines
to determine whether an application can be loaded, paused, or stopped. If the user is currently working with an activity, the system will give high priority to that application. Conversely, if an activity is not visible and the system determines that an application must be shut down to free up resources, it will shut down the lower-priority application.
The concept of application lifecycle is logical, but a fundamental aspect of Android applications complicates matters. Specifically, the Android application architecture is component-and integration-oriented. This allows a rich user experience, seamless reuse, and easy application integration, but creates a complex task for the application-lifecycle manager.
Let’s consider a typical scenario. A user is talking to someone on the phone and needs to open an e-mail message to answer a question. She goes to the home screen, opens the mail application, opens the e-mail message, clicks a link in the e-mail, and answers her friend’s question by reading a stock quote from a web page. This scenario would require four applications: the home application, a talk application, an e-mail application, and a browser application. As the user navigates from one application to the next, her experience is seamless. In the background, however, the system is saving and restoring application state. For instance,when the user clicks the link in the e-mail message, the system saves metadata on the running
e-mail–message activity before starting the browser-application activity to launch a URL. In fact, the system saves metadata on any activity before starting another, so that it can come back to the activity (when the user backtracks, for example). If memory becomes an issue, the system will have to shut down a process running an activity and resume it as necessary.
Android is sensitive to the lifecycle of an application and its components. Therefore, you’ll need to understand and handle lifecycle events in order to build a stable application. The processes running your Android application and its components go through various lifecycle events, and Android provides callbacks that you can implement to handle state changes. For
starters, you’ll want to become familiar with the various lifecycle callbacks for an activity (see Listing 2-7).
Listing 2-7. Lifecycle Methods of an Activity
protected void onCreate(Bundle savedInstanceState);
protected void onStart();
protected void onRestart();
protected void onResume();
protected void onPause();
protected void onStop();
protected void onDestroy();
Listing 2-7 shows the list of lifecycle methods that Android calls during the life of an activity. It’s important to understand when each of the methods is called by the system to ensure that you implement a stable application. Note that you do not need to react to all of these methods. If you do, however, be sure to call the superclass versions as well. Figure 2-9 shows
the transitions between states.
The system can start and stop your activities based on what else is happening. Android calls the onCreate() method when the activity is freshly created. onCreate() is always followed by a call to onStart(), but onStart() is not always preceded by a call to onCreate() because onStart() can be called if your application was stopped (from onStop()). When onStart() is called, your activity is not visible to the user, but it’s about to be. onResume() is called after onStart(), just when the activity is in the foreground and accessible to the user. At this point, the user is interacting with your activity.
When the user decides to move to another activity, the system will call your activity’s onPause() method. From onPause(), you can expect either onResume() or onStop() to be called. onResume() is called, for example, if the user brings your activity back to the foreground.onStop() is called if your activity becomes invisible to the user. If your activity is brought back
to the foreground, after a call to onStop(), then onRestart() will be called. If your activity sits on the activity stack but is not visible to the user, and the system decides to kill your activity, onDestroy() will be called.
The state model described for an activity appears complex, but you are not required to deal with every possible scenario. In fact, you will mostly handle onCreate() and onPause().You will handle onCreate() to create the user interface for your activity. In this method, you will bind data to your widgets and wire up any event handlers for your UI components. In
onPause(), you will want to persist critical data to your application’s data store. It’s the last safe method that will get called before the system kills your application. onStop() and onDestroy() are not guaranteed to be called, so don’t rely on these methods for critical logic.
The takeaway from this discussion? The system manages your application, and it can start, stop, or resume an application component at any time. Although the system controls your components, they don’t run in complete isolation with respect to your application. In other words, if the system starts an activity in your application, you can count on an application context in your activity. For example, it’s not uncommon to have global variables shared among the activities in your application. You can share a global variable by writing an extension of the android.app.Application class and then initializing the global
variable in the onCreate() method (see Listing 2-8). Activities and other components in your application can then access these references with confidence when they are executing.
Listing 2-8. An Extension of the Application Class
public class MyApplication extends Application
{
// global variable
private static final String myGlobalVariable;
@Override
public void onCreate()
{
super.onCreate();
//... initialize global variables here
myGlobalVariable = loadCacheData();
}
public static String getMyGlobalVariable() {
return myGlobalVariable;
}
}
In the next section, we’ll give you some armor to help you develop Android applications—we will discuss debugging.
Debugging Your App
After you write a few lines of code for your first application, you’ll start wondering if it’s possible to have a debug session while you interact with your application in the emulator. Shortly after that, you’ll instinctively run to System.out.println(), which will fail because the code is running on the emulator and the sys-out statement is not fed back to the IDE. But don’t worry; the Android SDK includes a host of applications that you can use for debugging purposes.
To log messages from your application, you’ll want to use the android.util.Log class. This class defines the familiar informational, warning, and error methods. You can also get detailed tracing information by using the android.os.Debug class, which provides a start-tracing method (Debug.startMethodTracing()) and a stop-tracing method (Debug.stopMethodTracing()). You can then view the tracer output using the trace-viewer tool included in the Android SDK. The SDK also includes a file-explorer tool that you can use to view files on the device. These tools are integrated with the Eclipse IDE (see Figure 2-10).
You can view the tools by selecting the Debug perspective in Eclipse. You can also launch each tool by going to Window ➤ Show View ➤ Other ➤ Android.One of the tools that you’ll use throughout your Android development is LogCat. This tool
displays the log messages that you emit using android.util.Log, exceptions, and so on. We will introduce the other tools throughout the book.
The lifecycle of an Android application, on the other hand, is strictly managed by the system, based on the user’s needs, available resources, and so on. A user might want to launch a web browser, for example, but the system ultimately decides whether to start the application.Although the system is the ultimate manager, it adheres to some defined and logical guidelines
to determine whether an application can be loaded, paused, or stopped. If the user is currently working with an activity, the system will give high priority to that application. Conversely, if an activity is not visible and the system determines that an application must be shut down to free up resources, it will shut down the lower-priority application.
The concept of application lifecycle is logical, but a fundamental aspect of Android applications complicates matters. Specifically, the Android application architecture is component-and integration-oriented. This allows a rich user experience, seamless reuse, and easy application integration, but creates a complex task for the application-lifecycle manager.
Let’s consider a typical scenario. A user is talking to someone on the phone and needs to open an e-mail message to answer a question. She goes to the home screen, opens the mail application, opens the e-mail message, clicks a link in the e-mail, and answers her friend’s question by reading a stock quote from a web page. This scenario would require four applications: the home application, a talk application, an e-mail application, and a browser application. As the user navigates from one application to the next, her experience is seamless. In the background, however, the system is saving and restoring application state. For instance,when the user clicks the link in the e-mail message, the system saves metadata on the running
e-mail–message activity before starting the browser-application activity to launch a URL. In fact, the system saves metadata on any activity before starting another, so that it can come back to the activity (when the user backtracks, for example). If memory becomes an issue, the system will have to shut down a process running an activity and resume it as necessary.
Android is sensitive to the lifecycle of an application and its components. Therefore, you’ll need to understand and handle lifecycle events in order to build a stable application. The processes running your Android application and its components go through various lifecycle events, and Android provides callbacks that you can implement to handle state changes. For
starters, you’ll want to become familiar with the various lifecycle callbacks for an activity (see Listing 2-7).
Listing 2-7. Lifecycle Methods of an Activity
protected void onCreate(Bundle savedInstanceState);
protected void onStart();
protected void onRestart();
protected void onResume();
protected void onPause();
protected void onStop();
protected void onDestroy();
Listing 2-7 shows the list of lifecycle methods that Android calls during the life of an activity. It’s important to understand when each of the methods is called by the system to ensure that you implement a stable application. Note that you do not need to react to all of these methods. If you do, however, be sure to call the superclass versions as well. Figure 2-9 shows
the transitions between states.
The system can start and stop your activities based on what else is happening. Android calls the onCreate() method when the activity is freshly created. onCreate() is always followed by a call to onStart(), but onStart() is not always preceded by a call to onCreate() because onStart() can be called if your application was stopped (from onStop()). When onStart() is called, your activity is not visible to the user, but it’s about to be. onResume() is called after onStart(), just when the activity is in the foreground and accessible to the user. At this point, the user is interacting with your activity.
When the user decides to move to another activity, the system will call your activity’s onPause() method. From onPause(), you can expect either onResume() or onStop() to be called. onResume() is called, for example, if the user brings your activity back to the foreground.onStop() is called if your activity becomes invisible to the user. If your activity is brought back
to the foreground, after a call to onStop(), then onRestart() will be called. If your activity sits on the activity stack but is not visible to the user, and the system decides to kill your activity, onDestroy() will be called.
The state model described for an activity appears complex, but you are not required to deal with every possible scenario. In fact, you will mostly handle onCreate() and onPause().You will handle onCreate() to create the user interface for your activity. In this method, you will bind data to your widgets and wire up any event handlers for your UI components. In
onPause(), you will want to persist critical data to your application’s data store. It’s the last safe method that will get called before the system kills your application. onStop() and onDestroy() are not guaranteed to be called, so don’t rely on these methods for critical logic.
The takeaway from this discussion? The system manages your application, and it can start, stop, or resume an application component at any time. Although the system controls your components, they don’t run in complete isolation with respect to your application. In other words, if the system starts an activity in your application, you can count on an application context in your activity. For example, it’s not uncommon to have global variables shared among the activities in your application. You can share a global variable by writing an extension of the android.app.Application class and then initializing the global
variable in the onCreate() method (see Listing 2-8). Activities and other components in your application can then access these references with confidence when they are executing.
Listing 2-8. An Extension of the Application Class
public class MyApplication extends Application
{
// global variable
private static final String myGlobalVariable;
@Override
public void onCreate()
{
super.onCreate();
//... initialize global variables here
myGlobalVariable = loadCacheData();
}
public static String getMyGlobalVariable() {
return myGlobalVariable;
}
}
In the next section, we’ll give you some armor to help you develop Android applications—we will discuss debugging.
Debugging Your App
After you write a few lines of code for your first application, you’ll start wondering if it’s possible to have a debug session while you interact with your application in the emulator. Shortly after that, you’ll instinctively run to System.out.println(), which will fail because the code is running on the emulator and the sys-out statement is not fed back to the IDE. But don’t worry; the Android SDK includes a host of applications that you can use for debugging purposes.
To log messages from your application, you’ll want to use the android.util.Log class. This class defines the familiar informational, warning, and error methods. You can also get detailed tracing information by using the android.os.Debug class, which provides a start-tracing method (Debug.startMethodTracing()) and a stop-tracing method (Debug.stopMethodTracing()). You can then view the tracer output using the trace-viewer tool included in the Android SDK. The SDK also includes a file-explorer tool that you can use to view files on the device. These tools are integrated with the Eclipse IDE (see Figure 2-10).
You can view the tools by selecting the Debug perspective in Eclipse. You can also launch each tool by going to Window ➤ Show View ➤ Other ➤ Android.One of the tools that you’ll use throughout your Android development is LogCat. This tool
displays the log messages that you emit using android.util.Log, exceptions, and so on. We will introduce the other tools throughout the book.
Published in
Java
Wednesday, 28 July 2010 22:49
Exploring the Structure of an Android Application
Although the size and complexity of Android applications can vary greatly, their structures will be similar. Figure 2-7 shows the structure of the “Hello World!” app you just built.
As you can see from Table 2-1, an Android application is primarily made up of three pieces: the application descriptor, a collection of various resources, and the application’s source code. If you put aside the AndroidManifest.xml file for a moment, you can view an Android app in this simple way: you have some business logic implemented in code, and everything else is a resource. This basic structure resembles the basic structure of a J2EE app, where the resources correlate to JSPs, the business logic correlates to servlets, and the AndroidManifest.xml file correlates to the web.xml file.
You can also compare J2EE’s development model to Android’s development model. In J2EE, the philosophy of building views is to build them using markup language. Android has also adopted this approach, although the markup in Android is XML. You benefit from this approach because you don’t have to hard-code your application’s views; you can modify the look and feel of the application by editing the markup.
It is also worth noting a few constraints regarding resources. First, Android supports only a linear list of files within the predefined folders under res. For example, it does not support nested folders under the layout folder (or the other folders under res). Second, there are some similarities between the assets folder and the raw folder under res. Both folders can contain raw files, but the files within raw are considered resources and the files within assets are not.So the files within raw will be localized, accessible through resource IDs, and so on. But the contents of the assets folder are considered general-purpose contents, to be used without resource constraints and support. Note that because the contents of the assets folder are not considered resources, you can put an arbitrary hierarchy of folders and files within it. (We’ll
talk a lot more about resources in Chapter 3.)
Analyzing the Notepad Application
Not only do you know how to create a new Android application and run it in the emulator, but you also have a feel for the artifacts of an Android application. Next, we are going to look at the Notepad application that ships with the Android SDK. Notepad’s complexity falls between that of the “Hello World!” app and a full-blown Android application, so analyzing its components will give you some realistic insight into Android development.
Loading and Running the Notepad Application
In this section, we’ll show you how to load the Notepad application into the Eclipse IDE and run it in the emulator. Before we start, you should know that the Notepad application implements several use cases. For example, the user can create a new note, edit an existing note, delete a note,view the list of created notes, and so on. When the user launches the application, there aren’t any saved notes yet, so the user sees an empty note list. If the user presses the Menu key, the application presents him with a list of actions, one of which allows him to add a new note. After he adds the note, he can edit or delete the note by selecting the corresponding menu option.
Follow these steps to load the Notepad sample into the Eclipse IDE:
1. Start Eclipse.
2. Go to File ? New ? Project.
3. In the “New Project” dialog, select Android ? Android Project.
4. In the “New Android Project” dialog, select “Create project from existing source” and set the “Location” field to the path of the Notepad application. Note that the Notepad application is located in c:\AndroidSDK\samples\, which you downloaded earlier. After you set the path, the dialog reads the AndroidManifest.xml file and prepopulates the remaining fields in the “New Android Project” dialog box.
5. Click the “Finish” button.
You should now see the NotesList application in your Eclipse IDE. To run the application, you could create a launch configuration (as you did for the “Hello World!” application), or you can simply right-click the project, choose Run As, and select Android Application. This will launch the emulator and install the application on it. After the emulator has completed loading (you’ll see the date and time displayed in the center of the emulator’s screen), press the Menu button to view the Notepad application. Play around with the application for a few minutes to become familiar with it.
Dissecting the Application
As you can see, the application contains several .java files, a few .png images, three views (under the layout folder), and the AndroidManifest.xml file. If this were a command-line application,you would start looking for the class with the Main method. So what’s the equivalent of a Main method in Android?
Android defines an entry-point activity, also called the top-level activity. If you look in the AndroidManifest.xml file, you’ll find one provider and three activities. The NotesList activity defines an intent-filter for the action android.intent.action.MAIN and for the category android.intent.category.LAUNCHER. When an Android application is asked to run, the host loads the
application and reads the AndroidManifest.xml file. It then looks for, and starts, an activity or activities with an intent-filter that has the MAIN action with a category of LAUNCHER, as shown here:
After the host finds the activity it wants to run, it must resolve the defined activity to an actual class. It does this by combining the root package name and the activity name, which in this case is com.example.android.notepad.NotesList (see Listing 2-1).
Listing 2-1. The AndroidManfiest.xml File
package="com.example.android.notepad"
>
android:label="@string/app_name"
>
android:authorities="com.google.provider.NotePad"
/>
android:label="@string/title_notes_list">
android:name="android.intent.category.LAUNCHER" />
e="vnd.android.cursor.dir/vnd.google.note" />
android:mimeType="vnd.android.cursor.item/vnd.google.note" />
…
The application’s root package name is defined as an attribute of the element in the AndroidManifest.xml file, and each activity has a name attribute.
Once the entry-point activity is determined, the host starts the activity and the onCreate() method is called. Let’s have a look at NotesList.onCreate(), shown in Listing 2-2.
Listing 2-2. The onCreate Method
public class NotesList extends ListActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
Intent intent = getIntent();
if (intent.getData() == null) {
intent.setData(Notes.CONTENT_URI);
}
getListView().setOnCreateContextMenuListener(this);
Cursor cursor = managedQuery(getIntent().getData(),
PROJECTION, null, null,
Notes.DEFAULT_SORT_ORDER);
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
R.layout.noteslist_item, cursor, new String[] { Notes.TITLE },
new int[] { android.R.id.text1 });
setListAdapter(adapter);
}
}
Activities in Android are usually started with an intent, and one activity can start another activity. The onCreate() method checks whether the current activity’s intent has data (notes).If not, it sets the URI to retrieve the data on the intent. We’ll learn in Chapter 3 that Android accesses data through content providers that operate on URIs. In this case, the URI provides enough information to retrieve data from a database. The constant Notes.CONTENT_URI is defined as a static final in Notepad.java:
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/notes");
The Notes class is an inner class of the Notepad class. For now, know that the preceding
URI tells the content provider to get all of the notes. If the URI looked something like this
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/notes/11");
then the consuming content provider would return (update or delete) the note with an ID equal to 11. We will discuss content providers and URIs in depth in Chapter 3.
The NotesList class extends the ListActivity class, which knows how to display listoriented data. The items in the list are managed by an internal ListView (a UI component), which displays the notes in the list vertically (by default). After setting the URI on the activity’s intent, the activity registers to build the context menu for notes. If you’ve played with the application, you probably noticed that context-sensitive menu items are displayed depending on your selection. For example, if you select an existing note, the application displays “Edit note” and “Edit title.” Similarly, if you don’t select a note, the application shows you the “Add note” option.
Next, we see the activity execute a managed query and get a cursor for the result. A managed query means that Android will manage the returned cursor. In other words, if the application has to be unloaded or reloaded, neither the application nor the activity has to worry about positioning the cursor, loading it, or unloading it. The parameters to managedQuery(),shown in
Table 2-2, are interesting.
We will discuss managedQuery() and its sibling query() later in this section and also in Chapter 3. For now, realize that a query in Android returns tabular data. The projection parameter allows you to define the columns you are interested in. You can also reduce the overall result set and sort the result set using a SQL order-by clause (such as asc or desc).Also note that an
Android query must return a column named _ID to support retrieving an individual record. Moreover, you must know the type of data returned by the content provider—whether a column contains a string, int, binary, or the like.
After the query is executed, the returned cursor is passed to the constructor of SimpleCursorAdapter, which adapts records in the dataset to items in the user interface (ListView). Look closely at the parameters passed to the constructor of SimpleCursorAdapter:
SimpleCursorAdapter adapter =
new SimpleCursorAdapter(this, R.layout.noteslist_item,
cursor, new String[] { Notes.TITLE }, new int[] { android.R.id.text1 });
Specifically, look at the second parameter: an identifier to the view that represents the items in the ListView. As you’ll see in Chapter 3, Android provides an auto-generated utility class that provides references to the resources in your project. This utility class is called the R class because its name is R.java. When you compile your project, the AAPT generates the R class for you from the resources defined within your res folder. For example, you could put all your string resources into the values folder and the AAPT will generate a public static identifier for each string. Android supports this generically for all of your resources. For example,in the constructor of SimpleCursorAdapter, the NotesList activity passes in the identifier of the view that displays an item from the notes list. The benefit of this utility class is that you don’t have to hard-code your resources and you get compile-time reference checking. In other words, if a resource is deleted, the R class will lose the reference and any code referring to the resource will not compile.
Let’s look at another important concept in Android that we alluded to earlier: the onListItemClick method of NotesList (see Listing 2-3).
Listing 2-3. The onListItemClick Method
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Uri uri = ContentUris.withAppendedId(getIntent().getData(), id);
String action = getIntent().getAction();
if (Intent.ACTION_PICK.equals(action) ||
Intent.ACTION_GET_CONTENT.equals(action)) {
setResult(RESULT_OK, new Intent().setData(uri));
} else {
startActivity(new Intent(Intent.ACTION_EDIT, uri));
}
}
The onListItemClick method is called when a user selects a note in the UI. The method demonstrates that one activity can start another activity. When a note is selected, the method creates a URI by taking the base URI and appending the selected note’s ID to it. The URI is then passed to startActivity() with a new intent. startActivity() is one way to start an activity: it starts an activity but doesn’t report on the results of the activity after it completes. Another way to start an activity is to use startActivityForResult(). With this method, you can start another activity and register a callback to be used when the activity completes. For example,you’ll want to use startActivityForResult() to start an activity to select a contact because you want that contact after the activity completes.
At this point, you might be wondering about user interaction with respect to activities.For example, if the running activity starts another activity, and that activity starts an activity,and so on, then what activity can the user work with? Can she manipulate all the activities simultaneously, or is she restricted to a single activity? Actually, activities have a defined lifecycle. They’re maintained on an activity stack, with the running activity at the top. If the running activity starts another activity, the first running activity moves down the stack and the new activity moves to the top. Activities lower in the stack can be in a so-called “paused” or “stopped” state. A paused activity is partially or fully visible to the user; a stopped activity is not visible to the user. The system can kill paused or stopped activities if it deems that resources are needed elsewhere.
Let’s move on to data persistence now. The notes that a user creates are saved to an actual database on the device. Specifically, the Notepad application’s backing store is a SQLite database. The managedQuery() method that we discussed earlier eventually resolves to data in a database, via a content provider. Let’s examine how the URI, passed to managedQuery(), results in the execution of a query against a SQLite database. Recall that the URI passed to managedQuery() looks like this:
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/notes");
Content URIs always have this form: content://, followed by the authority, followed by a general segment (context-specific). Because the URI doesn’t contain the actual data, it somehow results in the execution of code that produces data. What is this connection?How is the URI reference resolved to code that produces data? Is the URI an HTTP service or a web service? Actually, the URI, or the authority portion of the URI, is configured in the AndroidManifest.xml file as a content provider:
android:authorities="com.google.provider.NotePad"/>
When Android sees a URI that needs to be resolved, it pulls out the authority portion of it and looks up the ContentProvider class configured for the authority. In the Notepad application, the AndroidManifest.xml file contains a class called NotePadProvider configured for the com.google.provider.NotePad authority. Listing 2-4 shows a small portion of the class.
Listing 2-4. The NotePadProvider Class
public class NotePadProvider extends ContentProvider
{
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs,String sortOrder) {}
@Override
public Uri insert(Uri uri, ContentValues initialValues) {}
@Override
public int update(Uri uri, ContentValues values, String where,
String[] whereArgs) {}
@Override
public int delete(Uri uri, String where, String[] whereArgs) {}
@Override
public String getType(Uri uri) {}
@Override
public boolean onCreate() {}
private static class DatabaseHelper extends SQLiteOpenHelper {}
@Override
public void onCreate(SQLiteDatabase db) {}
@Override
public void onUpgrade(SQLiteDatabase db,
int oldVersion, int newVersion) {
//...
}
}
}
Clearly, you can see that the NotePadProvider class extends the ContentProvider class. The ContentProvider class defines six abstract methods, four of which are CRUD (Create, Read, Update, Delete) operations. The other two abstract methods are onCreate() and getType(). onCreate() is called when the content provider is created for the first time. getType() provides
the MIME type for the result set (you’ll see how MIME types work when you read Chapter 3).The other interesting thing about the NotePadProvider class is the internal DatabaseHelper class, which extends the SQLiteOpenHelper class. Together, the two classes take care of initializing the Notepad database, opening and closing it, and performing other database tasks.
Interestingly, the DatabaseHelper class is just a few lines of custom code (see Listing 2-5), while the Android implementation of SQLiteOpenHelper does most of the heavy lifting.
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + NOTES_TABLE_NAME + " ("
+ Notes._ID + " INTEGER PRIMARY KEY,"
+ Notes.TITLE + " TEXT,"
+ Notes.NOTE + " TEXT,"
+ Notes.CREATED_DATE + " INTEGER,"
+ Notes.MODIFIED_DATE + " INTEGER"
+ ");");
}
//…
}
As shown in Listing 2-5, the onCreate() method creates the Notepad table. Notice that the class’s constructor calls the superclass’s constructor with the name of the table. The superclass will call the onCreate() method only if the table does not exist in the database. Also notice that one of the columns in the Notepad table is the _ID column we discussed in the section “Dissecting the Application.” Now let’s look at one of the CRUD operations: the insert() method (see Listing 2-6).
Listing 2-6. The insert() Method
//…
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowId = db.insert(NOTES_TABLE_NAME, Notes.NOTE, values);
if (rowId > 0) {
Uri noteUri = ContentUris.withAppendedId(
NotePad.Notes.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(noteUri, null);
return noteUri;
}
The insert() method uses its internal DatabaseHelper instance to access the database and then inserts a notes record. The returned row ID is then appended to the URI and a new URI is returned to the caller.
At this point, you should be familiar with how an Android application is laid out. You should be able to navigate your way around Notepad, as well as some of the other samples in the Android SDK. You should be able to run the samples and play with them. Now let’s look at the overall lifecycle of an Android application.
As you can see from Table 2-1, an Android application is primarily made up of three pieces: the application descriptor, a collection of various resources, and the application’s source code. If you put aside the AndroidManifest.xml file for a moment, you can view an Android app in this simple way: you have some business logic implemented in code, and everything else is a resource. This basic structure resembles the basic structure of a J2EE app, where the resources correlate to JSPs, the business logic correlates to servlets, and the AndroidManifest.xml file correlates to the web.xml file.
You can also compare J2EE’s development model to Android’s development model. In J2EE, the philosophy of building views is to build them using markup language. Android has also adopted this approach, although the markup in Android is XML. You benefit from this approach because you don’t have to hard-code your application’s views; you can modify the look and feel of the application by editing the markup.
It is also worth noting a few constraints regarding resources. First, Android supports only a linear list of files within the predefined folders under res. For example, it does not support nested folders under the layout folder (or the other folders under res). Second, there are some similarities between the assets folder and the raw folder under res. Both folders can contain raw files, but the files within raw are considered resources and the files within assets are not.So the files within raw will be localized, accessible through resource IDs, and so on. But the contents of the assets folder are considered general-purpose contents, to be used without resource constraints and support. Note that because the contents of the assets folder are not considered resources, you can put an arbitrary hierarchy of folders and files within it. (We’ll
talk a lot more about resources in Chapter 3.)
Analyzing the Notepad Application
Not only do you know how to create a new Android application and run it in the emulator, but you also have a feel for the artifacts of an Android application. Next, we are going to look at the Notepad application that ships with the Android SDK. Notepad’s complexity falls between that of the “Hello World!” app and a full-blown Android application, so analyzing its components will give you some realistic insight into Android development.
Loading and Running the Notepad Application
In this section, we’ll show you how to load the Notepad application into the Eclipse IDE and run it in the emulator. Before we start, you should know that the Notepad application implements several use cases. For example, the user can create a new note, edit an existing note, delete a note,view the list of created notes, and so on. When the user launches the application, there aren’t any saved notes yet, so the user sees an empty note list. If the user presses the Menu key, the application presents him with a list of actions, one of which allows him to add a new note. After he adds the note, he can edit or delete the note by selecting the corresponding menu option.
Follow these steps to load the Notepad sample into the Eclipse IDE:
1. Start Eclipse.
2. Go to File ? New ? Project.
3. In the “New Project” dialog, select Android ? Android Project.
4. In the “New Android Project” dialog, select “Create project from existing source” and set the “Location” field to the path of the Notepad application. Note that the Notepad application is located in c:\AndroidSDK\samples\, which you downloaded earlier. After you set the path, the dialog reads the AndroidManifest.xml file and prepopulates the remaining fields in the “New Android Project” dialog box.
5. Click the “Finish” button.
You should now see the NotesList application in your Eclipse IDE. To run the application, you could create a launch configuration (as you did for the “Hello World!” application), or you can simply right-click the project, choose Run As, and select Android Application. This will launch the emulator and install the application on it. After the emulator has completed loading (you’ll see the date and time displayed in the center of the emulator’s screen), press the Menu button to view the Notepad application. Play around with the application for a few minutes to become familiar with it.
Dissecting the Application
As you can see, the application contains several .java files, a few .png images, three views (under the layout folder), and the AndroidManifest.xml file. If this were a command-line application,you would start looking for the class with the Main method. So what’s the equivalent of a Main method in Android?
Android defines an entry-point activity, also called the top-level activity. If you look in the AndroidManifest.xml file, you’ll find one provider and three activities. The NotesList activity defines an intent-filter for the action android.intent.action.MAIN and for the category android.intent.category.LAUNCHER. When an Android application is asked to run, the host loads the
application and reads the AndroidManifest.xml file. It then looks for, and starts, an activity or activities with an intent-filter that has the MAIN action with a category of LAUNCHER, as shown here:
After the host finds the activity it wants to run, it must resolve the defined activity to an actual class. It does this by combining the root package name and the activity name, which in this case is com.example.android.notepad.NotesList (see Listing 2-1).
Listing 2-1. The AndroidManfiest.xml File
>
>
/>
android:mimeType="vnd.android.cursor.item/vnd.google.note" />
…
The application’s root package name is defined as an attribute of the
Once the entry-point activity is determined, the host starts the activity and the onCreate() method is called. Let’s have a look at NotesList.onCreate(), shown in Listing 2-2.
Listing 2-2. The onCreate Method
public class NotesList extends ListActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
Intent intent = getIntent();
if (intent.getData() == null) {
intent.setData(Notes.CONTENT_URI);
}
getListView().setOnCreateContextMenuListener(this);
Cursor cursor = managedQuery(getIntent().getData(),
PROJECTION, null, null,
Notes.DEFAULT_SORT_ORDER);
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
R.layout.noteslist_item, cursor, new String[] { Notes.TITLE },
new int[] { android.R.id.text1 });
setListAdapter(adapter);
}
}
Activities in Android are usually started with an intent, and one activity can start another activity. The onCreate() method checks whether the current activity’s intent has data (notes).If not, it sets the URI to retrieve the data on the intent. We’ll learn in Chapter 3 that Android accesses data through content providers that operate on URIs. In this case, the URI provides enough information to retrieve data from a database. The constant Notes.CONTENT_URI is defined as a static final in Notepad.java:
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/notes");
The Notes class is an inner class of the Notepad class. For now, know that the preceding
URI tells the content provider to get all of the notes. If the URI looked something like this
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/notes/11");
then the consuming content provider would return (update or delete) the note with an ID equal to 11. We will discuss content providers and URIs in depth in Chapter 3.
The NotesList class extends the ListActivity class, which knows how to display listoriented data. The items in the list are managed by an internal ListView (a UI component), which displays the notes in the list vertically (by default). After setting the URI on the activity’s intent, the activity registers to build the context menu for notes. If you’ve played with the application, you probably noticed that context-sensitive menu items are displayed depending on your selection. For example, if you select an existing note, the application displays “Edit note” and “Edit title.” Similarly, if you don’t select a note, the application shows you the “Add note” option.
Next, we see the activity execute a managed query and get a cursor for the result. A managed query means that Android will manage the returned cursor. In other words, if the application has to be unloaded or reloaded, neither the application nor the activity has to worry about positioning the cursor, loading it, or unloading it. The parameters to managedQuery(),shown in
Table 2-2, are interesting.
We will discuss managedQuery() and its sibling query() later in this section and also in Chapter 3. For now, realize that a query in Android returns tabular data. The projection parameter allows you to define the columns you are interested in. You can also reduce the overall result set and sort the result set using a SQL order-by clause (such as asc or desc).Also note that an
Android query must return a column named _ID to support retrieving an individual record. Moreover, you must know the type of data returned by the content provider—whether a column contains a string, int, binary, or the like.
After the query is executed, the returned cursor is passed to the constructor of SimpleCursorAdapter, which adapts records in the dataset to items in the user interface (ListView). Look closely at the parameters passed to the constructor of SimpleCursorAdapter:
SimpleCursorAdapter adapter =
new SimpleCursorAdapter(this, R.layout.noteslist_item,
cursor, new String[] { Notes.TITLE }, new int[] { android.R.id.text1 });
Specifically, look at the second parameter: an identifier to the view that represents the items in the ListView. As you’ll see in Chapter 3, Android provides an auto-generated utility class that provides references to the resources in your project. This utility class is called the R class because its name is R.java. When you compile your project, the AAPT generates the R class for you from the resources defined within your res folder. For example, you could put all your string resources into the values folder and the AAPT will generate a public static identifier for each string. Android supports this generically for all of your resources. For example,in the constructor of SimpleCursorAdapter, the NotesList activity passes in the identifier of the view that displays an item from the notes list. The benefit of this utility class is that you don’t have to hard-code your resources and you get compile-time reference checking. In other words, if a resource is deleted, the R class will lose the reference and any code referring to the resource will not compile.
Let’s look at another important concept in Android that we alluded to earlier: the onListItemClick method of NotesList (see Listing 2-3).
Listing 2-3. The onListItemClick Method
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Uri uri = ContentUris.withAppendedId(getIntent().getData(), id);
String action = getIntent().getAction();
if (Intent.ACTION_PICK.equals(action) ||
Intent.ACTION_GET_CONTENT.equals(action)) {
setResult(RESULT_OK, new Intent().setData(uri));
} else {
startActivity(new Intent(Intent.ACTION_EDIT, uri));
}
}
The onListItemClick method is called when a user selects a note in the UI. The method demonstrates that one activity can start another activity. When a note is selected, the method creates a URI by taking the base URI and appending the selected note’s ID to it. The URI is then passed to startActivity() with a new intent. startActivity() is one way to start an activity: it starts an activity but doesn’t report on the results of the activity after it completes. Another way to start an activity is to use startActivityForResult(). With this method, you can start another activity and register a callback to be used when the activity completes. For example,you’ll want to use startActivityForResult() to start an activity to select a contact because you want that contact after the activity completes.
At this point, you might be wondering about user interaction with respect to activities.For example, if the running activity starts another activity, and that activity starts an activity,and so on, then what activity can the user work with? Can she manipulate all the activities simultaneously, or is she restricted to a single activity? Actually, activities have a defined lifecycle. They’re maintained on an activity stack, with the running activity at the top. If the running activity starts another activity, the first running activity moves down the stack and the new activity moves to the top. Activities lower in the stack can be in a so-called “paused” or “stopped” state. A paused activity is partially or fully visible to the user; a stopped activity is not visible to the user. The system can kill paused or stopped activities if it deems that resources are needed elsewhere.
Let’s move on to data persistence now. The notes that a user creates are saved to an actual database on the device. Specifically, the Notepad application’s backing store is a SQLite database. The managedQuery() method that we discussed earlier eventually resolves to data in a database, via a content provider. Let’s examine how the URI, passed to managedQuery(), results in the execution of a query against a SQLite database. Recall that the URI passed to managedQuery() looks like this:
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/notes");
Content URIs always have this form: content://, followed by the authority, followed by a general segment (context-specific). Because the URI doesn’t contain the actual data, it somehow results in the execution of code that produces data. What is this connection?How is the URI reference resolved to code that produces data? Is the URI an HTTP service or a web service? Actually, the URI, or the authority portion of the URI, is configured in the AndroidManifest.xml file as a content provider:
When Android sees a URI that needs to be resolved, it pulls out the authority portion of it and looks up the ContentProvider class configured for the authority. In the Notepad application, the AndroidManifest.xml file contains a class called NotePadProvider configured for the com.google.provider.NotePad authority. Listing 2-4 shows a small portion of the class.
Listing 2-4. The NotePadProvider Class
public class NotePadProvider extends ContentProvider
{
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs,String sortOrder) {}
@Override
public Uri insert(Uri uri, ContentValues initialValues) {}
@Override
public int update(Uri uri, ContentValues values, String where,
String[] whereArgs) {}
@Override
public int delete(Uri uri, String where, String[] whereArgs) {}
@Override
public String getType(Uri uri) {}
@Override
public boolean onCreate() {}
private static class DatabaseHelper extends SQLiteOpenHelper {}
@Override
public void onCreate(SQLiteDatabase db) {}
@Override
public void onUpgrade(SQLiteDatabase db,
int oldVersion, int newVersion) {
//...
}
}
}
Clearly, you can see that the NotePadProvider class extends the ContentProvider class. The ContentProvider class defines six abstract methods, four of which are CRUD (Create, Read, Update, Delete) operations. The other two abstract methods are onCreate() and getType(). onCreate() is called when the content provider is created for the first time. getType() provides
the MIME type for the result set (you’ll see how MIME types work when you read Chapter 3).The other interesting thing about the NotePadProvider class is the internal DatabaseHelper class, which extends the SQLiteOpenHelper class. Together, the two classes take care of initializing the Notepad database, opening and closing it, and performing other database tasks.
Interestingly, the DatabaseHelper class is just a few lines of custom code (see Listing 2-5), while the Android implementation of SQLiteOpenHelper does most of the heavy lifting.
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + NOTES_TABLE_NAME + " ("
+ Notes._ID + " INTEGER PRIMARY KEY,"
+ Notes.TITLE + " TEXT,"
+ Notes.NOTE + " TEXT,"
+ Notes.CREATED_DATE + " INTEGER,"
+ Notes.MODIFIED_DATE + " INTEGER"
+ ");");
}
//…
}
As shown in Listing 2-5, the onCreate() method creates the Notepad table. Notice that the class’s constructor calls the superclass’s constructor with the name of the table. The superclass will call the onCreate() method only if the table does not exist in the database. Also notice that one of the columns in the Notepad table is the _ID column we discussed in the section “Dissecting the Application.” Now let’s look at one of the CRUD operations: the insert() method (see Listing 2-6).
Listing 2-6. The insert() Method
//…
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowId = db.insert(NOTES_TABLE_NAME, Notes.NOTE, values);
if (rowId > 0) {
Uri noteUri = ContentUris.withAppendedId(
NotePad.Notes.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(noteUri, null);
return noteUri;
}
The insert() method uses its internal DatabaseHelper instance to access the database and then inserts a notes record. The returned row ID is then appended to the URI and a new URI is returned to the caller.
At this point, you should be familiar with how an Android application is laid out. You should be able to navigate your way around Notepad, as well as some of the other samples in the Android SDK. You should be able to run the samples and play with them. Now let’s look at the overall lifecycle of an Android application.
Published in
Java
Recent Posts
-
Compiled and Noncompiled Android Resources
-
Using resources, Content Providers, and Intents
-
Examining the Application Lifecycle
-
Exploring the Structure of an Android Application
-
Learning the Fundamental Components
-
Getting Your Feet Wet
-
Android Service Components
-
Developing an End-User Application with the Android SDK
-
Introducing the Android Computing Platform
-
Setting up a Successful Website