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

Lập trình Androi part 42 docx

6 131 0
Tài liệu đã được kiểm tra trùng lặp

Đ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

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

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.. Services are

Trang 1

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

Trang 2

 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

Trang 3

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

Trang 4

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

Trang 5

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));

Trang 6

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)

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