学习内容

Ø GPS的概念和用途

Ø LocationManager和LocationProvider

Ø LocationListener

能力目标

Ø 掌握GPS的概念和用途

Ø 掌握如何利用DDMS模拟定位

Ø 在Android中如何获取全部及指定的LocationProvider

Ø 掌握如何获取用户的位置


本章简介

现在的手机大多数都提供了GPS模块、通过GPS模块可以接收GPS信号,并可精确地指定目前所在的位置,如果将GPS定位功能应用到地图上,还可以实现导航、搜索公交、搜索驾车路线等实用且有趣功能。本章中我们先学习GPS的基本知识,然后通过具体的案例讲解Android环境中GPS开发的基本原理及步骤。学习过本章知识之后我们能够利用GPS进行简单的定位功能的开发。学习本章的重点是掌握LocationManager、LocationProvider与LocationListener等API的功能和用法,并可以通过它们来监听、获取GPS定位信息。

  

核心技能部分

9.1 GPS简介

全球定位系统GPS的全称是Global Positioning System,它是20世纪70年代由美国陆海空三军联合研制的新一代空间卫星导航定位系统。GPS的作用就是在全球范围内提供提供准确的定位、测量、和高精度的时间标准等功能。

GPS定位系统由三部分组成:GPS卫星组成的空间部分,若干地面站组成的控制部分和普通用户手机中的接收器。对于手机用户来说,手机就是GPS定位系统的接收器,也就是说GPS定位需要手机的硬件支持GPS功能。

GPS是一个中距离圆形轨道卫星导航系统,该系统包括太空中分布在倾角为55度的6个轨道上的24颗GPS卫星、地面上的一个主控站、3个数据注入站和5个监测站以及作为用户端的GPS接收器。最少只需要其中的3颗卫星,就能迅速确定用户端在地球上所处的位置及海拔坐标,所能接收到的卫星数越多,解码出来的数据就越精确。

GPS定位系统听上去专业、高深,是一门非常高深的技术,但对于Android应用开发的程序员来说,开发提供GPS功能的应用程序十分简单。就像Android为电话管理支持提供了TelephonyManager类、为音频管理支持提供了AudioManager类一样,Android为GPS提供了LocationManager类,通过LocationManager类及其他几个辅助类,我们可以非常方便地开发出功能强大的GPS应用。

 

9.2 获取LocationProvider

在Android中进行GPS开发,需要涉及LocationManager、LocationProvider、Location三个类。

LocationManager类的作用与TelephonyManager、AudioManager等服务类的作用相似,所有GPS定位相关的服务、对象都将由该对象来产生。与程序中获取TelephonyManager、AudioManager的方法相似 ,程序中获取LocationManager的实例是通过调用Context的getSystemService()方法来获取,如下代码所示:

LocationManager lm = getSystemService(Context.LOCATION_SERVICE);

一旦在程序中获得了LocationManager对象之后,接下来即可调用LocationManager的方法来获取GPS定位的相关服务和对象。

定位提供者LocationProvider是GPS定位组件的抽象表示,Android的定位信息就是由它提供的,即,通过LocationProvider类的方法可以获取定位组件的相关信息。

Location是位置信息的抽象类,它提供了大量用来获取定位信息的方法。

上面三个类就是Android GPS支持的三个核心API,使用它们获取GPS定位信息的通用步骤为:

(1) 获取系统的LocationManager对象。

(2) 使用LocationManager,通过指定LocationProvider来获取定位信息,定位信息由Location对象来表示。

(3) 从Location对象中获取定位信息。

 

示例9.1

获取LocationProvider。

通过前面的介绍可以看出,Android的定位信息由LocationProvider对象提供的,因此在开始编程之前,需要先获取LocationProvider对象。LocationManager提供了一个getAllProviders()方法,该方法可以用来获取系统所有可用的LocationProvider。

在布局文件中我们使用一个id为list的ListView组件,用来显示所有LocationProvider。

Activity类的代码如下:

