地震监视器服务例子

   

在这一章,你将修改在第5章创建的地震监视器的例子(在第67章增加了一些功能)。在这个例子中,你将将地震更新和处理的功能移到一个独立的Service组件中。

 

在本章的后面,你将为这个Service添加额外的功能,将网络查询和XML解析放到后台线程中。再之后,你将使用ToastNotification来警告用户有新的地震信息。

 

1. 创建一个扩展了Service类的EarthquakeService类。

 

package com.paad.earthquake;

import android.app.Service;

import android.content.Intent;

import android.os.IBinder;

import java.util.Timer;

import java.util.TimerTask;

public class EarthquakeService extends Service {

 

@Override

public void onStart(Intent intent, int startId) {

// TODO: Actions to perform when service is started.

}

 

@Override

public void onCreate() {

// TODO: Initialize variables, get references to GUI objects

}

 

@Override

public IBinder onBind(Intent intent) {

return null;

}

}

 

2. 将这个Service添加到manifest中,通过在application节点中添加一个新的service标签来完成。

 

<service android:enabled=”true” android:name=”.EarthquakeService”></service>

 

3. refreshEarthquakesaddNewQuake方法从Earthquake Activity中移到EarthquakeService中。

 

你还需要移除对addQuakeToArrayloadQuakesFromProvider的调用(仍然把这两方法留在Earthquake Activity中,是因为它们在Activity中仍然需要)。在EarthquakeService中还需要移除所有对earthquakes的引用。

 

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.

String w = EarthquakeProvider.KEY_DATE + “ = “ + _quake.getDate().getTime();

// If the earthquake is new, insert it into the provider.

Cursor c = cr.query(EarthquakeProvider.CONTENT_URI, null, w, null, null);

if (c.getCount()==0){

ContentValues values = new ContentValues();

values.put(EarthquakeProvider.KEY_DATE, _quake.getDate().getTime());

values.put(EarthquakeProvider.KEY_DETAILS, _quake.getDetails());

double lat = _quake.getLocation().getLatitude();

double lng = _quake.getLocation().getLongitude();

values.put(EarthquakeProvider.KEY_LOCATION_LAT, lat);

values.put(EarthquakeProvider.KEY_LOCATION_LNG, lng);

values.put(EarthquakeProvider.KEY_LINK, _quake.getLink());

values.put(EarthquakeProvider.KEY_MAGNITUDE, _quake.getMagnitude());

cr.insert(EarthquakeProvider.CONTENT_URI, values);

}

c.close();

}

 

private void refreshEarthquakes() {

// Get the XML

URL url;

try

{

String quakeFeed = getString(R.string.quake_feed);

url = new URL(quakeFeed);

URLConnection connection;

connection = url.openConnection();

HttpURLConnection httpConnection = (HttpURLConnection)connection;

int responseCode = httpConnection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK)

{

InputStream in = httpConnection.getInputStream();

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

DocumentBuilder db = dbf.newDocumentBuilder();

// Parse the earthquake feed.

Document dom = db.parse(in);

Element docEle = dom.getDocumentElement();

// Get a list of each earthquake entry.

NodeList nl = docEle.getElementsByTagName(“entry”);

if (nl != null && nl.getLength() > 0)

{

for (int i = 0 ; i < nl.getLength(); i++)

{

Element entry = (Element)nl.item(i);

Element title;

title = (Element)entry.getElementsByTagName(“title”).item(0);

Element g;

g = (Element)entry.getElementsByTagName(“georss:point”).item(0);

Element when;

when = (Element)entry.getElementsByTagName(“updated”).item(0);

Element link = (Element)entry.getElementsByTagName(“link”).item(0);

String details = title.getFirstChild().getNodeValue();

String hostname = “http://earthquake.usgs.gov/”;

String linkString = hostname + link.getAttribute(“href”);

String point = g.getFirstChild().getNodeValue();

String dt = when.getFirstChild().getNodeValue();

SimpleDateFormat sdf;

sdf = new SimpleDateFormat(“yyyy-MM-dd’T’hh:mm:ss’Z’“);

Date qdate = new GregorianCalendar(0,0,0).getTime();

try

{

qdate = sdf.parse(dt);

} catch (ParseException e)

{

e.printStackTrace();

}

String[] location = point.split(“ “);

Location l = new Location(“dummyGPS”);

l.setLatitude(Double.parseDouble(location[0]));

l.setLongitude(Double.parseDouble(location[1]));

String magnitudeString = details.split(“ “)[1];

int end = magnitudeString.length()-1;

double magnitude;

magnitude = Double.parseDouble(magnitudeString.substring(0, end));

details = details.split(“,”)[1].trim();

Quake quake = new Quake(qdate, details, l, magnitude, linkString);

// Process a newly found earthquake

addNewQuake(quake);

}

}

}

} catch (MalformedURLException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

} catch (ParserConfigurationException e) {

e.printStackTrace();

} catch (SAXException e) {

e.printStackTrace();

}

