Tải bản đầy đủ (.pdf) (6 trang)

Lập trình Androi part 42 docx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (204.23 KB, 6 trang )



279
279
Chapter
Creating a Service
As noted previously, Android services are for long-running processes that may need to
keep running even when decoupled from any activity. Examples include playing music
even if the player activity gets garbage-collected, polling the Internet for RSS/Atom feed
updates, and maintaining an online chat connection even if the chat client loses focus
due to an incoming phone call.
Services are created when manually started (via an API call) or when some activity tries
connecting to the service via interprocess communication (IPC). Services will live until no
longer needed and if RAM needs to be reclaimed, or until shut down (on their own
volition or because no one is using them anymore). Running for a long time isn’t without
its costs, though, so services need to be careful not to use too much CPU or keep
radios active too much of the time, lest the service cause the device’s battery to get
used up too quickly.
This chapter covers how you can create your own services. The next chapter covers
how you can use such services from your activities or other contexts. Both chapters will
analyze the Service/WeatherPlus sample application. This chapter focuses mostly on
the WeatherPlusService implementation. WeatherPlusService extends the weather-
fetching logic of the original Internet/Weather sample, by bundling it in a service that
monitors changes in location, so the weather is updated as the emulator is “moved.”
Service with Class
Creating a service implementation shares many characteristics with building an activity.
You inherit from an Android-supplied base class, override some life-cycle methods, and
hook the service into the system via the manifest.
So, the first step in creating a service is to extend the Service class—in our case, with
our own WeatherPlusService subclass.
Just as activities have onCreate(), onResume(), onPause(), and the like, Service


implementations have their own life-cycle methods, such as the following:
29

CHAPTER 29: Creating a Service
280
 onCreate(): As with activities, called when the service process is
created, by any means.
 onStart(): Called each time the service is started via startService().
 onDestroy(): Called as the service is being shut down.
For example, here is the onCreate() method for WeatherPlusService:
@Override
public void onCreate() {
super.onCreate();

client=new DefaultHttpClient();
format=getString(R.string.url);

mgr=(LocationManager)getSystemService(Context.LOCATION_SERVICE);
mgr.requestLocationUpdates(LocationManager.GPS_PROVIDER,
10000, 10000.0f, onLocationChange);
}
First, we chain upward to the superclass, so Android can do any setup work it needs to
have done. Then we initialize our HttpClient component and format string as we did in
the Weather demo. We then get the LocationManager instance for our application and
request to get updates as our location changes, via the GPS LocationProvider, which
will be discussed in greater detail in Chapter 32.
The onDestroy() method is much simpler:
@Override
public void onDestroy() {
super.onDestroy();


mgr.removeUpdates(onLocationChange);
}
Here, we just shut down the location-monitoring logic, in addition to chaining upward to
the superclass for any Android internal bookkeeping that might be needed.
In addition to those life-cycle methods, your service also needs to implement onBind().
This method returns an IBinder, which is the linchpin behind the IPC mechanism. We
will examine onBind() a bit more closely in the next section.
There Can Only Be One
Services, by default, run in the same process as all other components of the application,
such as its activities. Hence, you can call API methods on the service object—if you can
get your hands on it. Ideally, there would be some means, perhaps even type-safe, to
ask Android to give you the local service object. Unfortunately, at the time of this writing,
there is no such API. Hence, we are forced to cheat.
Any given service can, at most, have one copy running in memory. There might be zero
copies in memory, if the service has not been started. But even if multiple activities try
CHAPTER 29: Creating a Service
281
using the service, only one will actually be running. This is a fine implementation of the
singleton pattern—all we need to do is expose the singleton itself, so other components
can access the object.
We could expose the singleton via a public static data member or a public static getter
method. However, then we run into some memory-management risks. Since everything
referenced from a static context is immune to garbage collection, we would need to be
very careful to set the static reference to null in our service’s onDestroy(). Otherwise,
our service, while disconnected from Android, would remain in memory indefinitely, until
Android elected to shut down our process.
Fortunately, there is an alternative, and that is using onBind().
Binding allows a service to expose an API to activities (or other services) that bind to it.
Much of this infrastructure is set up to support remote services, where the bound-to API