public class GetAllProvidersActivity extends Activity {

private ListView listView = null;

private LocationManager manager = null;

 

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.providers);

listView = (ListView) findViewById(R.id.list);

// 获取系统的LocationManager对象

manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

// 获取系统所有的LocationProvider的名称

List<String> providerNames = manager.getAllProviders();

ArrayAdapter<String> adapter =

new ArrayAdapter<String>(this,

android.R.layout.simple_list_item_1,

 providerNames);

// 使用ListView来显示所有可用的LocationProvider

listView.setAdapter(adapter);

}

}

运行程序,结果如下图9.1.1所示:

 GPS用户定位_LocationManager

图9.1.1 系统所有LocationProvider

从图9.1.1可以看出,当前模拟器所有可用的LocationProvider有如下三个:

Ø network:由LocationManager.NETWORK_PROVIDER常量表示,它代表通过无线信号基站或Wi-fi网络获取定位信息的LocationProvider对象。

Ø passive:由LocationManager.PASSIVE_PROVIDER常量表示。

Ø gps:由LocationManager.GPS_PROVIDER常量表示,它代表通过GPS卫星获取定位信息的LocationProvider对象。

 

LocationManager类提供了访问定位服务的功能,该类提供了如下常用的方法。

Ø boolean  addGpsStatusListener(GpsStatus.Listener listener)

添加一个监听GPS状态的监听器。

Ø void  addProximityAlert(double latitude, double longitude, float radius, long expiration, PendingIntent intent)

设置当临近某指定位置(经度,纬度)和半径的位置时的警告信息。

Ø List<String>  getAllProviders()

获取所有的LocationProvider列表。

Ø String  getBestProvider(Criteria criteria, boolean enabledOnly)

根据指定条件返回最优的LocationProvider对象。

Ø GpsStatus  getGpsStatus(GpsStatus status)

获取GPS状态。

Ø Location  getLastKnownLocation(String provider)

根据LocationProvider获取最近一次已知的Location。

Ø LocationProvider  getProvider(String name)

根据名称来获取LocationProvider。

Ø List<String>  getProviders(Criteria criteria, boolean enabledOnly)

根据指定条件获取满足该条件的全部LocationProvider的名称。

Ø List<String>  getProviders(boolean enabledOnly)

获取所有可用的LocationProvider。

Ø boolean  isProviderEnabled(String provider)

判断指定名称的LocationProvider是否可用。

Ø void  removeGpsStatusListener(GpsStatus.Listener listener)

删除GPS状态监听器。

Ø void  removeProximityAlert(PendingIntent intent)

删除一个临近警告。

Ø void  requestLocationUpdates(String provider, long minTime, float minDistance, PendingIntent intent)

通过指定的LocationProvider周期性地获取定位信息,并通过intent启动相应的组件。

Ø void  requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener)

通过指定的LocationProvider周期性地获取定位信息,并触发Listener所对应的触发器

例如,在示例9.1我们通过调用LocationManager的getAllProviders()方法获取所有LocationProvider时返回的是List<String>集合,集合元素即为LocationProvider的名称。我们还可以借助于LocationManager的getProvider(String name)方法,获取指定名称的LocationProvider对象。例如以下代码:

// 获取基于GPS的LocationProvider

LocationProvider  locprovider =

manager.getProvider(LocationManager.GPS_PROVIDER);

前面的程序调用LocationManager的getAllProviders()方法返回了系统所有可用的Location Provider,但大部分时候,应用程序可能希望得到符合指定条件的LocationProvider,这就需要借助于LocationManager的getBestProvider(Criteria criteria,boolean enabledOniy)方法来获取。

除了利用LocationManager类来获取LocationProvider外,我们还可以借助Criteria类来获取符合指定条件的LocationProvider。Criteria提供如下常用的方法来设置条件。

Ø void  setAccuracy(int accuracy)

设置对LocationProvider的精度要求。

Ø void  setAltitudeRequired(boolean altitudeRequired)

设置要求LocationProvider能提供高度信息。

Ø void  setBearingRequired(boolean bearingRequired)

