LISTING 8-9: Using map projections Point myPoint = new Point; // To screen coordinates projection.toPixelsgeoPoint, myPoint; // To GeoPoint location coordinates projection.fromPixelsmyPo
Trang 1// Update the map location.
Double geoLat = location.getLatitude()*1E6;
Double geoLng = location.getLongitude()*1E6;
GeoPoint point = new GeoPoint(geoLat.intValue(),
geoLng.intValue());
mapController.animateTo(point);
double lat = location.getLatitude();
double lng = location.getLongitude();
latLongString = "Lat:" + lat + "\nLong:" + lng;
double latitude = location.getLatitude();
double longitude = location.getLongitude();
Geocoder gc = new Geocoder(this, Locale.getDefault());
try {
List<Address> addresses = gc.getFromLocation(latitude, longitude, 1);
StringBuilder sb = new StringBuilder();
if (addresses.size() > 0) {
Address address = addresses.get(0);
for (int i = 0; i < address.getMaxAddressLineIndex(); i++)
Trang 2addressString = sb.toString();
} catch (IOException e) {}
} else { latLongString = "No location found";
} myLocationText.setText("Your Current Position is:\n" +
latLongString + "\n" + addressString);
}
All code snippets in this example are part of the Chapter 8 Where Am I? project, available for download at Wrox.com.
Creating and Using Overlays
Overlays enable you to add annotations and click handling toMapViews Each Overlay lets you draw2D primitives, including text, lines, images, and shapes, directly onto a canvas, which is then overlaidonto a Map View
You can add several Overlays onto a single map All the Overlays assigned to a Map View are added
as layers, with newer layers potentially obscuring older ones User clicks are passed through the stackuntil they are either handled by an Overlay or registered as clicks on the Map View itself
Creating New Overlays
Each Overlay is a canvas with a transparent background that is layered onto a Map View and used tohandle map touch events
To add a new Overlay create a new class that extendsOverlay Override thedrawmethod to draw theannotations you want to add, and overrideonTapto react to user clicks (generally made when the usertaps an annotation added by this Overlay)
Listing 8-8 shows the framework for creating a new Overlay that can draw annotations and handleuser clicks
LISTING 8-8: Creating a new Overlay
Trang 3// Return true if screen tap is handled by this overlay
TheProjectionclass lets you translate between latitude/longitude coordinates (stored asGeoPoints)and x/y screen pixel coordinates (stored asPoints)
A map’s Projection may change between subsequentcalls to draw, so it’s good practice to get a newinstance each time Get a Map View’s Projection by callinggetProjection
Projection projection = mapView.getProjection();
Use thefromPixelandtoPixelmethods to translate from GeoPoints to Points and vice versa
For performance reasons, you can best use thetoPixelProjection method by passing a Point object to
be populated (rather than relying on the return value), as shown in Listing 8-9
LISTING 8-9: Using map projections
Point myPoint = new Point();
// To screen coordinates
projection.toPixels(geoPoint, myPoint);
// To GeoPoint location coordinates
projection.fromPixels(myPoint.x, myPoint.y);
Drawing on the Overlay Canvas
You handle Canvas drawing for Overlays by overriding the Overlay’sdrawhandler
The passed-in Canvas is the surface on which you draw your annotations, using the same techniquesintroduced in Chapter 4 for creating custom user interfaces for Views The Canvas object includes themethods for drawing 2D primitives on your map (including lines, text, shapes, ellipses, images, etc.).UsePaintobjects to define the style and color
Listing 8-10 uses a Projection to draw text and an ellipse at a given location
LISTING 8-10: A simple Map Overlay
@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
Projection projection = mapView.getProjection();
Double lat = -31.960906*1E6;
continues
Trang 4// Create and setup your paint brush
Paint paint = new Paint();
For more advanced drawing features see Chapter 11, where gradients, strokes, and
filters are introduced.
Handling Map Tap Events
To handle map taps (user clicks), override theonTapevent handler within the Overlay extension class.TheonTaphandler receives two parameters:
➤ AGeoPointthat contains the latitude/longitude of the map location tapped
➤ TheMapViewthat was tapped to trigger this event
When you are overridingonTap, the method should returntrueif it has handled a particular tap andfalseto let another Overlay handle it, as shown in Listing 8-11
LISTING 8-11: Handling map-tap events
@Override
public boolean onTap(GeoPoint point, MapView mapView) {
// Perform hit test to see if this overlay is handling the click
if ([ . perform hit test . ]) {
[ . execute on tap functionality . ]
return true;
Trang 5// If not handled return false
return false;
}
Adding and Removing Overlays
EachMapViewcontains a list of Overlays currently displayed You can get a reference to this list bycallinggetOverlays, as shown in the following snippet:
List<Overlay> overlays = mapView.getOverlays();
Adding and removing items from the list is thread-safe and synchronized, so you can modify and querythe list safely You should still iterate over the list within a synchronization block synchronized on theList
To add an Overlay onto a Map View, create a new instance of the Overlay and add it to the list, asshown in the following snippet
List<Overlay> overlays = mapView.getOverlays();
MyOverlay myOverlay = new MyOverlay();
overlays.add(myOverlay);
mapView.postInvalidate();
The added Overlay will be displayed the next time the Map View is redrawn, so it’s usually a goodpractice to callpostInvalidateafter you modify the list to update the changes on the map display
Annotating ‘Where Am I?’
This final modification to ‘‘Where Am I?’’ creates and adds a new Overlay that displays a white circle
at the device’s current position
1. Start by creating a newMyPositionOverlayOverlay class in the Where Am I? project
Trang 6return false;
} }
2. Create a new instance variable to store the current Location, and add setter and getter ods for it
3. Override thedrawmethod to add a small white circle at the current location
private final int mRadius = 5;
@Override
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
Projection projection = mapView.getProjection();
if (shadow == false) { // Get the current location Double latitude = location.getLatitude()*1E6;
Double longitude = location.getLongitude()*1E6;
GeoPoint geoPoint;
geoPoint = new GeoPoint(latitude.intValue(),longitude.intValue());
// Convert the location to screen pixels Point point = new Point();
projection.toPixels(geoPoint, point);
RectF oval = new RectF(point.x - mRadius, point.y - mRadius,
point.x + mRadius, point.y + mRadius);
// Setup the paint Paint paint = new Paint();
// Draw the marker canvas.drawOval(oval, paint);
canvas.drawRoundRect(backRect, 5, 5, backPaint);
Trang 7point.x + 2*mRadius, point.y, paint);
}
super.draw(canvas, mapView, shadow);
}
4. Now open theWhereAmIActivity class, and add theMyPositionOverlayto theMapView
Start by adding a new instance variable to store theMyPositionOverlay, then override
onCreateto create a new instance of the class, and add it to theMapView’s Overlay list
// Add the MyPositionOverlay
positionOverlay = new MyPositionOverlay();
List<Overlay> overlays = myMapView.getOverlays();
String provider = locationManager.getBestProvider(criteria, true);
Location location = locationManager.getLastKnownLocation(provider);
Trang 8myLocationText = (TextView)findViewById(R.id.myLocationText);
String addressString = "No address found";
if (location != null) { // Update my location marker
positionOverlay.setLocation(location);
// Update the map location.
Double geoLat = location.getLatitude()*1E6;
Double geoLng = location.getLongitude()*1E6;
GeoPoint point = new GeoPoint(geoLat.intValue(),
geoLng.intValue());
mapController.animateTo(point);
double lat = location.getLatitude();
double lng = location.getLongitude();
latLongString = "Lat:" + lat + "\nLong:" + lng;
double latitude = location.getLatitude();
double longitude = location.getLongitude();
Geocoder gc = new Geocoder(this, Locale.getDefault());
try { List<Address> addresses = gc.getFromLocation(latitude,
longitude, 1);
StringBuilder sb = new StringBuilder();
if (addresses.size() > 0) { Address address = addresses.get(0);
for (int i = 0; i < address.getMaxAddressLineIndex(); i++) sb.append(address.getAddressLine(i)).append("\n");
sb.append(address.getLocality()).append("\n");
sb.append(address.getPostalCode()).append("\n");
sb.append(address.getCountryName());
} addressString = sb.toString();
} catch (IOException e) {}
} else { latLongString = "No location found";
} myLocationText.setText("Your Current Position is:\n" +
latLongString + "\n" + addressString);
}
All code snippets in this example are part of the Chapter 8 Where Am I? project, available for download at Wrox.com.
When run, your application will display your current device location with a white circle and supportingtext, as shown in Figure 8-6
Trang 9It’s worth noting that this is not the preferred technique for displaying your current
location on a map This functionality is implemented natively by Android through
theMyLocationOverlayclass If you want to display and follow your current
location, you should consider using (or extending) this class, as shown in the next
section, instead of implementing it manually as shown here.
Introducing My Location Overlay
FIGURE 8-6
TheMyLocationOverlayclass is a special Overlay designed
to show your current location and orientation on aMapView
To use My Location Overlay you need to create a new
instance, passing in the application Context and target Map
View, and add it to theMapView’s Overlay list, as shown
You can use My Location Overlay to display both your
current location (represented as a flashing blue marker) and
your current orientation (shown as a compass on the map
display)
The following snippet shows how to enable both the
compass and marker; in this instance the Map View’s
MapControlleris also passed in, allowing the Overlay to
automatically scroll the map if the marker moves
offscreen
myLocationOverlay.enableCompass();
myLocationOverlay.enableMyLocation(mapView.getMapController());
Introducing Itemized Overlays and Overlay Items
OverlayItems are used to supply simple maker functionality to your Map Views via the
ItemizedOverlayclass
ItemizedOverlaysprovide a convenient shortcut for adding markers to a map, letting you assign
a marker image and associated text to a particular geographical position TheItemizedOverlayinstance handles the drawing, placement, click handling, focus control, and layout optimization ofeachOverlayItemmarker for you
Trang 10To add anItemizedOverlaymarker layer to your map, start by creating a new class that extendsItemizedOverlay<OverlayItem>, as shown in Listing 8-12.
ItemizedOverlayis a generic class that lets you create extensions based on any
OverlayItem-derived subclass.
Within the constructor you need to call through to the superclass after defining the bounds for yourdefault marker You must then callpopulateto trigger the creation of eachOverlayItem;populatemust be called whenever the data used to create the items changes
Within the implementation, overridesizeto return the number of markers to display andcreateItem
to create a new item based on the index of each marker
LISTING 8-12: Creating a new Itemized Overlay
import android.graphics.drawable.Drawable;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.OverlayItem;
public class MyItemizedOverlay extends ItemizedOverlay<OverlayItem> {
public MyItemizedOverlay(Drawable defaultMarker) {
public int size() {
// Return the number of markers in the collection
return 1;
}
}
Trang 11To add anItemizedOverlayimplementation to your map, create a new instance (passing in the able marker image to use for each marker) and add it to the map’s Overlay list.
Draw-List<Overlay> overlays = mapView.getOverlays();
MyItemizedOverlay markers = new
MyItemizedOverlay(r.getDrawable(R.drawable.marker));
overlays.add(markers);
Note that the map markers placed by the Itemized Overlay use state to indicate if
when a marker has been selected.
In Listing 8-12, the list of Overlay items is static and defined in code More typically your Overlay itemswill be a dynamic ArrayList to which you will want to add and remove items at run time
Listing 8-13 shows the skeleton class for a dynamic Itemized Overlay implementation, backed by anArrayList, and supporting the addition and removal of items at run time
LISTING 8-13: Skeleton code for a dynamic Itemized Overlay
public class MyDynamicItemizedOverlay extends ItemizedOverlay<OverlayItem>
{
private ArrayList<OverlayItem> items;
public MyDynamicItemizedOverlay(Drawable defaultMarker) {
Trang 12Pinning Views to the Map and Map Positions
You can pin any View-derived object to a Map View (including layouts and other View Groups),attaching it to either a screen position or a geographical map location
In the latter case, the View will move to follow its pinned position on the map, effectively acting as aninteractive map marker As a more resource-intensive solution, this is usually reserved for supplying thedetail ‘‘balloons’’ often displayed on mashups to provide further detail when a marker is clicked.You implement both pinning mechanisms by callingaddViewon theMapView, usually from theonCreate
oronRestoremethods within theMapActivitycontaining it Pass in the View you want to pin and thelayout parameters to use
TheMapView.LayoutParamsparameters you pass in toaddViewdetermine how, and where, the View isadded to the map
To add a new View to the map relative to the screen, specify a newMapView.LayoutParams,includingarguments that set the height and width of the View, the x/y screen coordinates to pin to, and thealignment to use for positioning, as shown in Listing 8-14
LISTING 8-14: Pinning a View to a map
mapView.addView(editText1, screenLP);
To pin a View relative to a physical map location, pass four parameters when constructing the newMap ViewLayoutParams, representing the height, width, GeoPoint to pin to, and layout alignment asshown in Listing 8-15
LISTING 8-15: Pinning a View to a geographical location
Double lat = 37.422134*1E6;
Double lng = -122.084069*1E6;
Trang 13MapView.LayoutParams geoLP;
geoLP = new MapView.LayoutParams(MapView.LayoutParams.WRAP_CONTENT,
MapView.LayoutParams.WRAP_CONTENT, geoPoint,
MAPPING EARTHQUAKES EXAMPLE
The following step-by-step guide demonstrates how to build a map-based Activity for the Earthquakeproject you started in Chapter 5 The newMapActivitywill display a map of recent earthquakes usingtechniques you learned within this chapter
1. Create a new earthquake_map.xml layout resource that includes aMapView, being sure toinclude anandroid:idattribute and anandroid:apiKeyattribute that contains your AndroidMaps API key
Trang 14public class EarthquakeMap extends MapActivity {
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.earthquake_map);
}
@Override protected boolean isRouteDisplayed() { return false;
} }
3. Update the application manifest to include your newEarthquakeMapActivity and import themap library
4. Add a new menu option to theEarthquakeActivity to display theEarthquakeMapActivity
4.1. Start by adding a new string to the strings.xml resource for the menu text
<string name="menu_update">Refresh Earthquakes</string>
<string name="auto_update_prompt">Auto Update?</string>
<string name="update_freq_prompt">Update Frequency</string>
Trang 154.2. Then add a new menu identifier before modifying theonCreateOptionsMenu
han-dler to add the new Menu Item It should use the text defined in Step 4.1, and whenselected it should fire an Intent to explicitly start theEarthquakeMapActivity
static final private int MENU_EARTHQUAKE_MAP = Menu.FIRST+2;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, MENU_UPDATE, Menu.NONE, R.string.menu_update);
menu.add(0, MENU_PREFERENCES, Menu.NONE,
R.string.menu_preferences);
Intent startMap = new Intent(this, EarthquakeMap.class);
menu.add(0, MENU_EARTHQUAKE_MAP,
Menu.NONE, R.string.menu_earthquake_map).setIntent(startMap);
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
Projection projection = mapView.getProjection();
5.1. Add a new constructor that accepts aCursorto the current earthquake data, and
store that Cursor as an instance variable
Cursor earthquakes;
public EarthquakeOverlay(Cursor cursor, ContentResolver resolver) {
super();
earthquakes = cursor;
Trang 165.2. Create a newrefreshQuakeLocationsmethod that iterates over the results Cursor
and extracts the location of each earthquake, extracting the latitude and longitudebefore storing each coordinate in a List ofGeoPoints
ArrayList<GeoPoint> quakeLocations;
private void refreshQuakeLocations() {
if (earthquakes.moveToFirst())
do { Double lat = earthquakes.getFloat(EarthquakeProvider.LATITUDE_COLUMN) * 1E6; Double lng =
earthquakes.getFloat(EarthquakeProvider.LONGITUDE_COLUMN) * 1E6; GeoPoint geoPoint = new GeoPoint(lng.intValue(),
lat.intValue());
quakeLocations.add(geoPoint);
} while(earthquakes.moveToNext());
}
5.3. CallrefreshQuakeLocationsfrom the Overlay’s constructor Also register a
DataSetObserveron the results Cursor that refreshes the Earthquake Location list
if a change in the Earthquake Cursor is detected
public EarthquakeOverlay(Cursor cursor) { super();
} });
}
5.4. Complete theEarthquakeOverlayby overriding thedrawmethod to iterate over the
list ofGeoPoints, drawing a marker at each earthquake location In this example
a simple red circle is drawn, but you could easily modify it to include additionalinformation, such as by adjusting the size of each circle based on the magnitude ofthe quake
int rad = 5;
@Override public void draw(Canvas canvas, MapView mapView, boolean shadow) {
Projection projection = mapView.getProjection();
// Create and setup your paint brush Paint paint = new Paint();
paint.setARGB(250, 255, 0, 0);
paint.setAntiAlias(true);
paint.setFakeBoldText(true);
Trang 17if (shadow == false) { for (GeoPoint point : quakeLocations) { Point myPoint = new Point();
}
6. Return to theEarthquakeMapclass Within theonCreatemethod, create a Cursor that
returns the earthquakes you want to display on the map Use this Cursor to create a newEarthquakeOverlaybefore adding the new instance to the Map View’s list of Overlays
MapView earthquakeMap = (MapView)findViewById(R.id.map_view);
EarthquakeOverlay eo = new EarthquakeOverlay(earthquakeCursor);
Trang 188. If you run the application and select Earthquake Map from the main menu, your applicationshould appear as shown in Figure 8-7.
All code snippets in this example are part of the Chapter 8 Earthquake project, available for download at Wrox.com.
SUMMARY
FIGURE 8-7
Location-based services, the Geocoder, and MapViews are
available to create intuitive, location-aware applications that
feature geographical information
This chapter introduced the Geocoder and showed how to
perform forward and reverse geocoding lookups to translate
between map coordinates and street addresses You were
introduced to location-based services, used to find the
cur-rent geographical position of a device You also used them
to track movement and create proximity alerts
Then you created interactive map applications Using
Over-lays and Views you annotatedMapViewswith 2D graphics,
as well as markers in the form ofOverlayItemsand Views
(including View Groups and layouts)
In Chapter 9 you’ll learn how to work from the background
You’ll be introduced to the Service component and learn
how to move processing onto background threads To
inter-act with the user while hidden from view, you’ll use Toasts
to display transient messages and the Notification Manager
to ring, vibrate, and flash the phone
Trang 19Working in the Background
WHAT’S IN THIS CHAPTER?
➤ Creating, starting, and stopping Services
➤ Binding Services to Activities
➤ Setting Service priority to foreground
➤ Using AsyncTasks to manage background processing
➤ Creating background threads and using Handlers to synchronize with
the GUI thread
➤ Displaying Toasts
➤ Using the Notification Manager to notify users of application events
➤ Creating insistent and ongoing Notifications
➤ Using Alarms to schedule application events
Android offers theServiceclass to create application components specifically to handle tions and functionality that should run invisibly, without a user interface
opera-Android accords Services a higher priority than inactive Activities, so they’re less likely to bekilled when the system requires resources In fact, should the run time prematurely terminate aService that’s been started, it can be configured to restart as soon as sufficient resources becomeavailable In extreme cases, the termination of a Service — such as an interruption in musicplayback — will noticeably affect the user experience, and in these cases a Service’s priority can
be raised to the equivalent of a foreground Activity
By using Services, you can ensure that your applications continue to run and respond to events,even when they’re not in active use
Services run without a dedicated GUI, but, like Activities and Broadcast Receivers, they still cute in the main thread of the application’s process To help keep your applications responsive,
Trang 20exe-you’ll learn to move time-consuming processes (like network lookups) into background threads usingtheThreadandAsyncTaskclasses.
Android offers several techniques for applications to communicate with users without an Activity.You’ll learn how to use Notifications and Toasts to alert and update users without interrupting theactive application
Toasts are a transient, non-modal dialog-box mechanism used to display information to users
with-out stealing focus from the active application You’ll learn to display Toasts from any applicationcomponent to send unobtrusive on-screen messages to your users
Where Toasts are silent and transient, Notifications represent a more robust mechanism for alerting
users In many cases, when the user isn’t actively using the mobile phone it sits silent and unwatched in
a pocket or on a desk until it rings, vibrates, or flashes Should a user miss these alerts, status bar iconsare used to indicate that an event has occurred All these attention-grabbing antics are available to yourAndroid application through Notifications
Alarms provide a mechanism for firing Intents at set times, outside the control of your application lifecycle You’ll learn to use Alarms to start Services, open Activities, or broadcast Intents based on eitherthe clock time or the time elapsed since device boot An Alarm will fire even after its owner applicationhas been closed, and can (if required) wake a device from sleep
INTRODUCING SERVICES
Unlike Activities, which present a rich graphical interface to users, Services run in the background —updating your Content Providers, firing Intents, and triggering Notifications They are the perfectmeans of performing ongoing or regular processing and of handling events even when your applica-tion’s Activities are invisible or inactive, or have been closed
Services are started, stopped, and controlled from other application components, including otherServices, Activities, and Broadcast Receivers If your application performs actions that don’t dependdirectly on user input, Services may be the answer
Started Services always have higher priority than inactive or invisible Activities, making them less likely
to be terminated by the run time’s resource management The only reason Android will stop a Serviceprematurely is to provide additional resources for a foreground component (usually an Activity) Whenthat happens, your Service will be restarted automatically when resources become available
If your Service is interacting directly with the user (for example, by playing music) it may be necessary toincrease its priority to that of a foreground Activity This will ensure that your Service isn’t terminatedexcept in extreme circumstances, but reduces the run time’s ability to manage its resources, potentiallydegrading the overall user experience
Applications that update regularly but only rarely or intermittently need user interaction are goodcandidates for implementation as Services MP3 players and sports-score monitors are examples ofapplications that should continue to run and update without a visible Activity
Further examples can be found within the software stack itself: Android implements several Services,including the Location Manager, Media Controller, and Notification Manager
Trang 21Creating and Controlling Services
In the following sections you’ll learn how to create a new Service, and how to start and stop it usingIntents and thestartServicemethod Later you’ll learn how to bind a Service to an Activity to provide
a richer communications interface
public void onCreate() {
// TODO: Actions to perform when service is created.
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Replace with service binding implementation.
TheonStartCommandhandler replaces theonStartevent that was used prior to Android 2.0 By trast, it enables you to tell the system how to handle restarts if the Service is killed by the system prior
con-to an explicit call con-tostopServiceorstopSelf
The following snippet extends Listing 9-1 to show the skeleton code for overriding theonStartCommandhandler Note that it returns a value that controls how the system will respond if the Service is restartedafter being killed by the run time
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Launch a background thread to do processing.
return Service.START_STICKY;
}
Services are launched on the main Application thread, meaning that any processing done in theonStartCommandhandler will happen on the main GUI thread The standard pattern for implementing
Trang 22a Service is to create and run a new thread fromonStartCommandto perform the processing in thebackground and stop the Service when it’s complete (you will be shown how to create and managebackground threads later in this chapter).
This pattern letsonStartCommandcomplete quickly, and lets you control the restart behavior using one
of the followingServiceconstants:
➤ START_STICKY Describes the standard behavior, which is similar to the way in whichonStartwas implemented prior to Android 2.0 If you return this value,onStartCommandwill
be called any time your Service restarts after being terminated by the run time Note that on arestart the Intent parameter passed in toonStartCommandwill benull
This mode is typically used for Services that handle their own states, and that are explicitlystarted and stopped as required (viastartServiceandstopService) This includes Servicesthat play music or handle other ongoing background tasks
➤ START_NOT_STICKY This mode is used for Services that are started to process specific actions
or commands Typically they will usestopSelfto terminate once that command has beencompleted
Following termination by the run time, Services set to this mode will restart only if there arepending start calls If nostartServicecalls have been made since the Service was terminated,the Service will be stopped without a call being made toonStartCommand
This mode is ideal for Services that handle specific requests, particularly regular processingsuch as updates or network polling Rather than restarting the Service during a period ofresource contention, it’s often more prudent tolet the Service stop and retry at the next sched-uled interval
➤ START_REDELIVER_INTENT In some circumstances you will want to ensure that the mands you have requested from your Service are completed
com-This mode is a combination of the first two — if the Service is terminated by the run time, it
will restart only if there are pending start calls or the process was killed prior to its calling
event handler to let you perform actions when the Service started Implementing
theonStarthandler is now the equivalent of overridingonStartCommandand
The restart mode you specify in youronStartCommandreturn value will affect the parameter valuespassed in to subsequent calls
Trang 23Initially the Intent will be the parameter you passed in tostartServiceto start your Service Aftersystem-based restarts it will be either null, in the case ofSTART_STICKYmode, or the original Intent, ifthe mode is set toSTART_REDELIVER_INTENT.
Theflagparameter can be used to discover how the Service was started In particular you can use thecode snippet shown in Listing 9-2 to determine if either of the following cases is true:
➤ START_FLAG_REDELIVERY Indicates that the Intent parameter is a redelivery caused by thesystem run time’s having terminated the Service before it was explicitly stopped by a call tostopSelf
➤ START_FLAG_RETRY Indicates that the Service has been restarted after an abnormal tion Passed in when the Service was previously set toSTART_STICKY
termina-LISTING 9-2: Determining the cause of a system start
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if ((flags & START_FLAG_RETRY) == 0) {
// TODO If it’s a restart, do something.
Registering a Service in the Manifest
Once you’ve constructed a new Service, you have to register it in the application manifest
Do this by including a<service>tag within theapplicationnode Use therequires-permissionattribute to require a uses-permission for other applications to access this Service
The following is theservicetag you’d add for the skeleton Service you created earlier:
<service android:enabled="true" android:name=".MyService"/>
Self-Terminating a Service
Once your Service has completed the actions or processing it was started for, you should make a call
tostopSelf, either without a parameter to force a stop, or by passing in astartIdvalue to insure cessing has been completed for each instance ofstartServicecalled so far, as shown in the followingsnippet:
pro-stopSelf(startId);
By explicitly stopping the Service when your processing is complete, you allow the system to recoverthe resources otherwise required to keep it running Due to the high priority of Services they are notcommonly killed by the run time, so self-termination can significantly improve the resource footprint
of your application
Trang 24Starting, Controlling, and Interacting with a Service
To start a Service, callstartService; you can either use an action to implicitly start a Service with theappropriate Intent Receiver registered, or you can explicitly specify the Service using its class If theService requires permissions that your application does not have, the call tostartServicewill throw aSecurityException
In both cases you can pass values in to the Service’sonStarthandler by adding extras to the Intent, asshown in Listing 9-3, which demonstrates both techniques available for starting a Service
LISTING 9-3: Starting a Service
// Implicitly start a Service
Intent myIntent = new Intent(MyService.ORDER_PIZZA);
myIntent.putExtra("TOPPING", "Margherita");
startService(myIntent);
// Explicitly start a Service
startService(new Intent(this, MyService.class));
MyServiceclass and use an Intent Filter to register the Service as a provider of
MY_ACTION.
To stop a Service usestopService, passing an Intent that defines the Service to stop Listing 9-4 firststarts and then stops a Service both explicitly and by using the component name returned from a call
tostartService
LISTING 9-4: Stopping a Service
ComponentName service = startService(new Intent(this, BaseballWatch.class));
// Stop a service using the service name.
stopService(new Intent(this, service.getClass()));
// Stop a service explicitly.
try {
Class serviceClass = Class.forName(service.getClassName());
stopService(new Intent(this, serviceClass));
} catch (ClassNotFoundException e) {}
IfstartServiceis called on a Service that’s already running, the Service’sonStartCommandhandler will
be executed again Calls tostartServicedo not nest, so a single call tostopServicewill terminate it
no matter how many timesstartServicehas been called
An Earthquake Monitoring Service Example
In this chapter you’ll modify the Earthquake example you started in Chapter 5 (and continued toenhance in Chapters 6, 7, and 8) In this example you’ll move the earthquake updating and processingfunctionality into a separate Service component
Trang 25Later in this chapter you’ll build additional functionality within this Service,
starting by moving the network lookup and XML parsing to a background thread.
Later you’ll use Toasts and Notifications to alert users of new earthquakes.
1. Start by creating a newEarthquakeServicethat extendsService
public void onCreate() {
// TODO: Initialize variables, get references to GUI objects
<service android:enabled="true" android:name=".EarthquakeService"/>
3. Move therefreshEarthquakesandaddNewQuakemethods out of theEarthquakeActivityand into theEarthquakeService
You’ll need to remove the calls toaddQuakeToArrayandloadQuakesFromProvider(leaveboth of these methods in the Earthquake Activity because they’re still required) In the
EarthquakeServicealso remove all references to the earthquakes ArrayList
private void addNewQuake(Quake _quake) {
ContentResolver cr = getContentResolver();
// Construct a where clause to make sure we don’t already have
// this earthquake in the provider.