is available via IPC, so one service can expose its API to other applications. However,
the simple act of binding itself can be useful in situations where the service and its
clients are all in the same application—the local service scenario.
To expose the service itself to activities via local binding, you must first create a public
inner class that extends the android.os.Binder class:
public class LocalBinder extends Binder {
WeatherPlusService getService() {
return(WeatherPlusService.this);
}
}
Here, our binder exposes one method: getService(), which returns the service itself. In
a remote service scenario, this would not work, because the limitations of IPC prevent
us from passing services between processes. However, for local services, this is a
perfectly fine binder.
Next, we need to return that binder object in our onBind() method:
@Override
public IBinder onBind(Intent intent) {
return(binder);
}
At this point, any client that binds to our service will be able to access the service object
itself and call methods on it. We will go into this in greater detail in the next chapter.
Manifest Destiny
Finally, you need to add the service to your AndroidManifest.xml file, for it to be
recognized as an available service for use. That is simply a matter of adding a service
element as a child of the application element, providing android:name to reference your
service class.
CHAPTER 29: Creating a Service
282
For example, here is the AndroidManifest.xml file for WeatherPlus:
<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="
package="com.commonsware.android.service">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application android:label="@string/app_name"
android:icon="@drawable/cw">
<activity android:name=".WeatherPlus" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".WeatherPlusService" />
</application>
</manifest>
Since the service class is in the same Java namespace as everything else in this
application, we can use the shorthand dot notation (".WeatherPlusService") to
reference our class.
If you want to require some permission of those who wish to start or bind to the service,
add an android:permission attribute naming the permission you are mandating. See
Chapter 28 for more details.
Lobbing One Over the Fence
Sometimes, the service needs to asynchronously alert an activity of some occurrence.
For example, the theory behind the WeatherPlusService implementation is that the
service gets “tickled” when the device (or emulator) position changes. At that point, the
service calls out to the web service and generates a new forecast web page for the
activity to display. Then the service needs to let the activity know that a new forecast is
available, so the activity can load and display it.
To interoperate with components this way, there are two major alternatives: callbacks

and broadcast Intents.
Note that if all your service needs to do is alert the user of some event, you may wish to
consider using a notification (described in Chapter 31), as that is the more normal way to
handle that requirement.
CHAPTER 29: Creating a Service
283
Callbacks
Since an activity can work with a local service directly, an activity could provide some
sort of listener object to the service, which the service could then call when needed. To
make this work, you would need to:
1. Define a Java interface for that listener object.
2. Give the service a public API to register and retract listeners.
3. Have the service use those listeners at appropriate times, to notify those
who registered the listener of some event.
4. Have the activity register and retract a listener as needed.
5. Have the activity respond to the listener-based events in some suitable
fashion.
The biggest catch is to make sure that the activity retracts the listeners when it is done.
Listener objects generally know their activity, explicitly (via a data member) or implicitly
(by being implemented as an inner class). If the service is holding onto defunct listener
objects, the corresponding activities will linger in memory, even if the activities are no
longer being used by Android. This represents a big memory leak. You may wish to use
WeakReferences, SoftReferences, or similar constructs to ensure that if an activity is
destroyed, any listeners it registers with your service will not keep that activity in
memory.
Broadcast Intents
An alternative approach, first mentioned in Chapter 17, is to have the service send a
broadcast Intent that can be picked up by the activity—assuming the activity is still
around and is not paused. We will look at the client side of this exchange in Chapter 30.
Here, let’s examine how the service can send a broadcast.

The high-level implementation of the flow is packaged in FetchForecastTask, an
AsyncTask implementation that allows us to move the Internet access to a background
thread:
class FetchForecastTask extends AsyncTask<Location, Void, Void> {
@Override
protected Void doInBackground(Location locs) {
Location loc=locs[0];
String url=String.format(format, loc.getLatitude(),
loc.getLongitude());
HttpGet getMethod=new HttpGet(url);

try {
ResponseHandler<String> responseHandler=new BasicResponseHandler();
String responseBody=client.execute(getMethod, responseHandler);
String page=generatePage(buildForecasts(responseBody));
CHAPTER 29: Creating a Service
284

synchronized(this) {
forecast=page;
}

sendBroadcast(broadcast);
}
catch (Throwable t) {
android.util.Log.e("WeatherPlus",
"Exception in updateForecast()", t);
}

return(null);

}

@Override
protected void onProgressUpdate(Void unused) {
// not needed here
}

@Override
protected void onPostExecute(Void unused) {
// not needed here
}
}
Much of this is similar to the equivalent piece of the original Weather demo. It performs
the HTTP request, converts that into a set of Forecast objects, and turn those into a web
page. The first difference, besides the introduction of the AsyncTask, is that the web
page is simply cached in the service, since the service cannot put the page directly into
the activity’s WebView. The second difference is that we call sendBroadcast(), which
takes an Intent and sends it out to all interested parties. That Intent is declared up
front in the class prologue:
private Intent broadcast=new Intent(BROADCAST_ACTION);
Here, BROADCAST_ACTION is simply a static String with a value that will distinguish this
Intent from all others:
public static final String BROADCAST_ACTION=
"com.commonsware.android.service.ForecastUpdateEvent";
Where’s the Remote? And the Rest of the Code?
In Android, services can either be local or remote. Local services run in the same
process as the launching activity. Remote services run in their own process. A detailed
discussion of remote services can be found in The Busy Coder’s Guide to Advanced
Android Development (CommonsWare, 2009).
We will return to this service in Chapter 32, at which point we will flesh out how locations

are tracked (and, in this case, mocked up).

×