设置要求LocationProvider能提供方向信息。

Ø void  setCostAllowed(boolean costAllowed)

设置要求LocationProvider是否免费。

Ø void  setPowerRequirement(int level)

设置要求LocationProvider的耗电量。

Ø void  setSpeedRequired(boolean speedRequired)

设置要求LocationProvider能提供速度信息。

示例9.2

根据Criteria类获取系统中免费的LocationProvider,并且该LocationProvider必须能提供高度信息、速度信息等。

本示例程序采用示例9.1的布局文件,我们重点来看一下Activity类的代码:

public class GetProvidersActivity extends Activity {

private ListView listView = null;

private LocationManager manager = null;

 

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.providers);

listView = (ListView) findViewById(R.id.list);

// 获取系统的LocationManager对象

manager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);

// 创建一个LocationProvider的过滤条件

Criteria criteria = new Criteria();

// 设置要求LocationProvider必须是免费的。

criteria.setCostAllowed(false);

// 设置要求LocationProvider能提供高度信息

criteria.setAltitudeRequired(true);

// 设置要求LocationProvider能提供方向信息

criteria.setBearingRequired(true);

// 获取系统所有复合条件的LocationProvider的名称

List<String> providerNames = manager.getAllProviders();

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, providerNames);

// 使用ListView来显示所有可用的LocationProvider

listView.setAdapter(adapter);

}

}

运行该程序,即可在ListView中看到所有符合 Criteria条件的LocationProvider,程序运行结果和示例9.1相同。

9.3 GPS定位

9.3.1 使用DDMS模拟定位

Android模拟器本身并不能作为GPS接收器,因此无法得到GPS的定位信息,但为了方便程序员测试GPS应用,Android提供了DDMS工具来发送模拟的GPS信息。

启动Android模拟器之后,然后打开Eclipse的DDMS透视图下的Emulator Control面板即可向Android模拟器发送GPS定位信息,如图9.1.2所示。

 GPS用户定位_传感器_02

图9.1.2 Emulator Control面板

9.3.2 获取用户的位置

当我们可以获取到LocationProvider的时候,就可以通过其提供的位置组件获取到设备此时的Position位置信息,到这里,我们就实现了通过手机自带的GPS设备获取当前位置信息的一整套简单流程,下面我们通过一个示例来演示获取用户信息的简单应用。

示例9.3

获取用户当前的地理位置,利用DDMS模拟器模拟用户当前的位置数据。

在布局文件中提供一个按钮,当用户单击这个按钮时程序能通过LocationProvider周期性地获取定位信息,并触发Listener所指定的LocationListener类型的触发器。Android类及自定义LocationListener的详细代码如下:

public class UserLocationActivity extends Activity {

 

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.userlocation);

Button button1 = (Button) findViewById(R.id.btn1);

button1.setOnClickListener(new OnClickListener() {

public void onClick(View v) {

//通过名字来得到指定的系统级的服务

LocationManager manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

manager.requestLocationUpdates(LocationManager.GPS_PROVIDER,1000,0,locationListener);   ------------------①

}

});

}

LocationListener locationListener = new LocationListener() {

 

public void onStatusChanged(String provider, int status, Bundle extras) {

}

 

public void onProviderEnabled(String provider) {

}

 

public void onProviderDisabled(String provider) {

}

 

public void onLocationChanged(Location location) {

System.out.println(location.getLatitude());

System.out.println(location.getLongitude());

 

}

}

上面的程序中粗体字代码用于从Location中获取经度、纬度定位信息。程序中编号为处的代码通过LocationManager设置了一个监听器,该监听器负责每隔1秒向LocationProvider请求一次定位信息,当用户的定位信息发生改变时,系统会在LogCat下输出用户的定位信息。程序中用到的requestLocationUpdates()方法的原型如下:

void LocationManager.requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener)

该方法通过指定的LocationProvider周期性地获取定位信息,并出发listener所对应的触发器。

