Professional Documents
Culture Documents
COMPILED BY FASTOP
Welcome to Android!
The Android platform is a software stack for mobile devices including an operating syst em, middleware and key applications. Developers can create applications for the platform using the An droid SDK. Applications are written using the Java programming language and run on Dalvik, a custom virtual machine designed for embedded use which runs on top of a Linux kernel. If you want to know how to develop applications for Android , you're in the right place. This site provides a variety of documentation that will help you learn about Android and develop mobile applications for the platform. An early look at the the Android SDK is also available. It includes sample projects with source code, development tools, an emulator, and of course all the libraries you'll need to bui ld an Android application. Download the SDK
To start learning about the Android platform, please read the documentation in the following order: What is Android? An overview of the Android platform Getting Started All the basics, including Hello World Developing Applications The nuts and bolts of Android applications Developer Toolbox In-depth information on specific topics Reference Information A myriad of reference material Sample Code Several sample Android applications for your viewing pleasure Frequently Asked Questions Common issues and questions
What is Android?
Android is a software stack for mobile devices that includes an operating system, middleware and key applications. This early look at the Android SDK provides the tools and APIs necessary to begin developing applications on the Android platform using the Java programming language.
Features
Application framework enabling reuse and replacement of components Dalvik virtual machine optimized for mobile devices Integrated browser based on the open source WebKit engine Optimized graphics powered by a custom 2D graphics library; 3D graphics based on the OpenGL ES 1.0 specification (hardware acceleration optional) SQLite for structured data storage Media support for common audio, video, and still image formats (MPEG4, H.264, MP3, AAC, AMR, JPG, PNG, GIF) GSM Telephony (hardware dependent) Bluetooth, EDGE, 3G, and WiFi (hardware dependent) Camera, GPS, compass, and accelerometer (hardware dependent) Rich development environment including a device emulator, tools for debugging, memory and performance profiling, and a plugin for the Eclipse IDE
Android Architecture
The following diagram shows the major components of the Androi d operating system. Each section is described in more detail below.
Applications
Android will ship with a set of core applications including an email client, SMS program, calendar, maps, browser, contacts, and others. All applications are written using the Java programming language.
Application Framework
Developers have full access to the same framework APIs used by the core applications. The application architecture is designed to simplify the reuse of components; any application can publish its capabilities and any other application may then make use of those capabilities (subj ect to security constraints enforced by the framework). This same mechanism allows components to be replaced by th e user. Underlying all applications is a set of services and systems, includ ing: A rich and extensible set of Views that can be used to build an application, including li sts, grids, text boxes, buttons, and even an embeddable web browser Content Providers that enable applications to access data from other applications (such as Contacts), or to share their own data A Resource Manager, providing access to non-code resources such as localized strings, graph ics, and layout files A Notification Manager that enables all applications to display custom alerts in the status bar An Activity Manager that manages the lifecycle of applications and provides a commo n navigation backstack For more details and a walkthrough of an application, see Writing an Android Application .
Libraries
Android includes a set of C/C++ libraries used by various components of the Android system. These capabilities are exposed to developers through the Android application frame work. Some of the core libraries are listed below: System C library - a BSD-derived implementation of the standard C system library (libc), tuned for embedded Linux-based devices Media Libraries - based on PacketVideo's OpenCORE; the libraries support playback and recording of many popular audio and video formats, as well as static image f iles, including MPEG4, H.264, MP3, AAC, AMR, JPG, and PNG Surface Manager - manages access to the display subsystem and seamlessly composites 2D an d 3D graphic layers from multiple applications LibWebCore - a modern web browser engine which powers both the Android b rowser and an embeddable web view SGL - the underlying 2D graphics engine 3D libraries - an implementation based on OpenGL ES 1.0 APIs; the libraries use either hardware 3D acceleration (where available) or the included, highly optimize d 3D software rasterizer FreeType - bitmap and vector font rendering SQLite - a powerful and lightweight relational database engin e available to all applications
Android Runtime
Android includes a set of core libraries that provides most of the functionality available in the core libraries of the Java programming language. Every Android application runs in its own process, with its own in stance of the Dalvik virtual machine. Dalvik has been written so that a device can run multiple VMs efficiently. The Dalvik VM executes files in the Dalvik Executable (.dex) format which is optimized for minimal memory footprint. The VM is register-based, and runs classes compiled by a Java language compiler that have been transf ormed into the .dex format by the included "dx" tool. The Dalvik VM relies on the Linux kernel for underlying function ality such as threading and low-level memory management.
Linux Kernel
Android relies on Linux version 2.6 for core system services such as security, memory management, process management, network stack, and driver model. The kernel also acts as an abstraction layer between the hardware and the rest of the software stack.
Core Packages
These are the basic packages that make up the Android SDK for writing applications. The packages are organized as layers, listed here from lowest-level to highest. android.util contains various low-level utility classes, such as specialized contai ner classes, XML utilities, etc. android.os provides basic operating system services, message passing, and inter-process communication.
Compiled by fastop 2007-2 http://fastoponandroid.googlepages.com
android.graphics is the core rendering package. android.text, android.text.method, android.text.style, and android.text.util supply a rich set of text processing tools, supporting rich text, input methods, etc. android.database contains low-level APIs for working with databases. android.content provides various services for accessing data on the device: applicatio ns installed on the device and their associated resources, and content providers for persistent dynamic data. android.view is the core user-interface framework. android.widget supplies standard user interface elements (lists, buttons, layout managers, etc) built from the view package. android.app provides the high-level application model, implemented using Activities.
Hello, Android!
First impressions matter, and as a developer you know that the first impression you get of a development framework is how easy it is to write "Hello, World!" Well, in Android, it's pretty easy. Here's how it looks: Create the Project Construct the UI Run the Code: Hello, Android The sections below spell it all out in detail. Upgrading the UI to an XML Layout Debugging Your Project Creating a Project without Eclipse Let's jump in!
2. Fill out the project details The next screen allows you to enter the relevant details for your project. Here's an example:
This is the name of the directory or folder on your computer that you want to contain the project. This is the package namespace similar to the Java programming lang uage that you want all your source code to reside under. This also sets the package name under which the stub Activity will be generated. The package name you use in your application must be unique a cross all packages installed on the system; for this reason, it's very important to use a standard domain-style package for your applications. In the example above, we used the package domain "com.google.android"; you should use a different one appropriate to your organization.
Activity Name
This is the name for the class stub that will be generated by the plugin. This will be a subclass of Android's Activity class. An Activity is simply a class that can run and do work. It can create a UI if it chooses, but it doesn't need to. This is the human-readable title for your application.
Application Name
The checkbox for toggling "Use default location" allows you to change the location on disk where the project's files will be generated and stored.
3. Edit the auto-generated source code After the plugin runs, you'll have a class named HelloAndroid t hat looks like this: public class HelloAndroid extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.main); } } The next step is to start modifying it!
Construct the UI
Once you've got the project set up, the obvious next step is to get some text up there on the screen. Here's the finished product next we'll dissect it line by line: public class HelloAndroid extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); TextView tv = new TextView(this); tv.setText("Hello, Android"); setContentView(tv); } } Note that you should also add import android.widget.TextView; to the import statements in order for this example to compile. In Android, user interfaces are composed of hierarchies of classes called Views. A View is simply a drawable object, such as a radio button, an animation, or (in our case) a text label. The specific name for the View subclass that handles text is simply TextView. Here's how you construct a TextView: TextView tv = new TextView(this); The argument to TextView's constructor is an Android Context instance. The Context is simply a handle to the system; it provides services like resolving resources, obtaining access to databases and preferences, and so on. The Activity class inherits from Context. Since our HelloAndroid class is a subclass of Activity, it is also a Context, and so we can pass the 'this' reference to the TextView.
Once we've constructed the TextView, we need to tell it what to display: tv.setText("Hello, Android"); Nothing too surprising there. At this point, we've constructed a TextView and told it what text to display. The final step is to connect this TextView with the on-screen display, like so: setContentView(tv); The setContentView() method on Activity indicates to the system which View should be associated with the Activity's UI. If an Activity doesn't call this method, no UI is present at all and the system will display a blank screen. For our purposes, all we want is to display some text, so we pass it the TextView we just created. There it is "Hello, World" in Android! The next step, of course, is to see it running.
Next, highlight the "Android Application" entry, and then press the icon in the top left corner (the one depicting a sheet of paper with a plus sign in the corner) or simply double-click the "Android Application" entry. You should have a new launcher entry named "New_configura tion".
Change the name to something expressive, like "Hello, Android", and then pick your project by pressing the Browse button. (If you have more than one Android project open in Eclipse, be sure to pick the right one.) The plugin will automatically scan your project for Activity subclasses, and add each one it finds to the drop-down list under the "Activity:" label. Since your "Hello, Android" project only has one, it will be the default, and you can simply continue. Press the "Apply" button. Here's an example:
That's it you're done! Press the Run button, and the Android Emulator should start. Once it's booted up your application will appear. When all is said and done, you should see something like this:
10
That's "Hello, World" in Android. Pretty straightforward, eh? The next sections of the tutorial offer more detailed information that you may find valuable as you learn more about Android.
11
In this example, there are also four XML attributes. Here's a summary of what they mean: Attribute xmlns:android Meaning This is an XML namespace declaration that tells the Android tools that you are going to refer to common attributes defined in the Android namespace. The outermost tag in every Android layout file must have this attribute. This attribute defines how much of the available width on the screen this View should consume. In this case, it's our only View so we want it to take up the entire screen, which is what a value of "fill_parent" means. This is just like android:layout_width, except that it refers to available screen height. This sets the text that the TextView should contain. In this example, it's our usual "Hello, Android" message.
android:layout_width
android:layout_height
android:text
So, that's what the XML layout looks like, but where do you put it? Under the res/ directory in your project. The "res" is short for "resources" and that directory contains all the non-code assets that your application requires. This includes things like images, localized strings, and XML layout files. The Eclipse plugin creates one of these XML files for you. In our example above, we simply never used it. In the Package Explorer, expand the folder res/layout, and edit the file main.xml. Replace its contents with the text above and save your changes. Now open the file named R.java in your source code folder in the Package Explorer. You'll see that it now looks something like this: public final class R { public static final class attr { }; public static final class drawable { public static final int icon=0x7f020000; }; public static final class layout { public static final int main=0x7f030000; }; public static final class string { public static final int app_name=0x7f040000 ; }; }; A project's R.java file is an index into all the resources defined in the file. You use this class in your source code as a sort of short-hand way to refer to resources you've included in your project. This is particularly powerful with the code-completion features of IDEs like Eclipse because it lets you quickly and interactively locate the specific reference you're looking for. The important thing to notice for now is the inner class named "layout", and its member field "main". The Eclipse plugin noticed that you added a new XML layout file and then regenerated this R.java file. As you add other resources to your projects you'll see R.java change to keep up.
Compiled by fastop 2007-2 http://fastoponandroid.googlepages.com
12
The last thing you need to do is modify your HelloAndroid source code to use the new XML version of your UI, instead of the hard-coded version. Here's what your new class will look like. As you can see, the source code becomes much simpler: public class HelloAndroid extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.main); } } When you make this change, don't just copy-and-paste it in. Try out the code-completion feature on that R class. You'll probably find that it helps a lot. Now that you've made this change, go ahead and re-run your application all you need to do is press the green Run arrow icon, or select Run > Run Last Launched from the menu. You should see.... well, exactly the same thing you saw before! After all, the point was to show that the two different layout approaches produce identical results. There's a lot more to creating these XML layouts, but that's as far as we'll go here. Read the Implementing a User Interface documentation for more information on the power of this approach.
13
To find out what went wrong, set a breakpoint in your source code on the line "Object o = null;" (You can do this by double-clicking on the region to the left of the line number in Eclipse.) Then select Run > Debug Last Launched from the menu to enter debug mode. Your app will restart in the emulator, but this time it will suspend when it reaches the breakpoint you set. You can then step through the code in Eclipse's Debug Perspective, just as you would for any other application.
14
Activity
Activities are the most common of the four Android building blocks. An activity is usually a single screen in your application. Each activity is implemented as a single class that extends the Activity base class. Your class will display a user interface composed of Views and respond to events. Most applications consist of multiple screens. For example, a text messaging application might have one screen that shows a list of contacts to send messages to, a second screen to write the message to the chosen contact, and other screens to review old messages or change settings. Each of these screens would be implemented as an activity. Moving to another screen is accomplished by a starting a new activity. In some cases an activity may return a value to the previous activity -- for example an activity that lets the user pick a photo would return the chosen photo to the caller. When a new screen opens, the previous screen is paused and put onto a history stack. The user can navigate backward through previously opened screens in the history. Screens can also cho ose to be removed from the history stack when it would be inappropriate for them to remain. Android retains hi story stacks for each application launched from the home screen.
15
Intent Receiver
You can use an IntentReceiver when you want code in your application to execute in reacti on to an external event, for example, when the phone rings, or when the data network is available, or when it's midnight. Intent receivers do not display a UI, although they may use the NotificationManager to alert the user if something interesting has happened. Intent receivers are registered in AndroidManifest.xml, but you can also register them from code using Context.registerReceiver(). Your application does not have to be running for its intent receivers to be called; the system will start your application, if necessary, when an intent receiver is triggered. Applications can a lso send their own intent broadcasts to others with Context.broadcastIntent().
Service
A Service is code that is long-lived and runs without a UI. A good example of this is a media player playing songs from a play list. In a media player application, there would probably b e one or more activities that allow the user to choose songs and start playing them. However, the music playback itself should not be handled by an activity because the user will expect the music to keep playing even after navigating to a new screen. In this case, the media player activity could start a service using Context.startService() to run in the background to keep the music going. The system will then keep the music playback service running until it has finished. (You can learn more about the priority given to services in the system by reading Lifecycle of an Android Application .) Note that you can connect to a service (and start it if it's not already running) with the Context.bindService() method. When connected to a service, you can communicate with it through an interface exposed by the service. For the music service, this might allow you to pause, rewind, etc.
Content Provider
Applications can store their data in files, an SQLite database, or any other mechanism that makes sense. A content provider, however, is useful if you want your application's data to be shared with other applications. A content provider is a class that implements a standard set of methods to let other applications store and retrieve the type of data that is handled by that content provider. To get more details on content providers, see Accessing Content Providers .
16
Contents
Who Should Use this Tutorial Preparing for the Exercises Exercises Other Resources and Further Learning
17
Exercises
The table below lists the tutorial exercises and describes the devel opment areas that each covers. Each exercise assumes that you have completed the previous example (if any). Exercise 1 Construct a simple notes list that lets the user add new notes but not edit them. Demonstrates the basics of ListActivities and creating and handling menu options. Uses a SQLite database to store the notes. Add a second activity to the application. Demonstrates constructing a new Activity, adding it to the Android manifest, passing data between the activities, and using more advanced screen layout. Also shows how t o invoke another activity asynchronously using startSubActivity(). Add handling of life-cycle events to the application, to let it maintain application state across the life cycle. Demonstrates how to use the Eclipse debugger and how you can u se it to view lifecycle events as they are generated. This section is optional but highly recommended.
Exercise 2
Exercise 3
Extra Credit
Step 1
Open up the Notepadv1 project in Eclipse. Notepadv1 is a project that is provided as a starting point - it takes care of some of the boilerplate work that you have already seen if you followed the Hello Android tutorial. a. Right click in the Package Explorer, select Import.../General/Existing Projects into Workspace. b. Hit the browse button, and navigate to where you copied the three exercise folders, select Notepadv1 and hit OK. c. You should see Notepadv1 listed in the projects with a check mark next to it. d. Hit Finish. e. The exercise project should be open and ready to go in your Ecli pse package explorer. f. If you see an error about AndroidManifest.xml, or some problems related to an Android zip file, right click on the project and select Android Tools->Fix Project Properties from the popup menu (the project is looking in the wrong location for the library file, this will fix it for you).
Compiled by fastop 2007-2 http://fastoponandroid.googlepages.com
18
Step 2
Take a look at the DBHelper class this class is provided to encapsulate data access to a SQLite database that will hold our notes data and allow us to update it.
Accessing and modifying data For this exercise, we are just going to use a SQLite database directly to store our
Typically you would implement this using a ContentProvider, and in fact the full data, but in a real application it would be Notepad application included with the SDK does implement su ch a much better to write a proper ContentProvider. However, there is no reason you can't just use your own SQLite ContentProvider to encapsulate this behavior instead. database directly as we do here. The main thing to notice about this class is that it takes care of the details of storing, retrieving and updating data in the SQLite If you are interested, you can find out more about content providers or the database for us. There are methods for retrieving all the rows, retrieving a row whole subject of Storing, Retrieving, and based on rowIds, creating a new row, deleting an existing ro w and updating a row. Exposing Data. If you want a quick primer in how to use the SQLite Database for your own applications, either take a look at this class or better yet, look at the full Notepad application included with the SDK in the samples/ folder as the sample uses a ContentProvider.
Step 3
Open the notepad_list.xml file in res/layout and take a look at it: This is a layout definition file with a default starting point in it, we have provided this as a convenience to get you going quickly. a. All Android layout files must start with the XML header line: <?xml version="1.0" encoding="utf-8"?>. b. Also, the next definition will often (but not always) be a layout definition of some kind, in this case a LinearLayout. c. Note also that the xml namespace of Android should always be d efined in the top level component or layout in the XML so that android: tags can be used through the rest of the file: xmlns:android="http://schemas.android.com/apk/res/a ndroid"
Layouts and activities Most Activities will have a layout associated with them. The layout will be the "face" of the activity to the user. In this case our layout will take over the whole screen and provide a list of notes. Full screen layouts are not the only option for an Activity however. You might also want to use a floating layout (for example, a dialog or alert), or perhaps you don't need a layout at all (the activity will be invisible to the user unless you specify some kind of layout for it to use).
Step 4
We need to create the layout to hold our list. Add code inside of the LinearLayout tag so the whole file looks like this: (you may have to hit the Source tab in order to edit the XML file) <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android .com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content"> <ListView id="@id/android:list" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView id="@id/android:empty" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/no_notes"/> </LinearLayout>
a. The ListView and TextView can be thought as two alternative views, only one of which will be displayed at once. ListView will be used when there are notes to be shown, wh ile the TextView (which has a default value of "No Notes Yet!" defined as a string resource, will be displayed if there aren't any notes to display). b. The @ in the id strings of the ListView and TextView means that the XML parser should parse and expand the rest of the id string and use an ID resource.
19
c. And, the android:list and android:empty are IDs that are already provided for us by the Android platform, empty is used automatically when no data is provided in the l ist adapter. The List Adapter knows to look for these names specifically by default. Alternatively you could also choose to change the default empty view used by the List Adapter by using the setEmptyView(). More broadly, the android.R class is a set of predefined resources provided for you by the platform, while your project's R class is the set of resources your project has defined. Resources found in the android.R resource class can be used in the XML files by using the android: name space prefix (as we see here).
Step 5
To make a list view, we also need to define a view for each row in the list: a. Create a new file under res/layout called notes_row.xml. b. Add the following contents (note: again the xml header is used, and the first node defines the Android xml namespace) <?xml version="1.0" encoding="utf-8"?> <TextView id="@+id/text1" xmlns:android="http://schemas.android.com/apk/res/a ndroid" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
Resources and the R class The folders under res/ in the Eclipse project are special. There is a specific structure to the folders and files under this folder. In particular, resources defined in these folders and files will have corresponding entries in the R class allowing them to be easily accessed and used from your application. Furthermore, they will be bundled and deployed as part of the application.
c. This is the view that will be used for each .notes title row it has only one text field in it. d. In this case we create a new id called text1. The + after the @ in the id string indicates that the id should be automatically created if it does not already exist, so we are defining text1 on the fly and then using it. e. After saving this file, open the R.java class in the project and look at it, you should see new definitions for notes_row and text1 (our new definitions) meaning we can now gain access to these from the our code.
Step 6
Next, open the Notepadv1 class in the source. We are going to alter this class to become a list adapter and display our notes, and also allow us to add new notes Notepadv1 will be a subclass of Activity called a ListActivity, which has extra functionality to accommodate the kinds of things you might want to do with a list, for example: displaying an arbitrary number of list items in rows on the screen, moving through the list items, and allowing them to be selected. Take a look through the existing code in Notepadv1 class. There are some constant definitions at the top, followed by a private field we will use to create numbered note titles, and some overrides of methods from the superclass.
Step 7
Change the inheritance of Notepadv1 from Activity to ListActivity: public class Notepadv1 extends ListActivity Note: you will have to import ListActivity into the Notepadv1 class using Eclipse, ctrl-shift-O on Windows or Linux, or cmd-shift-O on the Mac (organize imports) will do this for you.
Step 8
There are already three override methods defined: onCreate, onCreateOptionsMenu and onOptionsItemSelected, we need to fill these out: onCreate() is called when the activity is started it is a little like the "main" method for the activity. We use this to set up resources and state for the activity when it is running onCreateOptionsMenu() is used to populate the menu for the activity. This is shown when the user hits the menu button, and has a list of options they can select (like "Create Note") onOptionsItemSelected() is the other half of the menu equation, it is used to handle events generated from the menu (e.g. when the user selects the "Create Note" item).
Compiled by fastop 2007-2 http://fastoponandroid.googlepages.com
20
Step 9
Fill out the body of the onCreate() method. Here we will set the title for the activity (shown at the top of the screen), use the notepad_list layout we have created for the activity display contents, set up the DBHelper instance we will use to access notes data, then populate the list with the available note titles: a. call super() with the icicle parameter passed into our method b. setContentView to R.layout.notepad_list c. Create a new private class field called dbHelper of class DBHelper (before the onCreate method) d. Back in the onCreate method, construct a DBHelper instance assign to the dbHelper field (note, you must pass this into the constructor for DBHelper) e. Finally, call a new method -fillData()- gets the data and populates it using the helper, we haven't defined it yet f. onCreate() should now look like this: @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.notepad_list); dbHelper = new DBHelper(this); fillData(); } And remember to add the DBHelper field definition (right under the noteNumber definition ): private DBHelper dbHelper;
Step 10
Fill out the body of the onCreateOptionsMenu() method. We are going to add just one menu item for now, "Add Item", using a string we will create in strings.xml, and defined with a constant we will create at the top of the class to identify the Add Item operation. a. In strings.xml resource (under res/values), add a new string for menu_insert with text "Add Item" <string name="menu_insert">Add Item</string>, then save the file b. Also, you need a menu position constant at the top of the Notepadv1 class (right under the KEY_BODY definition): public static final int INSERT_ID = Menu.FIRST; c. In the onCreateOptionsMenu() method, add the menu item. Also take care of the result of the super call being returned. The whole method should now look like this: @Override public boolean onCreateOptionsMenu(Menu menu) { boolean result = super.onCreateOptionsMenu( menu); menu.add(0, INSERT_ID, R.string.menu_insert ); return result; }
More on menus The notepad application we are constructing only scratches the surface with menus. You can also add shortcut keys for menu items, create submenus and even add menu items to other applications!.
21
Step 11
Fill out the body of the onOptionsItemSelected() method: This is going to handle our new "Add Note" menu item. When this is selected the onOptionsItemSelected() method will be called with the item.getId() set to INSERT_ID (the constant we used to identify the menu item). We can detect this, and take the appropriate actions: a. The super.onOptionsItemSelected(item) method call goes at the end of this method we want to catch our events first! b. Switch statement on item.getId() c. case INSERT_ID: d. calls new method createNote() e. break at the end of the case f. return the result of the superclass onOptionsItemSelected() method at the end g. The whole onOptionsItemSelect() method should now look like this: @Override public boolean onOptionsItemSelected(Item item) { switch (item.getId()) { case INSERT_ID: createNote(); break; } return super.onOptionsItemSelected(item); }
Step 12
Add a new createNote() method: In this first version of our application, createNote() is not going to be very useful. We will simply create a new note with a title assigned to it based on a counter ("Note 1", "Note 2"...) and with an empty body. At present we have no way of editing the contents of a note, so for now we will have to be content making one with some default values: a. String noteName = "Note " + noteNumber++; (Construct the name using "Note" and the counter we have defined in the class) b. Call dbHelper.createRow() using noteName as the title and "" for the body c. Call fillData() method again after adding (inefficient but simple) d. The whole createNote() method should look like this: private void createNote() { String noteName = "Note " + noteNumber++; dbHelper.createRow(noteName, ""); fillData(); }
22
Step 13
Define the fillData() method. This is fairly long: This method uses ArrayAdapter, which is the simplest way of putting data into a ListView. ArrayAdapter takes either a List or an array of Strings, and binds them into a text view provided in the layout defined for the list row (this is the text1 field in our notes_row.xml layout). The method simply obtains a list of notes from the database helper, constructs a List of Strings using the title strings from each row, and then creates an ArrayAdapter out of those items and bound to use the notes_row we defined. private void fillData() { // We need a list of strings for the list i tems List<String> items = new ArrayList<String>( );
List adapters Our example uses a very simple array adapter which binds an array or list of items into a ListView. More commonly in Android, List Adapters go hand in hand with ContentProviders, and this is also a very easy way to use lists. To bind a ContentProvider to a ListView you can use a android.widget.SimpleCursorAdapter to bind data from a ContentProvider into a ListView
// Get all of the rows from the database an d create the item list List<Row> rows = dbHelper.fetchAllRows(); for (Row row : rows) { items.add(row.title); } // Now create an array adapter and set it t o display using our row ArrayAdapter<String> notes = new ArrayAdapter<String>(this, R.layout .notes_row, items); setListAdapter(notes); } a. ArrayAdapter needs a List of Strings (List<String>) containing the items to display b. The data is read out of the database as rows, and the title field from each row is used to populate the list of strings c. We specify the notes_row view we created as the receptacle for the data d. If you get compiler errors about classes not being found, ctrl-shift-O or (cmd-shift-O on the mac) to organize imports. Note: that for this exercise we use an ArrayAdapter, this is not a very scalable solution and more typically a SimpleCursorAdapter would be used with a ContentProvider or at least a Cursor returned from a query. See the sidebar on List Adapters for more information.
Step 14
Run it! a. Right click on the Notepadv1 project b. From the popup menu, select Run As -> Android Application c. If you see a dialog come up, select Android Launcher as the way of running the application (you can also use the link near the top of the dialog to set this as your default for the workspace, this is recommended as it will stop the plugin from asking you this every time) d. Add new notes by hitting the menu button and selecting Add Item from the menu
23
Step 1
Import the existing Notepadv2 project from under the NotepadCodeLab folder. If you see an error about AndroidManifest.xml, or some problems related to an android.zip file, right click on the project and select Android Tools->Fix Project Properties from the popup menu. Open the Notepadv2 project and take a look around: a. Open and look at the strings.xml file under res/values there are several new strings which we will use for our new functionality b. Also, open and take a look at the top of the Notepadv2 class, you will notice some new stuff in there as well several new constants have been defined, as well as a member fi eld for rows (to hold row data for the list) c. Note also that the fillData() method has been altered slightly to use this rows field instead of a local variable d. There are also a couple of new overridden methods ( onListItemClick() and onActivityResult()) which we will be filling in below.
Step 2
Check the onCreate() method, you will see that it is unchanged from what we wrote in the first exercise.
Step 3
We need to add a delete entry into the menu: a. In the onCreateOptionsMenu() method, add a new line: menu.add(0, DELETE_ID, R.string.menu_delete); b. The whole method should now look like this: @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); menu.add(0, INSERT_ID, R.string.menu_insert ); menu.add(0, DELETE_ID, R.string.menu_delete ); return true; }
24
Step 4
In the onMenuItemSelected() method, add a new case for DELETE_ID: dbHelper.deleteRow(rows.get(getSelection()) .rowId); fillData(); break; a. This uses the getSelection() method from the ListActivity to see what note is currently selected from the list b. It then looks that row up from the rows field we have been holding on to, and gets the rowId, and uses it to delete the row using the DBHelper c. We fill data afterwards to keep everything up to date d. The whole method should now look like this: @Override public boolean onMenuItemSelected(int featureId , Item item) { super.onMenuItemSelected(featureId, item); switch(item.getId()) { case INSERT_ID: createNote(); fillData(); break; case DELETE_ID: dbHelper.deleteRow(rows.get(getSelectio n()).rowId); fillData(); break; } return true; }
Starting Other Activities As well as starting intents in classes we already know about, be they in our own application or another application, we can also create intents without knowing exactly which application will handle it. For example, we might want to open a page in a browser, and for this we still use an intent. But instead of specifying a class to handle it, we use a predefined Intent constant, and a content URI that describes what we want to do.
Step 5
Fill in the body of the createNote() method: We will create a new Intent to create a note (ACTIVITY_CREATE) using the NoteEdit class. We then fire the Intent using the startSubActivity() method call. Intent i = new Intent(this, NoteEdit.class) ; startSubActivity(i, ACTIVITY_CREATE); Don't worry about the fact that NoteEdit doesn't exist yet, we will fix that soon. Note: in this example our Intent uses a class name specifically. While this makes sense sometimes, it is more common to call an Intent using an action and contentURI. See android.content.Intent for more information.
Step 6
Fill in the body of the onListItemClick() override. onListItemClick() is the overridden method that is called when the user selects an item from the list. It is passed four parameters: the ListView object it was invoked from, the View inside the ListView that was clicked on, the position in the list that was clicked, and the rowId of the item that was clicked. In this instance we can ignore the first two parameters (we only have one ListView it could be), and we ignore the rowId as well. All we are interested in is the position that the user selected. We use this to get the data from the correct row, and bundle it up to send in to the NoteEdit activity.
25
This method creates an Intent to edit the note using the NoteEdit class. It then adds data into the extras bundle in the Intent, which can be used to pass information into the Intent. We use it to pass in the title and body text, and the rowId for the note we are editing. Finally, it will fire the Intent using the startSubActivity() method call. super.onListItemClick(l, v, position, id); Intent i = new Intent(this, NoteEdit.class) ; i.putExtra(KEY_ROW_ID, rows.get(position).r owId); i.putExtra(KEY_BODY, rows.get(position).bod y); i.putExtra(KEY_TITLE, rows.get(position).ti tle); startSubActivity(i, ACTIVITY_EDIT); a. This implementation uses an extras bundle available on the i ntent we are using to pass in the title, body and rowId of the note we want to edit, then invokes the intent ACTIVITY_EDIT on the NoteEdit class b. putExtra() is the method to add items into the extras bundle to pass in to intent invocations
Step 7
The above createNote() and onListItemClick() methods use an asynchronous intent invocation. We need a handler, so we fill in the body of the onActivityResult() override. onActivityResult() is the overridden method which will be called when a sub a ctivity returns. The parameters provided are: requestCode the original request code specified in the Intent invocation (either ACTIVITY_CREATE or ACTIVITY_EDIT for us) resultCode the result (or error code) of the call, this should be zero if everything was OK, but may have a non-zero code indicating that something failed. There are sta ndard result codes available, and you can also create your own constants to indicate specific problems data this is a simple string that can be used to hold some kind of textual return information (for example, the result of the user typing something into a dialog). This is useful if you only have one thing to return, and it happens to be a string, if you need to return more, use the extras bundle instead extras this is the returned extras bundle (if any) provided by the called Intent The combination of startSubActivity() and onActivityResult() can be thought of as an asynchronous RPC (remote procedure call) and forms the recommended way for Activities to invoke each other and share services. super.onActivityResult(requestCode, resultC ode, data, extras); switch(requestCode) { case ACTIVITY_CREATE: String title = extras.getString(KEY_TIT LE); String body = extras.getString(KEY_BODY ); dbHelper.createRow(title, body); fillData(); break; case ACTIVITY_EDIT: Long rowId = extras.getLong(KEY_ROW_ID) ; if (rowId != null) { String editTitle = extras.getString (KEY_TITLE); String editBody = extras.getString( KEY_BODY); dbHelper.updateRow(rowId, editTitle , editBody); } fillData(); break; } a. We are handling both the ACTIVITY_CREATE and ACTIVITY_EDIT activity results in this method b. In the case of a create, we pull the title and body from the extras and use them to create a new note c. In the case of an edit, we pull the rowId as well, and use that to update the note in the database d. fillData() at the end ensures everything is up to date
Compiled by fastop 2007-2 http://fastoponandroid.googlepages.com
26
Step 8
Open the file note_edit.xml that has been provided and take a look at it. This is the UI code for the Note Editor. This is the most sophisticated UI we have dealt with yet. The file is given to you to avoid problems that may sneak in when typing the code in (the XML is very strict about case sensitivity and structure, mistakes in these are the usual cause of problems with layout XML files). There is a new parameter used here that we haven't seen before: android:layout_weight (in this case set to use the value 1). layout_weight is used in LinearLayouts to assign "importance" to views within the layout. All views have a default layout_weight of zero, meaning they take up only as much room on the screen as they need to be displayed. Assigning a value higher than zero will split up the rest of the available space in the parent according to the value of the view's layout_weight and its ratio to the overall layout_weight specified in the current layout for this and other views.
The Art of Layout The provided note_edit.xml layout file is the most sophisticated one in the application we will be building, but that doesn't mean it is even close to the kind of sophistication you will be likely to want in real Android applications. Creating a good UI is part art and part science, and the rest is work. Mastering Android layout is an essential part of creating a good looking Android application. Take a look at the View Gallery for some example layouts and how to use them. The ApiDemos sample project is also a great resource from which to learn how to create different layouts.
To give an example: let's say we have a text label and two text edit views in a horizontal row. The label has no layout_weight specified, so it takes up the minimum space required to render. If the layout_weight of each of the two text edit views is set to 1, the remaining width in the parent layout will be split equally between them. If one has a layout_weight of 1 and the other has a layout_weight of 2, then one third of the remaining space will be given to the first, and two thirds to the second. This layout also demonstrates how to nest multiple layouts inside each other to achieve a more complex and pleasant layout. In this example, a horizontal linear layout is nested inside the vertical one to allow the title label and text field to be alongside each other horizontally.
Step 9
Create a NoteEdit class that extends android.app.Activity. This is the first time we will have created an activity without the Android Eclipse plugin doing it for us. When you do so, the onCreate() method is not automatically overridden for you. It is hard to imagine an activity that doesn't override the onCreate() method, so this should be the first thing you do (there is an override/implement methods option available from the right click popup menu in the eclipse editor that we will use) a. Right click on the com.google.android.demo.notepad2 package in the Package Explorer, and select New->Class from the popup menu b. Fill in NoteEdit for the Name: field in the dialog c. In the Superclass: field, enter android.app.Activity (you can also just type Activity and hit Ctrl-Space on Windows and Linux or Cmd-Space on the Mac, to invoke code assist and find the right package and class) d. Hit the Finish button e. In the resulting NoteEdit class, right click in the editor window and select Source->Override/Implement Methods... f. Scroll down through the checklist in the dialog until you see onCreate(Bundle) and check the box next to it g. Hit the OK button.
Step 10
Fill in the body of the onCreate() method. This will set the title of our new activity to say "Edit Note" (one of the strings defined in strings.xml). It will also set the content view to use our note_edit.xml layout file. We can then grab handles to the title and body text edit views, and the confirm button, so that our class can use them to set and get the note title and body, and attach an event to the confirm button for when it is pressed by the user.
27
We can then unbundle the values that were passed in to the activity from the extras bundle in the calling Intent, and use those to pre-populate the title and body text edit views with data so that the user can edit them. We will grab and store the rowId as well so that we can keep track of what note the user is editing. a. Set up the layout: setContentView(R.layout.note_edit); b. Find the edit and button components we need: These are found by the IDs associated to them in the R class, and need to be cast to the right type of View (EditText for the two text views, and Button for the confirm button) titleText = (EditText) findViewById(R.id.ti tle); bodyText = (EditText) findViewById(R.id.bod y); Button confirmButton = (Button) findViewByI d(R.id.confirm); Note that titleText and bodyText are member fields (you will need to add them at the top of the class definition) c. Create a Long rowId private field which will be used to store the current rowId being edited (if any) d. Add code to initialize the title, body and rowId from the extras bundle in the intent, if it is present: rowId = null; Bundle extras = getIntent().getExtras(); if (extras != null) { String title = extras.getString(Notepad v2.KEY_TITLE); String body = extras.getString(Notepadv 2.KEY_BODY); rowId = extras.getLong(Notepadv2.KEY_RO W_ID); if (title != null) { titleText.setText(title); } if (body != null) { bodyText.setText(body); } } We are pulling the title and body out of the extras bundle that was set from the Intent invocation Have to null-protect the text field setting (i.e. we don't want to set the text fields to null accidentally) e. Create an onClickListener() for the button: Listeners can be one of the more confusing aspects of UI implementation, but what we are trying to achieve in this case is simple. We simply want an onClick() method to be called when the user presses the confirm button, and we can use that to do some work and return the values of the edited note to the Intent caller. We do this using something called an anonymous inner class. This is a bit confusing to loo k at unless you have seen them before, but all you really need to take away from this is that you can refer to this code in the future to see how to create a listener and attach it to a button. (Listeners are a common idiom in Java development, particularly for user interfaces.) confirmButton.setOnClickListener(new View.O nClickListener() { public void onClick(View view) { } });
28
Step 11
Fill in the body of the onClick() method. This is the code that will be run when the user clicks on the confirm button. We want this to grab the title and body text from the edit text fields, and put them into the return bundle so that they can be passed back to the activity that invoked this Intent in the first place. If the operation is an edit rather than a create, we also want to put the rowId into the bundle as well so that the Notepadv2 class can save the changes back to the correct note. a. Create a bundle and put the title and body text into it using the constants defined in Notepadv2 as keys Bundle bundle = new Bundle(); bundle.putString(Notepadv2.KEY_TITLE, titleT ext.getText().toString()); bundle.putString(Notepadv2.KEY_BODY, bodyTex t.getText().toString()); if (rowId != null) { bundle.putLong(Notepadv2.KEY_ROW_ID, row Id); } b. Set the result information, including the bundle, and finish the activity: The setResult() method is used to set the result code, return data string, and extras bundle to be passed back to the Intent caller. In this case everything worked, so we return RESULT_OK for the result code. We are not using the string data field in this case, so we pass a null back for that and pass the bundle we have just created with the title, body and rowId information in it. The finish() call is used to signal that the activity is done (like a return call). Anything set in the Result will then be returned to the caller, along with execution control. setResult(RESULT_OK, null, bundle); finish(); c. The full onCreate() method (plus supporting class fields) should now look like this: private EditText titleText; private EditText bodyText; private Long rowId; @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.note_edit); titleText = (EditText) findViewById(R.id.ti tle); bodyText = (EditText) findViewById(R.id.bod y); Button confirmButton = (Button) findViewByI d(R.id.confirm); rowId = null; Bundle extras = getIntent().getExtras(); if (extras != null) { String title = extras.getString(Notepad v2.KEY_TITLE); String body = extras.getString(Notepadv 2.KEY_BODY); rowId = extras.getLong(Notepadv2.KEY_RO W_ID); if (title != null) { titleText.setText(title); } if (body != null) { bodyText.setText(body); } }
29
confirmButton.setOnClickListener(new View.OnClickList ener() { public void onClick(View view) { Bundle bundle = new Bundle(); bundle.putString(Notepadv2.KEY_TITL E, titleText.getText().toString()); bundle.putString(Notepadv2.KEY_BODY , bodyText.getText().toString()); if (rowId != null) { bundle.putLong(Notepadv2.KEY_RO W_ID, rowId); } setResult(RESULT_OK, null, bundle); finish(); } }); }
Step 12
Finally the new activity has to be defined in the manifest file: Before the new activity can be seen by Android, it needs its own activity entry in the AndroidManifest.xml file. This is to let the system know that it is there and can be called. We could also specify which IntentFilters the activity implements here, but we are going to skip this for now and just let Android know that the Activity is defined. <activity class=".NoteEdit"/> This should be placed just below the line that reads </activity> for the .Notepadv2 activity.
The All-Important Android Manifest File The AndroidManifest.xml file is the way in which Android sees your application. This file defines the category of the application, where it shows up (or even if it shows up) in the launcher or settings, what activities, services, and content providers it defines, what intents it can receive, and more. For more information, see the reference document AndroidManifest.xml
Step 13
Now Run it! (use Run As -> Android Application on the project right click menu again) You should now be able to add real notes from the menu, as well as deleting an existing one using the menu. Furthermore , selecting a note title from the list should bring up the note editor to let you edit it. Hit confirm when finished to save the changes back to the database.
30
Step 1
The current example has some problems hitting the back button when editing causes a crash, and anything else that happens during editing will cause the edits to be lost. To fix this, we will move most of the functionality for creating and editing the note into the NoteEdit class, and introduce a full lifecycle for editing notes. Import Notepadv3 into Eclipse. If you see an error about AndroidManifest.xml, or some problems related to an Android zip file, right click on the project and select Android Tools->Fix Project Properties from the popup menu. The starting point for this exercise is exactly the same as the solution for Notepadv2. a. Remove the NoteEdit code to parse out the title and body from the extras bundle. We are going to use the DBHelper class to access the notes from the database directly. All we need passed into the activity is a rowId (if we are editing, if creating we don't need anything). We will get rid of the properties that were being passed in through the extras bundle that we were using to set the title and body text edit values in the UI. b. Remove the lines that read: String title = extras.getString(Notepadv3.K EY_TITLE); String body = extras.getString(Notepadv3.KE Y_BODY); and if (title != null) { titleText.setText(title); } if (body != null) { bodyText.setText(body); }
(Extra credit, you could replace the remaining if (extras != null) block with a ternary operator as well: rowId = extras != null ? extras.getLong(Not epadv3.KEY_ROW_ID) : null;
Step 2
Create a class field for a DBHelper at the top of the class: private DBHelper dbHelper; and an instance of DBHelper in the onCreate() method (right below the super.onCreate() call): dbHelper = new DBHelper(this);
31
Step 3
We need to check the icicle for a rowId in case the note editing has been frozen and thawed: a. Replace the code that currently initializes the rowId: rowId = null; Bundle extras = getIntent().getExtras(); if (extras != null) { rowId = extras.getLong(Notepadv3.KEY_RO W_ID); }
with rowId = icicle != null ? icicle.getLong(Not epadv3.KEY_ROW_ID) : null; if (rowId == null) { Bundle extras = getIntent().getExtras() ; rowId = extras != null ? extras.getLong (Notepadv3.KEY_ROW_ID) : null; }
b. Note the null check for icicle, and we still need to load up rowId from the extras bundle if it is not provided by the icicle. This is a ternary operator shorthand to safely either use the value or null if it is not present.
Step 4
Next, we need to populate the fields based on the rowId if we have it: populateFields(); before the confirmButton.setOnClickListener() line.
Step 5
Get rid of the bundle creation and bundle value settings from the onClick() handler method. The Activity no longer needs to return any extra information to the caller. You can also use the shorter version of setResult(): public void onClick(View arg0) { setResult(RESULT_OK); finish(); } We will take care of storing the updates or new notes in the database ourselves using the lifecycle methods. The whole onCreate() method should now look like this: super.onCreate(icicle); dbHelper = new DBHelper(this); setContentView(R.layout.note_edit); titleText = (EditText) findViewById(R.id.ti tle); bodyText = (EditText) findViewById(R.id.bod y); Button confirmButton = (Button) findViewByI d(R.id.confirm);
32
rowId = icicle != null ? icicle.getLong(Not epadv3.KEY_ROW_ID) : null; if (rowId == null) { Bundle extras = getIntent().getExtras() ; if (extras != null) { rowId = extras.getLong(Notepadv3.KE Y_ROW_ID); } } populateFields(); confirmButton.setOnClickListener(new View.OnClickListener( ) { public void onClick(View arg0) { setResult(RESULT_OK); finish(); } });
Step 6
Define the populateFields() method. private void populateFields() { if (rowId != null) { DBHelper.Row row = dbHelper.fetchRow(ro wId); if (row.rowId > -1) { titleText.setText(row.title); bodyText.setText(row.body); } } }
Step 7
Override methods onFreeze(), onPause() and onResume() These are our lifecycle methods (along with onCreate() which we already have). onFreeze() is called by Android if the Activity is being stopped and may be killed before it is resumed! This means it should store any state necessary to re-initialize to the same condition when the activity is restarted. It is the counterpart to the onCreate() method, and in fact the icicle bundle passed in to onCreate() is the same bundle that you construct as outState in the onFreeze() method. onPause() and onResume() are also complimentary methods. onPause() is always called when the Activity ends, even if we instigated that (with a finish call for example). We will use this to save the current note back to the database. Good practice is to release any resources that can be released during an onPause() as well, to take up less resources when in the passive state. For this reason we will close the DBHelper class and set the field to null so that it can be garbage collected if necessary. onResume() on the other hand, will re-create the dbHelper instance so we can use it, and then read the note out of the database again and populate the fields.
Why handling lifecycle events is important If you are used to always having control in your applications, you might not understand why all this lifecycle work is necessary. The reason is that in Android, you are not in control of your activity, the operating system is! As we have already seen, the Android model is based around Activities calling each other. When one Activity calls another, the current activity is paused at the very least, and may be killed altogether if the system starts to run low on resources. If this happens, your Activity will have to store enough state to come back up later, preferably in the same state it was in when it was killed. Android has a well-defined lifecycle. Lifecycle events can happen even if you are not handing off control to another activity explicitly. For example, perhaps a call comes in to the handset. If this happens, and your activity is running, it will be swapped out while the call activity takes over.
33
a. onFreeze(): @Override protected void onFreeze(Bundle outState) { super.onFreeze(outState); outState.putLong(Notepadv3.KEY_ROW_ID, rowId); } b. onPause(): @Override protected void onPause() { super.onPause(); saveState(); dbHelper.close(); dbHelper = null; } c. onResume(): @Override protected void onResume() { super.onResume(); if (dbHelper == null) { dbHelper = new DBHelper(this); } populateFields(); }
Step 8
Define the saveState() method to put the data out to the database. private void saveState() { String title = titleText.getText().toString (); String body = bodyText.getText().toString() ; if (rowId == null) { dbHelper.createRow(title, body); } else { dbHelper.updateRow(rowId, title, body); } }
Step 9
Now pull out the previous handling code from the onActivityResult() method in the Notepadv3 class All of the note retrieval and updating now happens withi n the NoteEdit lifecycle, so all the onActivityResult() method needs to do is update its view of the data, no other work is necessary. The resulting method should look like this: @Override protected void onActivityResult(int requestCode , int resultCode, String data, Bu ndle extras) { super.onActivityResult(requestCode, resultC ode, data, extras); fillData(); } Because the other class now does the work, all this has to do is refresh the data.
Compiled by fastop 2007-2 http://fastoponandroid.googlepages.com
34
Step 10
Also remove the lines which set the title and body from the onListItemClick() method (again they are no longer needed, only the rowId is): i.putExtra(KEY_TITLE, rows.get(position).title); i.putExtra(KEY_BODY, rows.get(position).bod y); Run it! (use Run As -> Android Application on the project right click menu again)
Step 1
Using the working Notepadv3, put breakpoints in the code at the beginning of the onCreate(), onPause(), onFreeze() and onResume() methods in the NoteEdit class (if you are not familiar with Eclipse, just right click in the narrow grey border on the left of the edit window at the line you want a breakpoint, and select Toggle Breakpoint, you should see a blue dot appear).
Step 2
Now start the notepad demo in debug mode: a. Right click on the Notepadv3 project and from the Debug menu select Debug As -> Android Application. b. The Android emulator should say "waiting for debugger to connect" briefly and then run the application. c. If it gets stuck on the waiting... screen, quit the emulator and Eclipse, from the command line do an adb kill-server, and then restart Eclipse and try again.
Step 3
When you edit or create a new note you should see the breakpoints getting hit and the execution stopping.
Step 4
Hit the Resume button to let execution continue (yellow rectangle with a green triangle to its right in the Eclipse toolbars near the top).
Step 5
Experiment a bit with the confirm and back buttons, and try hitting home and making other mode changes. Watch what lifecycle events are generated and when. The Android Eclipse plugin not only offers excellent debuggin g support for your application development, but also superb profiling support. If your application is running too slow, this can help you find the bottlenecks and fix them.
35
36
Here is a list of the basic steps in building an application. 1. Create your required resource files This includes the AndroidManifest.xml global description file, string files that your application needs, and layout files describing your user in terface. A full list of optional and required files and syntax details for each is given in File List for an Android Application. 2. Design your user interface See Implementing a UI for details on elements of the Android screen. 3. Implement your Activity (this page) You will create one class/file for each screen in your applicatio n. Screens will inherit from an android.app class, typically android.app.Activity for basic screens, android.app.ListActivity for list screens, or android.app.Dialog for dialog boxes. You will implement the required callbacks th at let you draw your screen, query data, and commit changes, and also perform any required tasks such as opening additional screens or reading data from the device. Common tasks, such as opening a new screen or reading data from the device, are described below. The list of files you'll need for your application are described in List of Files for an Android Application. 4. Build and install your package. The Android SDK has some nice tools for generating projects and debugging code.
Floating or full?
When you open a new screen you can decide whether to make it transparent or floating, or full-screen. The choice of new screen affects the event sequence of events in the old screen (if the new screen obscures the old screen, a different series of events is called in the old screen). See Lifetime of an Activity for details. Transparent or floating windows are implemented in three stan dard ways: Create an app.Dialog class Create an app.AlertDialog class Set the Theme_Dialog theme attribute to @android:style/Theme.Dialog in your AndroidManifest.xml file. For example: <activity class="AddRssItem" android:label="Add an item" android:theme="@android:style/Theme.Dialog"/> Calling startActivity() or startSubActivity() will open a new screen in whatever way it defines itself (if it uses a floating theme it will be floating, otherwise it will be full screen).
Opening a Screen
When you want to open a new screen, you can either explicitly specify the activity class to open, or you can let the operating system decide which screen to open, based upon the data and various parameters you pass in. A screen is opened by calling startActivity and passing in an Intent object, which specifies the criteria for the handling screen. To specify a specific screen, call Intent.setClass or setClassName with the exact activity class to open. Otherwise, set a variety of values and
Compiled by fastop 2007-2 http://fastoponandroid.googlepages.com
37
data, and let Android decide which screen is appropriate to open. Android will find one or zero Activities that match the specified requirements; it will never open multiple activities for a single request. More information on Intents and how Android resolves them to a specific class is given in the Intent topic.
38
} else { myFightFunction(data, extras); } default: break; } } // Class SentResult // Temporary screen to let the user choose somethin g. private OnClickListener mLincolnListener = new OnClickListener(){ public void onClick(View v) { Bundle stats = new Bundle(); stats.putString("height","6\'4\""); stats.putString("weight", "190 lbs"); stats.putString("reach", "74\""); setResult(RESULT_OK, "Lincoln", stats); finish(); } }; private OnClickListener mWashingtonListener = n ew OnClickListener() { public void onClick(View v){ Bundle stats = new Bundle(); stats.putString("height","6\'2\""); stats.putString("weight", "190 lbs"); stats.putString("reach", "73\""); setResult(RESULT_OK, "Washington", Bund le); finish(); } };
39
Bundle only exists while the application is still in the history stack (whether or not it has been removed from memory) and will be lost when the application is finalized. See the topics for onFreeze(Bundle) and onCreate(Bundle) for examples of storing and retrieving state. Read more about the life cycle of an application in Lifetime of an Activity.
40
Receiver Java code: public class AlarmReceiver extends IntentReceiver{ // Display an alert that we've received a messa ge. @Override public void onReceiveIntent(Context context, In tent intent){ // Send a text notification to the scre en. NotificationManager nm = (NotificationManag er) context.getSystemService(Context.NOTIFICATI ON_SERVICE); nm.notifyWithText(R.id.alarm, "Alarm!!!", NotificationManager.LENGT H_SHORT, null); } }
Setting Alarms
Android provides an AlarmManager service that will let you specify an Intent to send at a designated time. This intent is typically used to start an application at a preset time. (Note: If you want to send a notification to a sleeping or running application, use Handler instead.)
Displaying Alerts
There are two major kinds of alerts that you may display to the user: (1) Normal alerts are displayed in response to a user action, such as trying to perform an action that is not allowed. (2) Out-of-band alerts, called notifications, are displayed as a result of something happening in the background, such as the user receiving new e-mail.
Normal Alerts
Android provides a number of ways for you to show popup notifications to your user as they interact with your application. Class app.Dialog app.AlertDialog or Context.showAlert() ProgressDialog A dialog box used to indicate progress of an operation with a known progress value or an indeterminate length (setProgress(bool)). See Views > Progress Bar in ApiDemos for examples. By setting the theme of an activity to android:theme="android:style/Theme.Dialog" , your activity will take on the appearance of a normal dialog, floating on top of whatever was underneath it. You usually set the theme through the android:theme attribute in your AndroidManifest.xml. The advantage of this over Dialog and AlertDialog is that Appli cation has a much better managed Description A generic floating dialog box with a layout that you design. A popup alert dialog with two buttons (typically OK and Cancel) that take callback handlers. It can be created separately, or launched using the Application h elper method Context.showAlert(). See the section after this table for more details.
Activity
41
AlertDialog
This is a basic warning dialog box that lets you configure a message, button text, and callback. You can create one by calling the Application helper method Context.showAlert(), as shown here. private Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case ACCEPT_CALL: answer(msg.obj); break; case BOUNCE_TO_VOICEMAIL: voicemail(msg.obj); break; } } };
private void IncomingMotherInlawCall(Connection c) { String Text; // "Answer" callback. Message acceptMsg = Message.obtain(); acceptMsg.target = mHandler; acceptMsg.what = ACCEPT_CALL; acceptMsg.obj = c.getCall(); // "Cancel" callback. Message rejectMsg = Message.obtain(); rejectMsg.target = mHandler; rejectMsg.what = BOUNCE_TO_VOICEMAIL; rejectMsg.obj = c.getCall(); showAlert(null, "Phyllis is calling", "Answer", acceptMsg, true, rejectMsg); }
Notifications
Out-of-band alerts should always be displayed using the NotificationManager, which allows you to tell the user about something they may be interested in without disrupting what they are currently doing. A notification can be anything from a brief pop-up box informing the user of the new information, through displaying a persistent icon in the status bar, to vibrating, playing sounds, or flashing lights to get the user's attention. In all cases, the user must explicitly shift their focus to the notification before they can interact with it. The following code demonstrates using NotificationManager to display a basic text popup when a new SMS message arrives in a listening service, and provides the current message count. You can see several more examples in the ApiDemos application, under app/ (named notification*.java). static void setNewMessageIndicator(Context context, int messageCount){ // Get the static global NotificationManager obj ect. NotificationManager nm = NotificationManager.get Default();
// If we're being called because a new message h as been received, // then display an icon and a count. Otherwise, delete the persistent // message.
42
if (messageCount > 0) { nm.notifyWithText(myApp.NOTIFICATION_GUID, // ID for this notification. messageCount + " new message" + mes sageCount > 1 ? "s":"", // Text to display. NotificationManager.LENGTH_SHORT); // Show it for a short time only. } } To display a notification in the status bar and have it launch an intent when the user selects it (such as the new text message notification does), call NotificationManager.notify(), and pass in vibration patterns, status bar icons, or Intents to associate with the notification.
// Called only the first time the options menu is d isplayed. // Create the menu entries. // Menu adds items in the order shown. @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); // Parameters for menu.add are: // group -- Not used here. // id -- Used only when you want to handle and identify the click yourself. // title menu.add(0, 0, "Zoom"); menu.add(0, 1, "Settings"); menu.add(0, 2, "Other");
43
return true; } // Activity callback that lets your handle the sele ction in the class. // Return true to indicate that you've got it, fals e to indicate // that it should be handled by a declared handler object for that // item (handler objects are discouraged for reason s of efficiency). @Override public boolean onOptionsItemSelected(Menu.Item item ){ switch (item.getId()) { case 0: showAlert("Menu Item Clicked", "Zoom", "ok" , null, false, null); return true; case 1: showAlert("Menu Item Clicked", "Settings", "ok", null, false, null); return true; case 2: showAlert("Menu Item Clicked", "Other", "ok ", null, false, null); return true; } return false; } You can add key shortcuts by calling the Item.setAlphabeticShortcut() or Item.setNumericShortcut() methods, as demonstrated here to add a "C" shortcut to a menu item: thisItem.setAlphabeticShortcut(0, 'c');
Adding Submenus
Add a submenu by calling Menu.addSubMenu(), which returns a SubMenu object. You can then add additional items to this menu. Menus can only be one level deep, and you can customize t he appearance of the submenu menu item. @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); // Parameters for menu.add are: // group -- Not used here. // id -- Used only when you want to handle and identify the click yourself. // title menu.add(0, 0, "Send message"); menu.add(0, 1, "Settings"); menu.add(0, 2, "Local handler"); menu.add(0, 3, "Launch contact picker"); // Add our submenu. SubMenu sub = menu.addSubMenu(1, 4, "Days of th e week"); sub.add(0, 5, "Monday"); sub.add(0, 6, "Tuesday"); sub.add(0, 7, "Wednesday"); sub.add(0, 8, "Thursday"); sub.add(0, 9, "Friday"); sub.add(0, 10, "Saturday"); sub.add(0, 11, "Sunday"); return true; }
44
example, suppose you implement a new image handling tool that shrinks an image to a smaller size and you would like to offer this as a menu option to any other Activity that handles pictures. To do this, you would exposes your capabilities inside an intent filter in your manifest. If another application that handles photos asks Android for any Activities that can perform actions on pictures, Android will perform intent resolution, find your Activity, and add it to the other Activity's options menu.
45
Binding to Data
You can bind a ListView to a set of underlying data by using a shim class called ListAdapter (or a subclass). ListAdapter subclasses bind to a variety of data sources, and expose a common set of methods such as getItem() and getView(), and uses them to pick View items to display in its list. You can extend ListAdapter and override getView() to create your own custom list items. There are essentially only two steps you need to perform to bind to data: 1. Create a ListAdapter object and specify its data source 2. Give the ListAdapter to your ListView object. That's it! Here's an example of binding a ListActivity screen to the results from a cursor query. (Note that the setListAdapter() method shown is a convenience method that gets the page's ListView object and calls setAdapter() on it.) // Run a query and get a Cursor pointing to the res ults. Cursor c = People.query(this.getContentResolver(), null); startManagingCursor(c); // Create the ListAdapter. A SimpleCursorAdapter le ts you specify two interesting things: // an XML template for your list item, and // The column to map to a specific item, by ID, in your template. ListAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1 , // Use a template that displays a text view c, // Give the cursor to the list adapter new String[] {People.NAME} , // Map the NAME column in the people database to... new String[] {"text1"}); // The "text1" view defined in the XML template setListAdapter(adapter); See view/List4 in the ApiDemos project for an example of extending ListAdapter for a new data type.
46
The following outline illustrates a typical implementation: public class MyActivity extends Activity { [ . . . ] // Need handler for callbacks to the UI thread final Handler mHandler = new Handler(); // Create runnable for posting final Runnable mUpdateResults = new Runnable() { public void run() { updateResultsInUi(); } }; @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); [ . . . ] } protected void startLongRunningOperation() { // Fire off a thread to do some work that w e shouldn't do directly in the UI thread Thread t = new Thread() { public void run() { mResults = doSomethingExpensive(); mHandler.post(mUpdateResults); } }; t.start(); } private void updateResultsInUi() { // Back in the UI thread -- update our UI e lements based on the data in mResults [ . . . ] } } For further discussions on this topic, see Developing Responsive Applications and the Handler documentation.
47
from the android.text.style package and the selection range. The following code snippet demonstrates creating a string wit h a highlighted section, italic section, and bold section, and adding it to an EditText object. // Get our EditText object. EditText vw = (EditText)findViewById(R.id.text); // Set the EditText's text. vw.setText("Italic, highlighted, bold."); // // // // If this were just a TextView, we could do: vw.setText("Italic, highlighted, bold.", TextVie w.BufferType.SPANNABLE); to force it to use Spannable storage so styles c an be attached. Or we could specify that in the XML.
// Get the EditText's internal text storage Spannable str = vw.getText(); // Create our span sections, and assign a format to each. str.setSpan(new StyleSpan(android.graphics.Typeface .ITALIC), 0, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); str.setSpan(new BackgroundColorSpan(0xFFFFFF00), 8, 19, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); str.setSpan(new StyleSpan(android.graphics.Typeface .BOLD), 21, str.length() - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
(optional) Zero or more files that will be compiled to android.graphics.drawable resources. Files can be image files (png, gif, or other) or XML files describing other graphics such as bitmaps, stretchable bitmaps, or gradients. Supported bitmap file formats are PNG (preferred), JPG, and GIF (discouraged), as well as the custom 9-patch stretchable bitmap format. These formats are described in Resources.
48
49
Development Tools
The Android SDK includes a variety of custom tools that help you develop mobile applications on the Android platform. The most important of these are the Android Emulator and the Android Development Tools plugin for Eclipse, but the SDK also includes a variety of other tools for debugging, packaging, and installing your applications on the emulator. Android Emulator A virtual mobile device that runs on your computer. You use the emulator to design, debug, and test your applications in an actual Android run-time environment. Android Development Tools Plugin for the Eclipse IDE The ADT plugin adds powerful extensions to the Eclipse integrated environment, making creating and debugging your Android applications easier and faster. If you use Eclipse, the ADT plugin gives you an incredible boost in developing Android applications: It gives you access to other Android development tools from inside the Eclipse IDE. For example, ADT lets you access the many capabilities of the DDMS tool taking screenshots, managing port-forwarding, setting breakpoints, and viewing thread and process information directl y from Eclipse. It provides a New Project Wizard, which helps you quickly create and set up all of the basic files you'll need for a new Android application. It automates and simplifies the process of building your Android application. It provides an Android code editor that helps you write valid XML for your Android manifest and resource files. For more information about the ADT plugin, including installation instructions, see Installing the ADT Plugin for Eclipse. For a usage example with screenshots, see Hello Android. Dalvik Debug Monitor Service (ddms) Integrated with Dalvik, the Android platform's custom VM, this tool lets you manage processes on an emulator or device and assists in debugging. You can use it to kill processes, select a specific process to debug, generate trace data, view heap and thread information, take screenshots of the emulator or device, and more. Android Debug Bridge (adb) The adb tool lets you install your application's .apk files on an emulator or device and access the emulator or device from a command line. You can also use it to link a standard debugger to application code running on an Android emulator or device. Android Asset Packaging Tool (aapt) The aapt tool lets you create .apk files containing the binaries and resources of Android applications. Android Interface Description Language (aidl) Lets you generate code for an interprocess interface, such as what a service might use. sqlite3 Included as a convenience, this tool lets you access the SQLite data files created and used by Android applications. Traceview This tool produces graphical analysis views of trace log data that you can generate from your Android application. mksdcard Helps you create a disk image that you can use with the emulator, to simulate the presence of an external storage card (such as an SD card). dx The dx tool rewrites .class bytecode into Android bytecode (stored in .dex files.) activityCreator A script that generates Ant build files that you can use to compile your Android applications. If you are developing on Eclipse with the ADT plugin, you won't need to use this script.
50
Android Emulator
The Android SDK includes a mobile device emulator a virtual device that runs on your computer. The emulator lets you prototype, develop, and test Android applications without using a physical device. The Android emulator mimics all of the typical functions and behaviors of a mobile device, except that it can not receive or place phone calls. As shown at right, the emulator provides a variety of navigation and control keys, which you can "press" using your mouse or keyboard to generate events for your application. It also provides a screen in which your applicatio n is displayed, together with any other Android applications running. To help you model and test your application, the emulator lets your application use the services of the Android platform to invoke other applications, access the network, play audio and video, store and retrieve data, notify the user, and render graphical transitions and themes. The emulator also includes a variety of debug capabilities, such as a console from which you can log kernel output, simulate application interrupts (such as arriving SMS messages or phone calls), and simulate latency effects and dropouts on the data channel. The sections below provide more information about the emulator and how to use it when developing your applications.
Contents
Starting and Stopping the Emulator Controlling the Emulator Emulator Startup Options Using the Emulator Console Port Redirections Network Status Network Delay Emulation Network Speed Emulation Telephony Emulation Using Emulator Skins Running Multiple Instances of the Emulator Installing Applications on the Emulator SD Card Emulation Creating a Disk Image Copying Files to a Disk Image Loading the Disk Image at Emulator Startup Troubleshooting Emulator Problems Emulator Limitations
51
52
Category Help
Option -help
Description Print a list of all emulator commands. Use <file> as the working user-data disk image.
Comments
Data
-data [file]
If -data is not used, the emulator looks for a file named "userdata.img" in ~/.android (Linux/Mac) or C:\Documents and Settings\<user>\Local Settings\Android (Windows). If you use -data <file> but <file> does not exist, the emulator creates a file at that location.
-ramdisk <file>
Use <file> as the ramdisk image. Use <file> as the SD card image. Wipe all data on the user disk image (see -data) before starting. Enable console shell on the current terminal. Send kernel output to the console. Enable logcat output with given tags.
-sdcard <file>
-wipe-data
Debug
-console
-debug-kernel
-logcat <logtags>
If the environment variable ANDROID_LOG_TAGS is defined and not empty, its value will be used to enable logcat output by default
-trace <name>
Enable code profiling (press F9 to start). Enable verbose output. Enable verbose key presses. Use device or WAV file for audio input. Disable Android audio support. Redirect radio modem interface to a host character device. Enable Android audio support. Set network latency emulation to <delay>. Disabled by default. Default value is none. See the table in Network Delay Emulation for supported Set by default.
53
Port Redirection
You can use the console to add and remove port redirections while the emulator is running. After connecting to the console, you can manage port redirections in this way: redir <list|add|del> The redir command supports the subcommands listed in the table below. Subcommand list Description List the current port redirections. Add a new port redirection. <protocol> must be either "tcp" or "udp" <host-port> is the port number to open on the host <guest-port> is the port number to route data to on the emulator/device del <protocol>:<host-port> Delete a port redirection. See above for meanings of <protocol> and <host-port>. Comments
add <protocol>:<host-port>:<guest-port>
Network Status
You can use the console to check the network status and current delay and speed characteristics. To do so, connect to the console and use the netstatus command. Here's an example of the command and its output. network status
Compiled by fastop 2007-2 http://fastoponandroid.googlepages.com
54
console and use the netstatus command. Here's an example of the command and its output. network status
<min>:<max>
55
Description GSM/CSD HSCSD GPRS EDGE/EGPRS UMTS/3G HSDPA no limit Set an exact rate used for both upload and download. Set exact rates for upload and download separately.
Comments (Up: 14.4, down: 14.4) (Up: 14.4, down: 43.2) (Up: 40.0, down: 80.0) (Up: 118.4, down: 236.8) (Up: 128.0, down: 1920.0) (Up: 348.0, down: 14400.0) (Up: 0.0, down: 0.0)
<up>:<down>
Telephony Emulation
The Android emulator includes its own GSM emulated modem that lets you simulate telephony functions in the emulator. For example, you can simulate inbound phone calls and establish/t erminate data connections. The Android system handles simulated calls exactly as it would actual calls. The emulator does not support call audio in this release. You can use the console to access the emulator's telephony functions. After connecting to the console, you can use gsm <call|data|voice> to invoke telephony functions. The gsm command supports the subcommands listed in the table below. Subcommand call <phonenumber> Description Simulate an inbound phone call from <phonenumber>. Change the state of the GPRS voice connection to <state>. Supported <state> values are: unregistered -- No network available home -- On local network, non-roaming roaming -- On roaming network searching -- Searching networks denied -- Emergency calls only off -- Same as 'unregistered' on -- Same as 'home' data <state> Change the state of the GPRS data connection to <state>. Supported <state> values are: unregistered -- No network available home -- On local network, non-roaming roaming -- On roaming network searching -- Searching networks Comments
voice <state>
56
QVGA-P
240x320, portrait
HVGA-L
480x320, landscape
HVGA-P
320x480, portrait
57
SD Card Emulation
You can create a disk image and then load it to the emulator at startup, to simulate the presence of a user's SD card in the device. The sections below describe how to create the disk image, how to copy files to it, and how to load it in the emulator at startup. Note that you can only load disk image at emulator startup. Similarly, you can not remove a simulated SD card from a running emulator. However, you can browse, send files to, and copy/remove files from a simulated SD card either with adb or the emulator. Also note that the emulator supports SD card images that are a maximum of 2 GB in size.
58
Emulator Limitations
In this release, the limitations of the emulator include: No support for placing or receiving actual phone calls. You can simulate phone calls (placed and received) through the emulator console, however. No support for USB connections No support for camera/video capture (input). No support for audio input (capture). Output (playback) is supported. No support for device-attached headphones No support for determining connected state No support for determining battery charge level and AC charging state No support for determining SD card insert/eject No support for Bluetooth
59
Topics
Hierarchy of Screen Elements Common Layout Objects Working with AdapterViews (Binding to Data) Designing Your Screen in XML Hooking into a Screen Element Listening for UI Notifications Applying a Theme to Your Application UI Elements and Concepts Glossary
Compiled by fastop 2007-2 http://fastoponandroid.googlepages.com
60
Views
A view is an object of base class android.view.View. It's a data structure whose properties store the layout and content for a specific rectangular area of the screen. A View object handles measuring and layout, drawing, focus change, scrolling, and key/gestures for the screen area it represents. The View class serves as a base class for widgets -- a set of fully implemented subclasses that draw interactive screen elements. Widgets handle their own measuring and drawing, so you can use them to build your UI more quickly. The list of widgets available includes Text, EditText, InputMethod, MovementMethod, Button, RadioButton, Checkbox, and ScrollView.
Viewgroups
A viewgroup is an object of class android.view.Viewgroup. As its name indicates, a viewgroup is a special type of view object whose function is to contain and manage a subordinate set of views and other viewgroups, Viewgroups let you add structure to your UI and build up complex screen elements that can be addressed as a single entity. The Viewgroup class serves as a base class for layouts -- a set of fully implemented subclasses that provide common types of screen layout. The layouts give you a way to build a structure for a set of views.
A Tree-Structured UI
On the Android platform, you define an Activity's UI using a tree of view and viewgroup nodes, as shown in the diagram below. The tree can be as simple or complex as you need to make it, and you can build it up using Android's set of predefined widgets and layouts or custom view types that you create yourself.
To attach the tree to the screen for rendering, your Activity calls its setContentView() method and passes a reference to the root node object. Once the Android system has the reference to the root node object, it can work directly with the node to invalidate, measure, and draw the tree. When your Activity becomes active and receives focus, the system notifies your activity and requests the root node to measure and draw the tree. The root node then requests that its child nodes draw themselves -- in turn, each viewgroup node in the tree is responsible for drawing its direct children. As mentioned previously, each view group has the responsibility of measuring its available space, laying out its children, and calling Draw() on each child to let it render itself. The children may request a size and location in the parent, but the parent object has the final decision on where how big each child can be.
61
Note that every LayoutParams subclass has its own syntax for setting values. Each child element must define LayoutParams that are appropriate for its parent, although it may define different LayoutParams for its children. All viewgroups include width and height. Many also include ma rgins and borders. You can specify width and height exactly, though you probably won't want to do this often. More often you will tell your view to size itself either to the dimensions of its content, or to become as big as its containing object will allow.
FrameLayout
FrameLayout is the simplest layout object. It is intended as a blank reserved space on your screen that you can later fill with a single object for example, a picture that you'll swap out. All child elements are pinned to the top left corner of the screen; you cannot specify a location for a child of a FrameLayout. Later children will simply be drawn over earlier objects, partially or totally obscuring them (unless the newer object is transparent).
LinearLayout
A LinearLayout aligns all children in a single direction vertically or horizontally, depending on what property you set on the LinearLayout. All children are stacked one after the other, so a vertical list will only have one child per row, no matter how wide they are, and a horizontal list will only be one row high (the height of the tallest child, plus padding). LinearLayout respects margins between children, and also gravity (right, center, or left alignment of a child). LinearLayout also supports assigning a weight to individual children. This value allows children to expand to fill any remaining space on a screen. This prevents a list of small objects from being bunched to one end of a large screen, allowing them to expand to fill the space. Children specify a weight value, and any remaining space is assigned to children in the proportion of their declared weight. Default weight is zero. So, for example, if there are three text boxes, and two of them declare a weight of 1, two of them will expand equally to fill the remaining space, and the third will not grow any additional amount.
62
The following two forms represent a LinearLayout with a set of elements: a button, some labels, some text boxes. Both have padding values to adjust the padding nicely. The text boxes have their width set to FILL_PARENT; other elements are set to WRAP_CONTENT. The gravity, by default, is left. The form on the left has weight values unset (0 by default); the form on the right has the comments text box weight set to 1. If the Name textbox had also been set be the same height.
Tip: To create a proportionate size layout on the screen, create a container object that is fill_parent, assign the children heights or widths of zero, and then assign relative weight values to each child, depending on what proportion of the screen each should take. to 1, the Name and Comments text boxes would
Within a horizontal LinearLayout, items are aligned by the position of their text base line (the first line of the first list element topmost or leftmost is considered the reference line). This is so that people scanning elements in a form shouldn't have to jump up and down to read element text in neighboring elements. This can be turned off by setting android:baselineAligned="false" in the layout XML.
TableLayout
TableLayout positions its children into rows and columns. A TableLayout consists of a number of TableRow objects, each defining a row (actually, you can have other children, which will be explained below). TableLayout containers do not display border lines for their rows, columns, or cells. Each row has zero or more cells; each cell can hold one View object. The table has as many columns as the row with the most cells. A table can leave cells empty. Cells cannot span columns, as they can in HTML. The following image shows a table layout, with the invisible cell borders displayed as dotted lines.
Columns can be hidden, can be marked to stretch to fill available screen space, or can be marked as shrinkable to force the column to shrink until the table fits the screen. See the reference documentation for this class for more details.
Compiled by fastop 2007-2 http://fastoponandroid.googlepages.com
63
AbsoluteLayout
AbsoluteLayout enables children to specify exact x/y coordinates to display on the screen, where (0,0) is the upper left corner, and values increase as you move down or to the right. Margins are not supported, and overlapping elements are allowed (although not recommended). W e generally recommend against using AbsoluteLayout unless you have good reasons to use it, because it is fairly rigid and does not work well with different device displays.
RelativeLayout
RelativeLayout lets children specify their position relative to each other (specified by ID), or to the parent. So you can align two elements by right border, or make one below another, or centered in the screen. Elements are rendered in the order given, so if the first element is centered in the screen, other elements aligning themselves to that element will be aligned relative to screen center. If using XML to specify this layout (as described later), a referenced element must be listed before you refer to it. Here is an example relative layout with the visible and invisible elements outlined. The root screen layout object is a RelativeLayout object.
This diagram shows the class names of the screen elements, followed by a list of the properties of each. Some of these properties are supported directly by the element, and some are supported by its LayoutParams member (subclass RelativeLayout for all the elements in this screen, because all elements are children of a RelativeLayout parent object). The RelativeLayout parameters are width, height, below, alignTop, toLeft, padding, and marginLeft. Note that some of these parameters support values relative to other children hence the name RelativeLayout. These include the toLeft, alignTop, and below properties, which indicate the object to the left, top, and below respectively.
64
ListView
65
<?xml version="1.0" encoding="utf-8"?> <!-- Demonstrates using a relative layout to create a form --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/a ndroid android:layout_width="fill_parent" android:layout_height="wrap_content " android:background="@drawable/blue" android:padding="10px"> <TextView id="@+id/label" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Type here:"/> <EditText id="@+id/entry" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@android:drawable/editbox_backg round"
66
Must at least
67
68
Panel A panel is a concept not backed by a specific class. It is a View of some sort that is tied in closely to a parent window, but can handle clicks and perform simple functions related to its parent. A panel floats in front of its parent, and is positioned relative to it. A common example of a panel (implemented by Android) is the options menu available to every screen. At present, there are no specific classes or methods for creating a panel it's more of a general idea. Dialog A dialog is a floating window that can have buttons, and acts as a lightweight form that is intended to, at most, perform a simple action (such as click a button) and perhaps return a value. It is not intended to persist in the history stack, contain complex layout, or perform complex actions. Android provides a default simple dialog for you with optional buttons, though you can define a dialog layout yourself. The base class is Dialog, and the helper methods to open a dialog box are the various Activity.showAlert() methods. Window An abstract class that specifies the elements of a generic window, such as the look and feel (title bar text, location and content of menus, and so on). Dialog and Activity use an implementation of this class to render a window. You should not need to implement this class. Surface A block of memory that gets composited to the screen. A Surface holds a Canvas object for drawing, and provides various helper methods to draw layers and resize the surface. You should not use this class directly; use SurfaceView instead. SurfaceView A View object that wraps a Surface for drawing, and exposes methods to specify its size and format dynamically. The camera app uses SurfaceView for its preview screen. A SurfaceView provides a way to draw independently of the UI thread for resource-intense operations (such as games or camera previews), but it uses extra memory as a result. SurfaceView supports both Canvas and OpenGL ES graphics. Canvas A drawing surface where the actual bits are composited. It has methods for standard computer drawing of bitmaps, lines, circles, rectangles, text, and so on. It is bound to a Bitmap or Surface. Canvas is the simplest, easiest way to draw 2D objects on the screen. However, it does not support hardware acceleration, as OpenGL ES does. OpenGL ES Android provides OpenGL ES libraries that you can use for fast, complex 3D images. It is much harder to use than a Canvas object, but better for 3D objects. The graphics.glutils package exposes OpenGL ES functionality.
69
70
71
<permission> Declares a security permission that can be used to restrict which applications can access components or features in your (or another) package. See the Security Model document for more information on permissions. A manifest can contain zero or more of these elements. <instrumentation> Declares the code of an instrumentation component that is available to test the functionality of this or another package. See Instrumentation for more details. A manifest can contain zero or more of these elements. <application> Root element containing declarations of the application-level components contained in the package. This element can also include global and/or default attributes for the application, such as a label, icon, theme, required permission, etc. A manifest can contain zero or one of these elements (more than one application tag is not allowed). Under it you can place zero or more of each of the following component declarations: <activity> An Activity is the primary facility for an application to interact with the user. The initial screen the user sees when launching an application is an activity, and most other screens they use will be implemented as separate activities declared with additional activity tags. Note: Every Activity must have an <activity> tag in the manifest whether it is exposed to the world or intended for use only within its own package. If an Activity has no matching tag in the manifest, you won't be able to launch it. Optionally, to support late runtime lookup of your activity, you can include one or more <intent-filter> elements to describe the actions the activity supports: <intent-filter> Declares a specific set of Intent values that a component supports, in the form of an IntentFilter. In addition to the various kinds of values that can be specified under this element, attributes can be given here to supply a unique label, icon, and other information for the action being described. <action> An Intent action that the component supports. <category> An Intent category that the component supports. <type> An Intent data MIME type that the component supports. <scheme> An Intent data URI scheme that the component supports. <authority> An Intent data URI authority that the component supports. <path> An Intent data URI path that the component supports. <receiver> An IntentReceiver allows an application to be told about changes to data or actions that happen, even if it is not currently running. As with the activity tag, you can optionally include one or more <intent-filter> elements that the receiver supports; see the activity's <intent-filter> description for more information. <service> A Service is a component that can run in the background for an arbitrary amount of time. As with the activity tag, you can optionally include one or more <intent-filter> elements that the receiver supports; see the activity's <intent-filter> description for more information. <provider> A ContentProvider is a component that manages persistent data and publishes it for access by other applications.
72
73
Using Files
Android provides access to read or write streams to files local to an application. Call Context.openFileOutput() and Context.openFileInput() with a local name and path to read and write files. Calling these methods with the same name and path strings from another application will not work; you can only access local files. If you have static files to package with your application at compile time, you can save your file in your project in res/raw/<mydatafile>, and then get it with Resources.openRawResource (R.raw. mydatafile).
74
Note that the query string isn't a standard SQL query string, but instead a URI string that describes the type of data to return. This URI consists of three parts: the string "content://"; a segment that describes what kind of data to retrieve; and finally an optional ID of a specific item of the specified content type. Here are a few more example query strings: content://media/images is the URI string that would return a list of all images on the device. content://contacts/people/ is the URI that would return a list of all contact names on the device. content://contacts/people/23 is the URI string that would return a single result row, the contact with ID = 23. Although there is a general form, query URIs are somewhat arbitrary and confusing. Therefore, Android provides a list of helper classes in the android.provider package that define these query strings so you should not need to know the actual URI value for different data types. These helper classes define a string (actually, a wrapper class called ContentURI) called CONTENT_URI for a specific data type. For instance, android.provider.contacts.People.CONTENT_URI defines the query string used to search for people in Android's built-in people content provider. Typically you will use the defined CONTENT_URI object to make a query. The only time you should need to examine or modify this string is to add an ID value to the end of the URI to retrieve a specific record. So, for example, if you were looking for record 23 in the people contacts, you might run a query as shown here: // Get the base URI for contacts. ContentURI myPerson = new ContentURI(android.provider.Contacts.People.CONTENT_URI.toURI()); // Add the ID of the record I'm looking for (I'd have to know this somehow). myPerson.addId(23); // Query for this record. Cursor cur = managedQuery(myPerson, null, null, null); This query returns a cursor over a database query result set. What columns are returned, what they're called, and what they are named are discussed next. For now, though, know that you can specify that only certain columns be returned, the sort order, and a SQL WHERE clause.
75
You should use the Activity.managedQuery() method to retrieve a managed cursor. A managed cursor handles all the niceties such as unloading itself when the application pauses, and requerying itself when the application restarts. You can ask Android to manage an unmanaged cursor for you by calling Activity.startManagingCursor(). Let's look at an example query to retrieve a list of contact names, phone numbers, and photos. // An array specifying which columns to return. // The provider exposes a list of column names it returns for a specific // query, or you can get all columns and iterate through them. string[] projection = new string[] { android.provider.BaseColumns._ID, android.provider.Contacts.PeopleColumns.NAME, android.provider.Contacts.PhonesColumns.NUMBER, android.provider.Contacts.PeopleColumns.PHOTO }; // Best way to retrieve a query; returns a managed query. Cursor managedCursor = managedQuery( android.provider.Contacts.Phones.CONTENT_URI, projection, //Which columns to return. null, // WHERE clause--we won't specify. android.provider.Contacts.PeopleColumns.NAME + " ASC"); // Order-by clause. This query will retrieve all the phone numbers stored in the phone number contacts provider. It will retrieve the name, number, and unique record ID for each contact. We can then scroll through the result set forward or backward, delete records, add records, and modify records, as described in the next sections. What the query returns A query returns a set of zero or more database records. The column names, order, and type are specific to the content provider, but every query includes a column called _id, which is the ID of the item in that row. If a query can return binary data, such as a bitmap or audio file, it will have a column with any name that holds a content:// URI that you can use to get this data (more information on how to get the file will be given later). Here is a tiresome example result set for the previous query: _id 44 13 53 name Alan Vain Bully Pulpit Rex Cars number 212 555 1234 425 555 6677 201 555 4433 photo content://images/media/123 content://images/media/128 content://images/media/332
This result set demonstrates what is returned when we specified a subset of columns to return. The optional subset list is passed in the projection parameter of the query. A content manager should list which columns it supports either by implementing a set of interfaces describing each column (see Contacts.People.Phones, which extends BaseColumns, PhonesColumns, and PeopleColumns), or by listing the column names as constants. Note that you need to know the data type of a column exposed by a content provider in order to be able to read it; the field reading method is specific to the data type, and a column's data type is not exposed programmatically. The retrieved data is exposed by a Cursor object that can be used to iterate backward or forward through the result set. You can use this cursor to read, modify, or delete rows. Adding new rows requires a different object described later. Note that by convention, every recordset includes a field named _id, which is the ID of a specific record, and a _count field, which is a count of records in the current result set. These field names are defined by BaseColumns. Querying for Files The previous query result demonstrates how a file is returned in a data set. The file field is typically (but not required to be) a string path to the file. However, the caller should never try to read and open the file directly (permissions problems for one thing can make this fail). Instead, you should call ContentResolver.openInputStream() / ContentResolver.openOutputStream() , or one of the helper functions from a content provider.
76
Reading Retrieved Data The Cursor object retrieved by the query provides access to a recordset of results. If you have queried for a specific record by ID, this set will contain only one value; otherwise, it can contain multiple values. You can read data from specific fields in the record, but you must know the data type of the field, because reading data requires a specialized method for each type of data. (If you call the string reading method on most types of columns, Android will give you the String representation of the data.) The Cursor lets you request the column name from the index, or the index number from the column name. If you are reading binary data, such as an image file, you should call ContentResolver.openOutputStream() on the string content:// URI stored in a column name. The following snippet demonstrates reading the name and phone number from our phone number query: private void getColumnData(Cursor cur){ if(!cur.first()) return; String name; String phoneNumber; int nameColumn = cur.getColumnIndex(android.provider.Contacts.PeopleColumns.NAME); int phoneColumn = cur.getColumnIndex(android.provider.Contacts.PhonesColumns.NUMBER); int pathColumn = cur.getColumnIndex(android.provider.Contacts.PeopleColumns.PHOTO); String imagePath; while (cur.next()) { // Get the field values name = cur.getString(nameColumn); phoneNumber = cur.getString(phoneColumn); imagePath = cur.getString(stringColumn); InputStream is = getContentResolver().openInputStream(imagePath); ... read the file stream into something... is.close() // Do something with the values. ... } } Note that the image field is actually a string path value. Some content provider convenience classes provide helper methods to get specific field values such as files more easily. Remember, whenever calling updating methods on the Cursor class, you must call commitUpdates() to send the changes to the database.
Modifying Data
To update an individual record, set the Cursor to the appropriate object, call the appropriate update... method, and then call commitUpdates(). To batch update a group of records (for example, to change "NY" to "New York" in all contact fields), call the ContentResolver.update() method with the columns and values to change. Remember, whenever calling updating methods on the Cursor class, you must call commitUpdates() to send the changes to the database.
77
To save a file, you can call ContentResolver().openOutputStream() with the URI as shown in the next snippet, or android.provider.Media.Images.insertImage(), in the snippet after that. // Save the name and description in a map. Key is the content provider's // column name, value is the value to save in that record field. HashMap<String, Object> values = new HashMap<String, Object>(); values.put(Media.Images.NAME, "road_trip_1"); values.put(Media.Images.DESCRIPTION, "Day 1, trip to Los Angeles"); // Add a new record without the bitmap, but with the values. // It returns the URI of the new record. ContentURI uri = getContentResolver().insert(Media.Images.CONTENT_URI, values); // Now get a handle to the file for that record, and save the data into it. // sourceBitmap is a Bitmap object representing the file to save to the database. OutputStream outStream = getContentResolver.openOutputStream(uri); sourceBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream); outStream.close(); Or, using a convenience class: android.provider.Media.Images.insertImage( getContentResolver(), sourceBitmap, "road_trip_1", "Day 1, trip to Los Angeles");
Deleting a Record
To delete a single record, call ContentResolver.delete() with the URI of a specific row, or call Cursor.deleteRow(). To delete multiple rows, call ContentResolver.delete() with the URI of the type of record to delete (for example, android.provider.Contacts.People.CONTENT_URI) and a SQL WHERE clause defining which rows to delete ( Warning: be sure to include a valid WHERE clause if deleting a general type using ContentResolver.delete(), or else you risk deleting more records than you intended!). Remember, whenever calling updating methods on the Cursor class, you must call commitUpdates() to send the changes to the database.
78
5. If you are exposing byte data, such as a bitmap file, the field that stores this data should actually be a string field with a content:// URI for that specific file. This is the field that clients will call to retrieve this data. The content provider for that content type (it can be the same content provider or another content provider for example, if you're storing a photo you would use the media content provider) should implement a field named _data for that record. The _data field lists the exact file path on the device for that file. This field is not intended to be read by the client, but by the ContentResolver. The client will call ContentResolver.openOutputStream() on the user-facing field holding the URI for the item (for example, the column named photo might have a value content://media/images/4453). The ContentResolver will request the _data field for that record, and because it has higher permissions than a client, it should be able to access that file directly and return a read wrapper for that file to the client. 6. Declare public static Strings that clients can use to specify which columns to return, or to specify field values from the cursor. Carefully document the data type of each field. Remember that file fields, such as audio or bitmap fields, are typically returned as string path values 7. Return a Cursor object over a recordset in reply to a query. This means implementing the query(), update(), insert(), and delete() methods. As a courtesy, you might want to call ContentResolver.notifyChange() to notify listeners about updated information. 8. Add a <provider> tag to AndroidManifest.xml, and use its authorities attribute to define the authority part of the content type it should handle. For example, if your content type is content://com.example.autos/auto to request a list of all autos, then authorities would be com.example.autos. Set the multiprocess attribute to true if data does not need to be synchronized between multiple running versions of the content provider. 9. If you are handling a new data type, you must define a new MIME type to return for your implementation of android.ContentProvider.getType(url). This type corresponds to the content:// URI submitted to getType(), which will be one of the content types handled by the provider. The MIME type for each content type has two forms: one for a specific record, and one for multiple records. Use the ContentURI methods to help determine what is being requested. Here is the general format for each: vnd.<yourcompanyname>.cursor.item/<contenttype> for a single row. For example: a request for train record 122, using content://com.example.transportationprovider/trains/122, might return the MIME type vnd.example.cursor.item/rail vnd.<yourcompanyname>.cursor.dir/<contenttype> for multiple rows. So, for example, a request for all train records, using content://com.example.transportationprovider/trains might return the MIME type vnd.example.cursor.dir/rail. For an example of a private content provider implementation, see the NodePadProvider class in the notepad sample application that ships with the SDK. Here is a recap of the important parts of a content URI:
A. Standard required prefix. Never modified. B. Authority part. For third-party applications, this should be a fully-qualified class to ensure uniqueness. This corresponds to the value in the <provider> element's authorities attribute: <provider class="TransportationProvider" authorities="com.example.transportationprovider" /> C. The path that the content provider uses to determine what kind of data is being requested. This can be zero or more segments: if the content provider exposes only one type of data (only trains, for example), this can be absent. If it provides several types, including subtypes, this can be several elements long: e.g., "land/bus, land/train, sea/ship, and sea/submarine" to give four possibilities. D. A specific record being requested, if any. This is the _id value of a specific record being requested. If all records of a specific type are being requested, omit this and the trailing slash: content://com.example.transportationprovider/trains
79
Contents
User IDs and File Access Using Permissions Declaring and Enforcing Permissions Enforcing Permissions in AndroidManifest.xml Enforcing Permissions when Broadcasting Intents Other Permission Enforcement
Using Permissions
A basic Android application has no permissions associated with it, meaning it can not do anything that would adversely impact the user experience or any data on the device. To make use of protected features of the device, you must include in your AndroidManifest.xml one or more <uses-permission> tags declaring the permissions that your application needs. For example, an application that needs to monitor incoming SMS messages would specify: <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.google.android.app.myapp" > <uses-permission id="android.permission.RECEIVE_SMS" /> </manifest>
80
At application install time, permissions requested by the application are granted to it by the package installer, based on checks with trusted authorities and interaction with the user. No checks with the user are done while an application is running: it either was granted a particular permission when installed, and can use that feature as desired, or the permission was not granted and any attempt to use the feature will fail without prompting the user. Often times a permission failure will result in a SecurityException being thrown back to the application. However, this is not guaranteed to occur everywhere. For example, the broadcastIntent(Intent) method checks permissions as data is being delivered to each receiver, after the method call has returned, so you will not receive an exception if there are permission failures. In almost all cases, however, a permission failure will be printed to the system log. The permissions provided by the Android system can be found at Manifest.permission. Any application may also define and enforce its own permissions, so this is not a comprehensive list of all possible permissions. A particular permission may be enforced at a number of places during your program's operation: At the time of a call into the system, to prevent an application from executing certain functions. When starting an activity, to prevent applications from launching activities of other applications. Both sending and receiving Intent broadcasts, to control who can receive your broadcast or who can send a broadcast to you. When accessing and operating on a content provider. Binding or starting a service.
81
Service permissions (applied to the <service> tag) restrict who can start or bind to the associated service. The permission is checked during Context.startService(), Context.stopService() and Context.bindService(); if the caller does not have the required permission then SecurityException is thrown from the call. IntentReceiver permissions (applied to the <receiver> tag) restrict who can broadcast Intent objects to the associated receiver. The permission is checked after Context.broadcastIntent() returns, as the system tries to deliver the submitted Intent to the given receiver. As a result, a permission failure will not result in an exception being thrown back to the caller; it will just not deliver the intent. In the same way, a permission can be supplied to Context.registerReceiver() to control who can broadcast to a programmatically registered receiver. Going the other way, a permission can be supplied when calling Context.broadcastIntent() to restrict which IntentReceiver objects are allowed to receive the broadcast (see below). ContentProvider permissions (applied to the <provider> tag) restrict who can access the data in a ContentProvider. Unlike the other components, there are two separate permission attributes you can set: android:readPermission restricts who can read from the provider, and android:writePermission restricts who can write to it. Note that if a provider is protected with both a read and write permission, holding only the write permission does not mean you can read from a provider. The permissions are checked when you first retrieve a provider (if you don't have either permission, a SecurityException will be thrown), and as you perform operations on the provider. Using ContentResolver.query() requires holding the read permission; using ContentResolver.insert(), ContentResolver.update(), ContentResolver.delete(), or Cursor.commitUpdates() requires the write permission. In all of these cases, not holding the required permission results in a SecurityException being thrown from the call.
82
Resources
This topic includes a terminology list associated with resources, and a series of examples of using resources in code. For a complete guide to the supported Android resource types, see Resources. The Android resource system keeps track of all non-code assets associated with an application. You use the Resources class to access your application's resources; the Resources instance associated with your application can generally be found through Context.getResources(). An application's resources are compiled into the application binary at build time for you by the build system. To use a resource, you must install it correctly in the source tree and build your application. As part of the build process, symbols for each of the resources are generated that you can use in your source code -- this allows the compiler to verify that your application code matches up with the resources you defined. The rest of this section is organized as a tutorial on how to use resources in an application.
Creating Resources
Android supports string, bitmap, and many other types of resource. The syntax and format of each, and where they're stored, depends upon the type of object. In general, though, you create resources from three types of files: XML files (everything but bitmaps and raw), bitmap files(for images) and Raw files (anything else, for example sound files, etc.). In fact, there are two different types of XML file as well, those that get compiled as-is into the package, and those that are used to generate resources by aapt. Here is a list of each resource
Compiled by fastop 2007-2 http://fastoponandroid.googlepages.com
83
type, the format of the file, a description of the file, and details of any XML files. You will create and store your resource files under the appropriate subdirectory under the res/ directory in your project. Android has a resource compiler (aapt) that compiles resources according to which subfolder they are in, and the format of the file. Here is a list of the file types for each resource. See the resource reference for descriptions of each type of object, the syntax, and the format or syntax of the containing file. Directory res/anim/ Resource Types XML files that are compiled into frame by frame animation or tweened animation objects .png, .9.png, .jpg files that are compiled into the following Drawable resource subtypes: To get a resource of this type, use Resource.getDrawable( id) bitmap files 9-patches (resizable bitmaps) res/layout/ res/values/ XML files that are compiled into screen layouts (or part of a screen). See layouts XML files that can be compiled into many kinds of resource. Note: unlike the other res/ folders, this one can hold any number of files that hold descriptions of resources to create rather than the resources themselves. The XML element types control where these resources are placed under the R class. While the files can be named anything, these are the typical files in this folder (the convention is to name the file after the type of elements defined within): arrays.xml to define arrays colors.xml to define color drawables and color string values. Use Resources.getDrawable() and Resources.getColor(), respectively, to get these resources. dimens.xml to define dimension value. Use Resources.getDimension() to get these resources. strings.xml to define string values (use either Resources.getString or preferably Resources.getText() to get these resources. getText() will retain any rich text styling which is usually desirable for UI strings. styles.xml to define style objects. res/xml/ Arbitrary XML files that are compiled and can be read at run time by calling Resources.getXML().
res/drawable/
Resources are compiled into the final APK file. Android creates a wrapper class, called R, that you can use to refer to these resources in your code. R contains subclasses named according to the path and file name of the source file
84
Using Resources
This section describes how to use the resources you've created. It includes the following topics: Using resources in code - How to call resources in your code to instantiate them. Referring to resources from other resources - You can reference resources from other resources. This lets you reuse common resource values inside resources. Supporting Alternate Resources for Alternate Configurations - You can specify different resources to load, depending on the language or display configuration of the host hardware. At compile time, Android generates a class named R that contains resource identifiers to all the resources in your program. This class contains several subclasses, one for each type of resource supported by Android, and for which you provided a resource file. Each class contains one or more identifiers for the compiled resources, that you use in your code to load the resource. Here is a small resource file that contains string, layout (screens or parts of screens), and image resources. Note: the R class is an auto-generated file and is not designed to be edited by hand. It will be automatically re-created as needed when the resources are updated.
package com.google.android.samples; public final class R { public static final class string { public static final int greeting=0x0204000e; public static final int start_button_text =0x02040001; public static final int submit_button_text =0x02040008; public static final int main_screen_title =0x0204000a; }; public static final class layout { public static final int start_screen=0x02070000; public static final int new_user_pane=0x02070001; public static final int select_user_list =0x02070002; }; public static final class drawable { public static final int company_logo=0x02020005; public static final int smiling_cat=0x02020006; public static final int yellow_fade_background =0x02020007; public static final int stretch_button_1 =0x02020008; }; };
85
Where resource_type is the R subclass that holds a specific type of resource. resource_name is the name attribute for resources defined in XML files, or the file name (without the extension) for resources defined by other file types. Each type of resource will be added to a specific R subclass, depending on the type of resource it is; to learn which R subclass hosts your compiled resource type, consult the resource reference document. Resources compiled by your own application can be referred to without a package name (simply as R.resource_type.resource_name). Android contains a number of standard resources, such as screen styles and button backgrounds. To refer to these in code, you must qualify them with android, as in android.R.drawable.button_background . Here are some good and bad examples of using compiled resources in code: // Load a background for the current screen from a drawable resource. this.getWindow().setBackgroundDrawableResource (R.drawable.my_background_image ); // WRONG Sending a string resource reference into a // method that expects a string. this.getWindow().setTitle(R.string.main_title); // RIGHT Need to get the title from the Resources wrapper. this.getWindow().setTitle(Resources.getText(R.string.main_title)); // Load a custom layout for the current screen. setContentView(R.layout.main_screen); // Set a slide in animation for a ViewFlipper object. mFlipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.hyperspace_in)); // Set the text on a TextView object. TextView msgTextView = (TextView)findViewByID(R.id.msg); msgTextView.setText(R.string.hello_message);
References to Resources
A value supplied in an attribute (or resource) can also be a reference to a resource. This is often used in layout files to supply strings (so they can be localized) and images (which exist in another file), though a reference can be any resource type including colors and integers. For example, if we have color resources, we can write a layout file that sets the text color size to be the value contained in one of those resources: <?xml version="1.0" encoding="utf-8"?> <EditText id="text" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:textColor="@color/opaque_red" android:text="Hello, World!" /> Note here the use of the '@' prefix to introduce a resource reference -- the text following that is the name of a resource in the form of @[package:]type/name . In this case we didn't need to specify the package because we are referencing a resource in our own package. To reference a system resource, you would need to write:
86
<?xml version="1.0" encoding="utf-8"?> <EditText id="text" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:textColor="@ android:color/opaque_red" android:text="Hello, World!" /> As another example, you should always use resource references when supplying strings in a layout file so that they can be localized: <?xml version="1.0" encoding="utf-8"?> <EditText id="text" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:textColor="@android:color/opaque_red" android:text="@string/hello_world" /> This facility can also be used to create references between resources. For example, we can create new drawable resources that are aliases for existing images: <?xml version="1.0" encoding="utf-8"?> <resources> <drawable id="my_background">@android:drawable/theme2_background</drawable> </resources>
87
88
Android supports several types of qualifiers, with various values for each. Append these to the end of the resource folder name, separated by dashes. You can add multiple qualifiers to each folder name, but they must appear in the order they are listed here. For example, a folder containing drawable resources for a fully specified configuration would look like: MyApp/ res/ drawable-en-rUS-port-160dpi-finger-qwerty-dpad-480x320/ More typically, you will only specify a few specific configuration options that a resource is defined for. You may drop any of the values from the complete list, as long as the remaining values are still in the same order: MyApp/ res/ drawable-en-rUS-finger/ drawable-port/ drawable-port-160dpi/ drawable-qwerty/
Values The two letter ISO 639-1 language code in lowercase. For example: en, fr, es The two letter ISO 3166-1-alpha-2 language code in uppercase preceded by a lowercase "r". For example: rUS, rFR, rES port, land, square 92dpi, 108dpi, etc. notouch, stylus, finger qwerty, 12key
Screen orientation Screen pixel density Touchscreen type Primary text input method Primary non-touchscreen navigation method Screen dimensions
This list does not include device-specific parameters such as carrier, branding, device/hardware, or manufacturer. Everything that an application needs to know about the device that it is running on is encoded via the resource qualifiers in the table above. Here are some general guidelines on qualified resource directory names: Values are separated by a dash (as well as a dash after the base directory name) Values are case-sensitive (even though they must be unique across all folder names in a case-insensitive way) For example, A portrait-specific drawable directory must be named drawable-port, not drawable-PORT. You may not have two directories named drawable-port and drawable-PORT, even if you had intended "port" and "PORT" to refer to different parameter values.
Compiled by fastop 2007-2 http://fastoponandroid.googlepages.com
89
Only one value for each qualifier type is supported (that is, you cannot specify drawable-rEN-rFR/ ) You can specify multiple parameters in any order to define specific configurations. For example, drawable-land-en-rUS will apply to landscape view, US-English devices. Android will try to find the most specific matching directory for the current configuration, as described below The order of parameters listed in this table is used to break a tie in case of multiple qualified directories (see the example given below) All directories, both qualified and unqualified, live under the res/ folder. Qualified directories cannot be nested (you cannot have res/drawable/drawable-en ) All resources will be referenced in code or resource reference syntax by their simple, undecorated name. So if a resource is named this: MyApp/res/drawable-port-92dp/myimage.png It would be referenced as this: R.drawable.myimage (code) @drawable/myimage (XML)
MyApp/res/drawable/myimage.png MyApp/res/drawable-en/myimage.png MyApp/res/drawable-port/myimage.png MyApp/res/drawable-port-92dpi/myimage.png 2. Pick the resources with the highest number of matching configurations. For example, if our locale is en-GB and orientation is port, then we have two candidates with one matching configuration each: MyApp/res/drawable-en/ and MyApp/res/drawable-port/ . The directory MyApp/res/drawable/ is eliminated because it has zero matching configurations, while the others have one matching configuration.
MyApp/res/drawable/myimage.png MyApp/res/drawable-en/myimage.png MyApp/res/drawable-port/myimage.png 3. Pick the final matching file based on configuration precedence, which is the order of parameters listed in the table above. That is, it is more important to match the language than the orientation, so we break the tie by picking the language-specific file, MyApp/res/drawable-en/ .
MyApp/res/drawable-en/myimage.png MyApp/res/drawable-port/myimage.png
90
Terminology
The resource system brings a number of different pieces together to form the final complete resource functionality. To help understand the overall system, here are some brief definitions of the core concepts and components you will encounter in using it: Asset: A single blob of data associated with an application. This includes object files compiled from the Java source code, graphics (such as PNG images), XML files, etc. These files are organized in a directory hierarchy that, during final packaging of the application, is bundled together into a single ZIP file. aapt: Android Asset Packaging Tool. The tool that generates the final ZIP file of application assets. In addition to collecting raw assets together, it also parses resource definitions into binary asset data. Resource Table: A special asset that aapt generates for you, describing all of the resources contained in an application/package. This file is accessed for you by the Resources class; it is not touched directly by applications. Resource: An entry in the Resource Table describing a single named value. Broadly, there are two types of resources: primitives and bags. Resource Identifier: In the Resource Table all resources are identified by a unique integer number. In source code (resource descriptions, XML files, Java source code) you can use symbolic names that stand as constants for the actual resource identifier integer. Primitive Resource: All primitive resources can be written as a simple string, using formatting to describe a variety of primitive types included in the resource system: integers, colors, strings, references to other resources, etc. Complex resources, such as bitmaps and XML describes, are stored as a primitive string resource whose value is the path of the underlying Asset holding its actual data. Bag Resource: A special kind of resource entry that, instead of a simple string, holds an arbitrary list of name/value pairs. Each name is itself a resource identifier, and each value can hold the same kinds of string formatted data as a normal resource. Bags also support inheritance: a bag can inherit the values from another bag, selectively replacing or extending them to generate its own contents. Kind: The resource kind is a way to organize resource identifiers for various purposes. For example, drawable resources are used to instantiate Drawable objects, so their data is a primitive resource containing either a color constant or string path to a bitmap or XML asset. Other common resource kinds are string (localized string primitives), color (color primitives), layout (a string path to an XML asset describing a view layout), and style (a bag resource describing user interface attributes). There is also a standard "attr" resource kind, which defines the resource identifiers to be used for naming bag items and XML attributes Style: The name of the resource kind containing bags that are used to supply a set of user interface attributes. For example, a TextView class may be given a style resource that defines its text size, color, and alignment. In a layout XML file, you associate a style with a bag using the "style" attribute, whose value is the name of the style resource. Style Class: Specifies a related set of attribute resources. This data is not placed in the resource table itself, but used to generate constants in the source code that make it easier for you to retrieve values out of a style resource and/or XML tag's attributes. For example, the Android platform defines a "View" style class that contains all of the standard view attributes: padding, visibility, background, etc.; when View is inflated it uses this style class to retrieve those values from the XML file (at which point style and theme information is applied as approriate) and load them into its instance. Configuration: For any particular resource identifier, there may be multiple different available values depending on the current configuration. The configuration includes the locale (language and country), screen orientation, screen density, etc. The current configuration is used to select which resource values are in effect when the resource table is loaded.
91
Theme: A standard style resource that supplies global attribute values for a particular context. For example, when writing an Activity the application developer can select a standard theme to use, such as the Theme.White or Theme.Black styles; this style supplies information such as the screen background image/color, default text color, button style, text editor style, text size, etc. When inflating a layout resource, most values for widgets (the text color, selector, background) if not explicitly set will come from the current theme; style and attribute values supplied in the layout can also assign their value from explicitly named values in the theme attributes if desired. Overlay: A resource table that does not define a new set of resources, but instead replaces the values of resources that are in another resource table. Like a configuration, this is applied at load time to the resource data; it can add new configuration values (for example strings in a new locale), replace existing values (for example change the standard white background image to a "Hello Kitty" background image), and modify resource bags (for example change the font size of the Theme.White style to have an 18 pt font size). This is the facility that allows the user to select between different global appearances of their device, or download files with new appearances.
Resource Reference
The Resource Reference document provides a detailed list of the various types of resource and how to use them from within the Java source code, or from other references.
92
Fast
An Android application should be fast. Well, it's probably more accurate to say that it should be efficient. There is a tendency in the computing world these days to assume that Moore's Law will solve all our problems eventually. When it comes to embedded applications, though, Moore's Law is a bit more complicated. Moore's Law doesn't really apply to mobile devices in the same way as to desktop and server applications. Moore's Law is actually a law about transistor density that is, it says that you can pack more circuitry into a given chip size, over time. For desktop and server applications, this means you can pack more "speed" into a chip of roughly the same size, resulting in the well-known performance increases. For embedded applications like cell phones, however, Moore's Law is usually exploited to make chips smaller. That is, the tendency is to use the increased density to make the same chip smaller and consume less power, to make phones smaller and make batteries last longer. As a result, embedded devices like phones are increasing in actual, raw speed much more slowly than desktop systems. For embedded devices, Moore's Law means more features and better battery life; increased speed is only an afterthought.
Compiled by fastop 2007-2 http://fastoponandroid.googlepages.com
93
That's why it's important to write efficient code: you can't assume that phones will see the same speed increases as desktops and servers. Generally speaking, writing fast code means keeping memory allocations to a minimum, writing tight code, and avoiding certain language and programming idioms that can subtly cripple performance. In object-oriented terms, most of this work takes place at the method level, on the order of actual lines of code, loops, and so on. The article on Writing Efficient Android Code will give you all the detail you need to write fast, efficient code for Android.
Responsive
It's possible to write code that wins every performance test in the world, but that still sends users in a fiery rage when they try to use it. These are the applications that aren't responsive enough the ones that feel sluggish, hang or freeze for significant periods, or take too long to process input. In Android terms, applications that are insufficiently responsive will frequently cause the system to pop up the dreaded "Application Not Responding" (ANR) message. Generally, this happens if your application cannot respond to user input. For example, if your application blocks on some I/O operation (frequently a network access), then the main application thread won't be able to process incoming user input events. After a time the system will conclude that your application has hung, and give the user the option to kill it. Similarly, if your application spends too much time building an elaborate in-memory structure, or perhaps computing the next move in a game, then again the system will conclude that your application has hung. It's always important to make sure these computations are efficient using the techniques above, but even the most efficient code still takes time to run. In both of these cases, the fix is usually to create a child thread, and do most of your work there. This keeps the main thread (which drives the user interface event loop) running, and prevents the system from concluding your code has frozen. Since such threading usually is accomplished at the class level, you can think of responsiveness as a class problem. (Compare this with basic performance, which was described above as a method-level concern.) The article on Building Responsive Android Applications discusses responsiveness in detail.
Seamless
Even if your application is fast and responsive, it can still annoy users. A common example is a background process (such as an Android Service or IntentReceiver) that pops up a UI in response to some event. This may seem harmless, and frequently developers assume that this is okay because they spend most of their time testing and using their own application. However, Android's application model is constructed explicitly to allow users to fluidly switch between applications. This means that when your background process actually fires up that UI, the user could be way over in another part of the system, doing something else such as taking a phone call. Imagine if the SMS service popped up a dialog box every time a text message came in; this would annoy users in no time. That's why the Android standard is to use Notifications for such events; this leaves the user in control. That's just one example; there are many more. For example, if Activities don't correctly implement the onPause() and other lifecycle methods, this will frequently result in data loss. Or, if your application exposes data intended to be used by other applications, you should expose it via a ContentProvider, rather than (for example) using a world-readable raw file or database. What those examples have in common is that they involve cooperating nicely with the system and other applications. The Android system is designed to treat applications as a sort of federation of loosely-coupled components, rather than chunks of black-box code. This allows you as the developer to view the entire system as just an even-larger federation of these components. This benefits you by allowing you to integrate cleanly and seamlessly with other applications, and so you should design your own code to return the favor. This is a component-level concept (as opposed to the class- and method-level concepts of performance and responsiveness, described above.) The article on Integrating with the System provides tips and best practices for writing code that cooperates nicely with the rest of the system.
94
Avoiding ANR
Given the above definition for ANR, let's examine why this can occur in Android applications and how best to structure your application to avoid ANR. Android applications normally run entirely on a single (i.e. main) thread. This means that anything your application is doing in the main thread that takes a long time to complete can trigger the ANR dialog because your application is not giving itself a chance to handle the input event or Intent broadcast. Therefore any method that runs in the main thread should do as little work as possible. In particular, Activities should do as little as possible to set up in key lifecycle methods such as onCreate() and onResume(). Potentially long running operations such as network or database operations, or computationally expensive calculations such as resizing bitmaps should be done in a child thread (or in the case of databases operations, via an asynchronous request). However, this does not mean that your main thread should block while waiting for the child thread to complete nor should you call Thread.wait() or Thread.sleep(). Instead of blocking while waiting for a child thread to complete, your main thread should provide a Handler for child threads to post back to upon completion. Designing your application in this way will allow your main thread to remain responsive to input and thus avoid ANR dialogs caused by the 5 second input event timeout. These same practices should be followed for any other threads that display UI, as they are also subject to the same timeouts. The specific constraint on IntentReciever execution time emphasizes what they were meant to do: small, discrete amounts of work in the background such as saving a setting or registering a Notification. So as with other methods called in the main thread, applications should avoid potentially long-running operations or calculations in IntentReceivers. But instead of doing intensive tasks via child threads (as the life of an IntentReceiver is short), your application should start a Service if a potentially long running action needs to be taken in response to an Intent broadcast. As a side note, you should also avoid starting an Activity from an Intent Receiver, as it will spawn a new screen that will steal focus from whatever application the user is currently has running. If your application has something to show the user in response to an Intent broadcast, it should do so using the Notification Manager.
Reinforcing Responsiveness
Generally, 100 to 200ms is the threshold beyond which users will perceive lag (or lack of "snappiness," if you will) in an application. As such, here are some additional tips beyond what you should do to avoid ANR that will help make your application seem responsive to users. If your application is doing work in the background in response to user input, show that progress is being made (ProgressBar and ProgressDialog are useful for this). For games specifically, do calculations for moves in a child thread. If your application has a time-consuming initial setup phase, consider showing a splash screen or rendering the main view as quickly as possible and filling in the information asynchronously. In either case, you should indicate somehow that progress is being made, lest the user perceive that the application is frozen.
95
Introduction
There are two basic rules for resource-constrained systems: Don't do work that you don't need to do. Don't allocate memory if you can avoid it. All the tips below follow from these two basic tenets. Some would argue that much of the advice on this page amounts to "premature optimization." While it's true that micro-optimizations sometimes make it harder to develop efficient data structures and algorithms, on embedded devices like handsets you often simply have no choice. For instance, if you bring your assumptions about VM performance on desktop machines to Android, you're quite likely to write code that exhausts system memory. This will bring your application to a crawl let alone what it will do to other programs running on the system! That's why these guidelines are important. Android's success depends on the user experience that your applications provide, and that user experience depends in part on whether your code is responsive and snappy, or slow and aggravating. Since all our applications will run on the same devices, we're all in this together, in a way. Think of this document as like the rules of the road you had to learn when you got your driver's license: things run smoothly when everybody follows them, but when you don't, you get your car smashed up. Before we get down to brass tacks, a brief observation: nearly all issues described below are valid whether or not the VM features a JIT compiler. If I have two methods that accomplish the same thing, and the interpreted execution of foo() is faster than bar(), then the compiled version of foo() will probably be as fast or faster than compiled bar(). It is unwise to rely on a compiler to "save" you and make your code fast enough.
96
97
It's also usually a good idea to create a local variable if you're going to be accessing an instance field more than once. For example: protected void drawHorizontalScrollBar(Canvas canvas, int width, int height) { if (isHorizontalScrollBarEnabled()) { int size = mScrollBar.getSize(false); if (size <= 0) { size = mScrollBarSize; } mScrollBar.setBounds(0, height - size, width, height); mScrollBar.setParams( computeHorizontalScrollRange(), computeHorizontalScrollOffset(), computeHorizontalScrollExtent(), false); } mScrollBar.draw(canvas); } That's four separate lookups of the member field mScrollBar. By caching mScrollBar in a local stack variable, the four member field lookups become four stack variable references, which are much more efficient. Incidentally, method arguments have the same performance characteristics as local variables.
98
However, the following code shows an acceptable use of foreach: public class Foo { int mSplat; static Foo mArray[] = new Foo[27]; public static void zero() { int sum = 0; for (int i = 0; i < mArray.length; i++) { sum += mArray[i].mSplat; } } public static void one() { int sum = 0; Foo[] localArray = mArray; int len = localArray.length; for (int i = 0; i < len; i++) { sum += localArray[i].mSplat; } } public static void two() { int sum = 0; for (Foo a: mArray) { sum += a.mSplat; } } } zero() retrieves the static field twice and gets the array length once for every iteration through the loop. one() pulls everything out into local variables, avoiding the lookups. two() uses the foreach syntax introduced in version 1.5 of the Java programming language. The code generated by the compiler takes care of copying the array reference and the array length to local variables, making it a good choice for walking through all elements of an array. It does generate an extra local load/store in the main loop (apparently preserving "a"), making it a teensy bit slower and 4 bytes longer than one(). To summarize all that a bit more clearly: "foreach" syntax performs well with arrays, but be cautious when using it with Iterable objects since there is additional object creation.
Avoid Enums
Enums are very convenient, but unfortunately can be painful when size and speed matter. For example, this: public class Foo { public enum Shrubbery { GROUND, CRAWLING, HANGING } } turns into a 900 byte .class file (Foo$Shubbery.class). On first use, the class initializer invokes the <init> method on objects representing each of the enumerated values. Each object gets its own static field, and the full set is stored in an array (a static field called "$VALUES"). That's a lot of code and data, just for three integers. This: Shrubbery shrub = Shrubbery.GROUND; causes a static field lookup. If "GROUND" were a static final int, the compiler would treat it as a known constant and inline it.
99
The flip side, of course, is that with enums you get nicer APIs and some compile-time value checking. So, the usual trade-off applies: you should by all means use enums for public APIs, but try to avoid them when performance matters. In some circumstances it can be helpful to get enum integer values through the ordinal() method. For example, replace: for (int n = 0; n < list.size(); n++) { if (list.items[n].e == MyEnum.VAL_X) // do stuff 1 else if (list.items[n].e == MyEnum.VAL_Y) // do stuff 2 } with: int valX = MyEnum.VAL_X.ordinal(); int valY = MyEnum.VAL_Y.ordinal(); int count = list.size(); MyItem items = list.items(); for (int { int n = 0; n < count; n++) valItem = items[n].e.ordinal();
if (valItem == valX) // do stuff 1 else if (valItem == valY) // do stuff 2 } In some cases, this will be faster, though this is not guaranteed.
100
/*package*/ static int Foo.access$100(Foo foo) { return foo.mValue; } /*package*/ static void Foo.access$200(Foo foo, int value) { foo.doStuff(value); } The inner-class code calls these static methods whenever it needs to access the "mValue" field or invoke the "doStuff" method in the outer class. What this means is that the code above really boils down to a case where you're accessing member fields through accessor methods instead of directly. Earlier we talked about how accessors are slower than direct field accesses, so this is an example of a certain language idiom resulting in an "invisible" performance hit. We can avoid this problem by declaring fields and methods accessed by inner classes to have package scope, rather than private scope. This runs faster and removes the overhead of the generated methods. (Unfortunately it also means the fields could be accessed directly by other classes in the same package, which runs counter to the standard OO practice of making all fields private. Once again, if you're designing a public API you might want to carefully consider using this optimization.)
Avoid Float
Before the release of the Pentium CPU, it was common for game authors to do as much as possible with integer math. With the Pentium, the floating point math co-processor became a built-in feature, and by interleaving integer and floating-point operations your game would actually go faster than it would with purely integer math. The common practice on desktop systems is to use floating point freely. Unfortunately, embedded processors frequently do not have hardware floating point support, so all operations on "float" and "double" are performed in software. Some basic floating point operations can take on the order of a millisecond to complete. Also, even for integers, some chips have hardware multiply but lack hardware divide. In such cases, integer division and modulus operations are performed in software something to think about if you're designing a hash table or doing lots of math.
101
Closing Notes
The best way to write good, efficient code for embedded systems is to understand what the code you write really does. If you really want to allocate an iterator, by all means use foreach syntax on a List; just make it a deliberate choice, not an inadvertent side-effect. Forewarned is forearmed! Know what you're getting into! Insert your favorite maxim here, but always think carefully about what your code is doing, and be on the lookout for ways to speed it up.
102
UI events. For example, when the user presses a key, a key-down event is added to the Activity's main thread's queue. The event handler system needs to dequeue and handle that event quickly; if it doesn't, the system concludes after a few seconds that the application is hung and offers to kill it for the user. If you have long-running code, running it inline in your Activity will run it on the event handler thread, effectively blocking the event handler. This will delay input processing, and result in the ANR dialogs. To avoid this, move your computations to a thread; click here to learn how.
Extend Themes
When it comes to the look-and-feel of the user interface, it's important to blend in nicely. Users are jarred by applications which contrast with the user interface they've come to expect. When designing your UIs, you should try and avoid rolling your own as much as possible. Instead, use a Theme. You can override or extend those parts of the theme that you need to, but at least you're starting from the same UI base as all the other applications. For all the details, click here.
103
104
105
Standard public methods of the type you would expect to see for a label component, for example setText(), setTextSize(), setTextColor() and so on. An overridden onMeasure method to determine and set the rendering size of the component. (Note that in LabelView, the real work is done by a private measureWidth() method.) An overridden onDraw() method to draw the label onto the provided canvas. You can see some sample usages of the LabelView custom component in custom_view_1.xml from the samples. In particular, you can see a mix of both android: namespace parameters and custom app: namespace parameters. These app: parameters are the custom ones that the LabelView recognizes and works with, and are defined in a styleable inner class inside of the samples R resources definition class.
106
107
The other attributes and parameters in the definition are the ones passed into the custom component constructor, and then passed through to the EditText constructor, so they are the same parameters that you would use for an EditText view. Note that it is possible to add your own parameters as well, and we will touch on this again below. And that's all there is to it. Admittedly this is a simple case, but that's the point creating custom components is only as complicated as you need it to be. A more sophisticated component may override even more on... methods and introduce some of its own helper methods, substantially customizing its properties and behavior. The only limit is your imagination and what you need the component to do.
Location-Based Services
Location-Based Services (LBS) allow software to obtain the phone's current location. This includes location obtained from the Global Positioning System (GPS) satellite constellation, but it's not limited to that. For instance, other location-based systems may come online in the future, and as they do, support for them can be added to this API.
Media APIs
The Media APIs are used to play media files. This includes both audio (such as playing MP3s or other music files, as well as game sound effects) and video (such as playing a video downloaded over the web.) Support is included for "playing URIs" that is, streaming media data over the network.
108
MapView
The MapView is an Android View that allows third-party code to display and control a Google Map. This is a complementary offering to the MapActivity provided in the Google Maps application that other Activities can use to display maps of a specific location. The table below summarizes the differences between the two approaches. Feature Embeddable directly into your own Layout? User-controlled navigation? Navigation via code? (i.e. scroll, zoom, etc. from your application code) Can fire events into your own code? MapActivity No (can only set map display) Yes No No MapView Yes Yes Yes Yes
The advantage of using the MapView over the Activity is that it gives you tight integration with your own layout; for instance, you can wrap custom controls around it, or implement fancy input mechanisms (such as using a tilt-sensor to scroll the Map based on how the user tilts the device). The disadvantage of using the MapView is that it requires more code to set up and use; if you simply wish to display a Google Map with the standard UI, it will be much easier to use the Activity.
2007 Google - Code Home - Site Terms of Service - Privacy Policy - Site Directory
109