finally {

}

}

 

4. Earthquake Activity中,创建一个新的refreshEarthquakes方法。它应该显式地启动EarthquakeService

 

private void refreshEarthquakes() {

startService(new Intent(this, EarthquakeService.class));

}

 

5. 返回到EarthquakeService中。重写onStartonCreate方法来支持一个新的定时器,用于更新地震列表。使用第6章创建的SharedPreference对象来决定地震数据是否要正常更新。

 

private Timer updateTimer;

private float minimumMagnitude;

@Override

public void onStart(Intent intent, int startId) {

// Retrieve the shared preferences

SharedPreferences prefs = getSharedPreferences(Preferences.USER_PREFERENCE, Activity.MODE_PRIVATE);

int minMagIndex = prefs.getInt(Preferences.PREF_MIN_MAG, 0);

if (minMagIndex < 0)

minMagIndex = 0;

int freqIndex = prefs.getInt(Preferences.PREF_UPDATE_FREQ, 0);

if (freqIndex < 0)

freqIndex = 0;

boolean autoUpdate = prefs.getBoolean(Preferences.PREF_AUTO_UPDATE, false);

Resources r = getResources();

int[] minMagValues = r.getIntArray(R.array.magnitude);

int[] freqValues = r.getIntArray(R.array.update_freq_values);

minimumMagnitude = minMagValues[minMagIndex];

int updateFreq = freqValues[freqIndex];

updateTimer.cancel();

if (autoUpdate) {

updateTimer = new Timer(“earthquakeUpdates”);

updateTimer.scheduleAtFixedRate(doRefresh, 0, updateFreq*60*1000);

}

else

refreshEarthquakes();

};

 

private TimerTask doRefresh = new TimerTask() {

public void run() {

refreshEarthquakes();

}

};

 

@Override

public void onCreate() {

updateTimer = new Timer(“earthquakeUpdates”);

}

 

6. EarthquakeService现在会按照更新时间来更新Earthquake Provider。这个信息现在还没有传回到Earthquake Activity中的ListViewEarthquakeMap Activity中。

 

为了通知其它的组件和其它对地震数据感兴趣的应用程序,修改EarthquakeService,在一个新的地震数据添加时广播一个Intent

 

6.1. 修改addNewQuake方法来调用新的announceNewQuake方法。

 

public static final String NEW_EARTHQUAKE_FOUND = “New_Earthquake_Found”;

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.

String w = EarthquakeProvider.KEY_DATE +“ = “ + _quake.getDate().getTime();

// If the earthquake is new, insert it into the provider.

Cursor c = cr.query(EarthquakeProvider.CONTENT_URI, null, w, null, null);

if (c.getCount()==0)

