1. Trang chủ
  2. » Công Nghệ Thông Tin

Lập trình Androi part 42 docx

6 130 0

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 6
Dung lượng 204,23 KB

Nội dung

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="http://schemas.android.com/apk/res/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). . package="com.commonsware.android.service"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION". is the AndroidManifest.xml file for WeatherPlus: <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android". /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <application android:label="@string/app_name" android:icon="@drawable/cw">

Ngày đăng: 01/07/2014, 21:20