定位提供者LocationProvider对象就是GPS定位组件的抽象表示,通过LocationProvider可以获取该定位组件的相关信息。LocationProvider提供了如下常用方法。

Ø abstract int  getAccuracy()

返回LocationProvider的精度。

Ø String  getName()

返回LocationProvider的名称。

Ø abstract int  getPowerRequirement()

获取LocationProvider的电源需求。

Ø abstract boolean  hasMonetaryCost()

Ø 返回该LocationProvider是收费的还是免费的

Ø boolean  meetsCriteria(Criteria criteria)

Ø 判断该LocationProvider是否满足Criteria条件。

Ø abstract boolean  requiresCell()

判断该LocationProvider是否需要访问网络基站。

Ø abstract boolean  requiresNetwork()

判断该LocationProvider是否需要网络数据。

Ø abstract boolean  requiresSatellite()

判断该LocationProvider是否需要访问基于卫星的定位系统。

Ø abstract boolean  supportsAltitude()

判断该LocationProvider是否支持高度信息。

Ø abstract boolean  supportsBearing()

判断该LocationProvider是否支持方向信息。

Ø abstract boolean  supportsSpeed()

判断该LocationProvider是否支持速度信息。

onLocationChanged 方法的参数的类型Location代表位置信息的抽象类,该类是GPS技术的一个很重要的类,它提供了如下重要的方法:

Ø boolean  hasAccuracy()

获取定位信息的精度。

Ø double  getAltitude()

获取定位信息的高度。

Ø float  getBearing()

获取定位信息的方向。

Ø double  getLatitude()

获取定位信息的纬度。

Ø double  getLongitude()

获取定位信息的经度。

Ø String  getProvider()

获取提供该定位信息的LocationProvider。

Ø float  getSpeed()

获取定位信息的速度。

Ø boolean  hasAccuracy()

判断该定位信息是否有精度信息。

Ø boolean  hasAltitude()

判断该定位信息是否有高度信息。

Ø boolean  hasBearing()

判断该定位信息是否有方向信息。

Ø boolean  hasSpeed()

判断该定位信息是否有速度信息。

 

该程序需要有访问GPS信号的权限,因此需要在AndroidManifest.xml文件中增加如下授权代码片段:

<!—授权获取定位信息-->

<use-permission android:name=’”android.permission.ACCESS_FINE_LOCATION”/>

 

首先运行该程序,然后单击图9.2中的【Send】按钮,在Eclipse的LogCat下我们会看到用户的位置信息,如下图9.1.3所示。

 GPS用户定位_LocationProvider_03

图9.1.3 用户位置信息

如果把该程序与Google Map结合,让该程序根据GPS提供的信息实时地显示用户在地图上的位置,即可开发出GPS导航系统。

9.3.3 指定区域临近警告

通过上面的示例,我们已经可以获取到设备所在的位置信息,但是,简单的经纬度信息对我们来说依然用处不大,实际生活中,我们更多的是通过GPS功能寻找我们的目的地,这里的目的地其实也是一个经纬度代表的位置信息,当我们靠近此位置达到一定的半径范围时,会有相应的提示通知我们,例如很多车辆上的GPS导航系统中会内置道路上的摄像头位置,当到此区域时提醒司机注意行驶速度等,这里就需要我们能判断出用户与指定位置之间的距离关系,当用户临近此指定位置时给出警告,这也就是我们这节要讲的位置临近警告。

所谓临近警告指的是当用户手机不断靠近指定固定点,当与该固定点的距离小于指定范围时,系统可以触发相应的处理。Android中,LocationManager类提供了一个名称为addProximityAlert(double latitude,double longitude,float radius,long expiration,PendingIntent intent)方法,该方法用于添加一个临近警告,它参数的说明如下。

Ø longitude:指定固定点的经度。

Ø latitude:指定固定点的纬度。

Ø radius:该参数指定一个半径长度。

Ø expiration:该参数指定经过多少毫秒后该临近警告就会过期失效。-1指定永不过期。

Ø intent:该参数指定临近该固定点时触发该intent对应的组件。

 

