Ø GPS的概念和用途
Ø LocationManager和LocationProvider
Ø LocationListener
能力目标
Ø 掌握GPS的概念和用途
Ø 掌握如何利用DDMS模拟定位
Ø 在Android中如何获取全部及指定的LocationProvider
Ø 掌握如何获取用户的位置
本章简介
现在的手机大多数都提供了GPS模块、通过GPS模块可以接收GPS信号,并可精确地指定目前所在的位置,如果将GPS定位功能应用到地图上,还可以实现导航、搜索公交、搜索驾车路线等实用且有趣功能。本章中我们先学习GPS的基本知识,然后通过具体的案例讲解Android环境中GPS开发的基本原理及步骤。学习过本章知识之后我们能够利用GPS进行简单的定位功能的开发。学习本章的重点是掌握LocationManager、LocationProvider与LocationListener等API的功能和用法,并可以通过它们来监听、获取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应用。
在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
ArrayAdapter
new ArrayAdapter
android.R.layout.simple_list_item_1,
providerNames);
// 使用ListView来显示所有可用的LocationProvider
listView.setAdapter(adapter);
}
}
运行程序,结果如下图9.1.1所示:
图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
获取所有的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
根据指定条件获取满足该条件的全部LocationProvider的名称。
Ø List
获取所有可用的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
// 获取基于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
ArrayAdapter
// 使用ListView来显示所有可用的LocationProvider
listView.setAdapter(adapter);
}
}
运行该程序,即可在ListView中看到所有符合 Criteria条件的LocationProvider,程序运行结果和示例9.1相同。
Android模拟器本身并不能作为GPS接收器,因此无法得到GPS的定位信息,但为了方便程序员测试GPS应用,Android提供了DDMS工具来发送模拟的GPS信息。
启动Android模拟器之后,然后打开Eclipse的DDMS透视图下的Emulator Control面板即可向Android模拟器发送GPS定位信息,如图9.1.2所示。
图9.1.2 Emulator Control面板
当我们可以获取到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文件中增加如下授权代码片段:
授权获取定位信息-->
首先运行该程序,然后单击图9.2中的【Send】按钮,在Eclipse的LogCat下我们会看到用户的位置信息,如下图9.1.3所示。
图9.1.3 用户位置信息
如果把该程序与Google Map结合,让该程序根据GPS提供的信息实时地显示用户在地图上的位置,即可开发出GPS导航系统。
通过上面的示例,我们已经可以获取到设备所在的位置信息,但是,简单的经纬度信息对我们来说依然用处不大,实际生活中,我们更多的是通过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所示的效果。
图9.1.4 进入指定地点
图9.1.5 离开指定地点
本示例可以和下一章中将要讲到的Google Map一起使用,完成许多非常有特色的功能。
任务实训部分
训练技能点
如何获取用户位置
需求说明
在本节中我们学习了如何获取用户位置,假设我们在做一个服务器(比如一个网站),当用户移动时,随时将用户的位置信息提交到服务器端。这个在现实生活中是有很大应用的,比如父母可利用它来临近孩子玩耍的地点,避免孩子丢失。
根据此需求,实现网站和手机端的应用。
训练技能点
利用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定位信息的步骤。
二、上机练习
编写一个小型的动画,动画界面中提供一个小车(用图片表示),一条带有障碍物的道路。当汽车在道路上行走,接近道路上的路障时,汽车自动停止或绕开。