{

ContentValues values = new ContentValues();

values.put(EarthquakeProvider.KEY_DATE, _quake.getDate().getTime());

values.put(EarthquakeProvider.KEY_DETAILS, _quake.getDetails());

double lat = _quake.getLocation().getLatitude();

double lng = _quake.getLocation().getLongitude();

values.put(EarthquakeProvider.KEY_LOCATION_LAT, lat);

values.put(EarthquakeProvider.KEY_LOCATION_LNG, lng);

values.put(EarthquakeProvider.KEY_LINK, _quake.getLink());

values.put(EarthquakeProvider.KEY_MAGNITUDE, _quake.getMagnitude());

cr.insert(EarthquakeProvider.CONTENT_URI, values);

announceNewQuake(_quake);

}

c.close();

}

 

private void announceNewQuake(Quake quake) {

}

 

6.2. 在annouceNewQuake方法中,当新的地震数据找到时广播一个Intent

 

private void announceNewQuake(Quake quake) {

Intent intent = new Intent(NEW_EARTHQUAKE_FOUND);

intent.putExtra(“date”, quake.getDate().getTime());

intent.putExtra(“details”, quake.getDetails());

intent.putExtra(“longitude”, quake.getLocation().getLongitude());

intent.putExtra(“latitude”, quake.getLocation().getLatitude());

intent.putExtra(“magnitude”, quake.getMagnitude());

sendBroadcast(intent);

}

 

7. 以上完成EarthquakeService的实现。现在,你需要修改两个Activity组件来监听Service广播的Intent,并且进行相应的更新显示。

 

7.1. 在Earthquake Activity中,创建一个内部的EarthquakeReceiver类,扩展BroadcastReceiver。重写onReceive方法来调用loadQuakesFromProvider方法,更新数组和更新列表。

 

public class EarthquakeReceiver extends BroadcastReceiver {

@Override

public void onReceive(Context context, Intent intent) {

loadQuakesFromProvider();

}

}

 

7.2. 重写onResume方法来注册新的Receiver并且当Activity变成活跃状态时更新ListView。重写onPause方法,当Activity从前台移出时反注册Receiver

 

EarthquakeReceiver receiver;

@Override

public void onResume() {

IntentFilter filter;

filter = new IntentFilter(EarthquakeService.NEW_EARTHQUAKE_FOUND);

receiver = new EarthquakeReceiver();

registerReceiver(receiver, filter);

loadQuakesFromProvider();

super.onResume();

}

 

@Override

public void onPause() {

unregisterReceiver(receiver);

super.onPause();

}

 

7.3. 在EarthquakeMap Activity中做相同的事情,这次,在收到Intent无效MapView之前调用Cursorrequery方法。

 

EarthquakeReceiver receiver;

@Override

public void onResume() {

earthquakeCursor.requery();

IntentFilter filter;

filter = new IntentFilter(EarthquakeService.NEW_EARTHQUAKE_FOUND);

receiver = new EarthquakeReceiver();

registerReceiver(receiver, filter);

super.onResume();

}

 

@Override

public void onPause() {

earthquakeCursor.deactivate();

super.onPause();

}

 

public class EarthquakeReceiver extends BroadcastReceiver {

@Override

public void onReceive(Context context, Intent intent) {

earthquakeCursor.requery();

MapView earthquakeMap = (MapView)findViewById(R.id.map_view);

earthquakeMap.invalidate();

}

}

 

现在,当Earthquake Activity启动后,它将启动EarthquakeService。这个Service将在后台运行、更新EarthquakeContent Provider,甚至在Activity中止或关闭之后。

 

在本章你将继续更新和增强EarthquakeService,第一次使用ToastNotification

 

在这个时刻,地震的处理已经在Service中运行了,但它们仍然在主线程中执行。在本章的后面,你将学习如何将耗时的操作移到后台线程来改善性能以及避免“应用程序无响应”的消息。

posted on 2009-08-13 11:25  xirihanlin  阅读(711)  评论(0编辑  收藏  举报