示例9.4

指定区域临近警告。

本示例程序演示了如何检测手机是否进入郑州二七广场。当程序启动后,程序就会添加一个临近警告,当用户临近郑州二七广场所在纬度、经度时,系统会以文本的形式在屏幕上显示提示信息。

本程序的实现思想是借助一个Intent接收器以经纬度为圆心以指定值为半径划定一个区域,当设备出入该区域时,发出提醒信息。

本示例的布局文件比较简单,在布局文件里面只提供了一个TextView,用来显示当用户进出二七广场时的提示信息。我们来重点看一下Activity类的代码:

public class ProximityActivity extends Activity {

private TextView textView = null;

private LocationManager manager = null;   // 定位服务管理器实例

 

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.proximity);

textView = (TextView) findViewById(R.id.textView1);

 

// 通过getSystemService方法获得LocationManager实例

manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

 

double longitude = 113.66632841527462;

double latitude = 34.752014421190424;

 

float radius = 2500;// 定义半径(2.5公里)

// 定义Intent

Intent intent = new Intent(this, ProximityAlertReciever.class);

// 将Intent包装成PendingIntent

PendingIntent pendingIntent = PendingIntent.getBroadcast(this, -1, intent, 0);

// 添加临近警告

manager.addProximityAlert(latitude, longitude, radius, -1, pendingIntent);

}

 

class ProximityAlertReciever extends BroadcastReceiver {

@Override

public void onReceive(Context context, Intent intent) {

// 获取是否为进入指定区域

boolean isEnter = intent.getBooleanExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);

if (isEnter) {

textView.setText("您已经进入郑州二七广场");// 显示提示信息

} else {

textView.setText("您已经离开郑州二七广场");// 显示提示信息

}

}

}

}

在上面的Activity类,我们通过自定义的广播接收者,当广播到来时,根据不同的情况在屏幕上来显示用户进出二七区时的相应提示信息。

注意:不要忘记在功能清单文件中对广播进行注册。

 

打开DDMS的Emulator Control面板,在其中输入指定的纬度、经度信息,我们可以看到下图9.1.4、9.1.5所示的效果。

 GPS用户定位_传感器_04

图9.1.4 进入指定地点

 GPS用户定位_LocationManager_05

图9.1.5 离开指定地点

本示例可以和下一章中将要讲到的Google Map一起使用,完成许多非常有特色的功能。


任务实训部分 

1:自定义实现一个位置记录器

训练技能点

如何获取用户位置

需求说明

在本节中我们学习了如何获取用户位置,假设我们在做一个服务器(比如一个网站),当用户移动时,随时将用户的位置信息提交到服务器端。这个在现实生活中是有很大应用的,比如父母可利用它来临近孩子玩耍的地点,避免孩子丢失。

根据此需求,实现网站和手机端的应用。

 

2:查找最合适的LocationProvider

训练技能点

利用Criteria查找满足指定条件的LocationProvider

需求说明

在实际的应用中,一般情况下我们需要的不是所有的LocationProvider,而是满足指定条件的LocationProvider,比如免费、需要网络数据、支持高度信息等。本示例要求大家完成查找满足低电耗、支持高度、免费的LocationProvider。

实现思路

核心代码如下:

Criteria criteria = new Criteria();

criteria.setAccuracy(Criteria.ACCURACY_FINE);

criteria.setPowerRequirement(Criteria.POWER_LOW);

criteria.setAltitudeRequired(false);

criteria.setSpeedRequired(false);

criteria.setCostAllowed(false);

//通过名字来得到指定的系统级的服务

LocationManager manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

String provider = manager.getBestProvider(criteria, false);

System.out.println(provider);

  


巩固练习

一、简答题

1. 简要描述GPS是什么以及它的功能。 

2. 简述在Android中获取GPS定位信息的步骤。 

二、上机练习

   编写一个小型的动画,动画界面中提供一个小车(用图片表示),一条带有障碍物的道路。当汽车在道路上行走,接近道路上的路障时,汽车自动停止或绕开。