GPS(Global Position System,全球定位系统)是20世纪70年代由美国陆海空三军联合研制的新一代空间卫星导航定位系统。
24颗GPS卫星在离地面2万2千功力的高空上,以12小时的周期环绕地球运行,使得在任意时刻,在地面的任意一点都可以同时观测到4颗以上的卫星。
由于卫星的位置精确,在GPS观测中,我们可以得到卫星到接收机的距离,利用三维坐标中的距离公式和3颗卫星,就可以组成3个方程式,解出观测点的位置(X、Y、Z)。考虑到卫星的时钟与接收机时钟之间的误差,实际上有4个未知数,X、Y、Z和钟差,因而需要引入第4颗卫星,形成4个方程式求解,从而得到观测点的经纬度和高程。
一、LocationManager和LocationProvider简介
LocationManager位于android.location包中,该类提供了系统位置访问的方法。LocationProvider定义了位置服务的提供方法,例如,是由GPS设备提供还是通过网络提供等。
1、LocationManager
通过LocationManager可以实现设备的定位、跟踪和趋近提示。可以通过getSystemService(Context.LOCATION_SERVICE)方法获得该类的实例。
常用属性和方法
属性或方法名称 | 属性或方法描述 |
GPS_PROVIDER | 静态字符串常量,表明LocationProvider是GPS |
NETWORK_PROVIDER | 静态字符串常量,表明LocationProvider是网络 |
addGpsStatusListener(Listener listener) | 添加一个GPS状态监听器 |
addProximityAlert(double latitude, double longitude, float radius, long expiration, PendingIntent intent) | 添加一个趋近警告 |
getAllProviders() | 获得所有LocationProvider列表 |
getBestProvider(Criteria criteria, boolean enabledOnly) | 根据Criteria返回最适合的LocationProvider |
getLastKnownLocation(String provider) | 根据Provider获得位置信息 |
getProvider(String name) | 获得指定名称的LocationProvider |
getProviders(boolean enabledOnly) | 获得可利用的LocationProvider列表 |
removeProximityAlert(PendingIntent intent) | 删除趋近警告 |
requestLocationUpdates(String provider, long minTime, float minDistance, LocationListener listener) | 通过给定的Provider名称,周期性的通知当前Activity |
2、LocationProvider
用来描述位置提供者,设置位置提供者的一些属性。可以通过Criteria类来为LocationProvider设置条件,获得合适的LocationProvider。
属性或方法名称 | 属性或方法描述 |
AVAILABLE | 静态整形常量,标示是否可利用 |
OUT_OF_SERVICE | 静态整形常量,不再服务器 |
TEMPORARILY_UNAVAILABLE | 静态整形常量,临时不可利用 |
getAccuracy() | 获得经度 |
getName() | 获得名称 |
getPowerRequirement() | 获得电源需求 |
hasMonetaryCost() | 花钱的还是免费的 |
requiresCell() | 是否需要访问基本网络 |
requiresNetwork() | 是否需要Internet网络数据 |
requiresSatellite() | 是否需要访问卫星 |
supportsAltitude() | 是否能够提供高度信息 |
supportsBearing() | 是否能够提供方向信息 |
supportsSpeed() | 是否能够提供速度信息 |
二、通过模拟器测试位置服务
演示通过模拟器发送经纬度来测试位置服务
注意:创建项目时,target build要选择Google APIs,这是为了添加选项jar文件maps.jar。
public class MainActivity extends Activity {
private LocationManager locationManager;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//通过getSystemService方法获得LocationManager实例
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
locate();
}
//定位方法
private void locate(){
TextView t = (TextView) findViewById(R.id.GPSTextView01);
StringBuilder builder = new StringBuilder("可利用的providers:");
List<String> providers = locationManager.getProviders(true);
//声明位置监听服务器
LocationListener ll = new LocationListener() {
//状态改变时调用
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {}
//Provider生效时调用
@Override
public void onProviderEnabled(String provider) {}
//Provider失效时调用
@Override
public void onProviderDisabled(String provider) {}
//位置改变时调用
@Override
public void onLocationChanged(Location location) {}
};
//循环provider,根据Provider获得位置信息(经纬度)
for(String provider:providers){
locationManager.requestLocationUpdates(provider, 0, 1000, ll);
builder.append("\n").append(provider).append(":");
Location location = locationManager.getLastKnownLocation(provider);
if(location != null){
//获得经度
double lat = location.getLatitude();
//获得纬度
double lng = location.getLongitude();
builder.append("(");
builder.append(lat);
builder.append(",");
builder.append(lng);
builder.append(")");
}else{
builder.append("没有位置信息");
}
}
t.setText(builder);
}
}
AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
三、获得LocationProvider
1、通过名称获得LocationProvider
//通过getSystemService方法获得LocationManager实例
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
//Provider名称常量
String name = LocationManager.GPS_PROVIDER;
//根据Provider名称获得LocationProvider
LocationProvider myProvider;
myProvider = locationManager.getProvider(name);
2、获得当前可利用的LocationProvider
//通过getSystemService方法获得LocationManager实例
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
//LocationProvider的两种方式
String name = LocationManager.GPS_PROVIDER;
String name2 = LocationManager.NETWORK_PROVIDER;
//根据Provider名称获得LocationProvider
LocationProvider myProvider;
myProvider = locationManager.getProvider(name);
boolean enabledOnly = true;
//获得所有可利用的Provider名称列表
List<String> providers = locationManager.getProviders(enabledOnly);
//根据名称获得Provider
myProvider = locationManager.getProvider(name);
3、根据Criteria条件获得LocationProvider
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
//LocationProvider的查询条件案例
Criteria criteria = new Criteria();
//设置精确度
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
//设置电耗
criteria.setPowerRequirement(Criteria.POWER_LOW);
//是否需要高度信息
criteria.setAltitudeRequired(false);
//是否需要方位信息
criteria.setBearingRequired(false);
//是否需要速度信息
criteria.setSpeedRequired(false);
//是否产生费用
criteria.setCostAllowed(true);
//获得符合条件最好的Provider
String bestProvider = locationManager.getBestProvider(criteria, true);
//获得符合条件的Provider
List<String> matchingProviders = locationManager.getProviders(criteria, false);
四、定位和跟踪
1、定位
定位就是确定设备的位置,在这里用到了另外一个Location类,该类描述了当前设备的地理位置信息,包括经纬度、方向、高度和速度等。
Location常见属性和方法
属性或方法名称 | 属性或方法描述 |
getLongitude() | 获得经度 |
getLatitude() | 获得纬度 |
getAccuracy() | 获得精确度 |
getAltitude() | 获得高度 |
getBearing() | 获得方向 |
getSpeed() | 获得速度 |
下面的实例演示了如何定位当前设备,并显示详细信息
public class MainActivity extends Activity {
//声明定位服务管理器实例
private LocationManager locationManager;
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
tv = (TextView) findViewById(R.id.LocationTextView01);
//获得位置信息
Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
//显示位置信息
printMsg(location);
}
//显示位置信息方法
private void printMsg(Location location){
StringBuilder builder = new StringBuilder("可利用的providers");
if(location != null){
//获得经度
double lat = location.getLatitude();
//获得纬度
double lng = location.getLongitude();
builder.append("(");
builder.append(lat);
builder.append(",");
builder.append(lng);
builder.append(")");
if(location.hasAccuracy()){
builder.append("\n精度");
builder.append(location.getAccuracy());
}
if(location.hasAltitude()){
builder.append("\n高度");
builder.append(location.getAltitude());
}
if(location.hasBearing()){
builder.append("\n方向");
builder.append(location.getBearing());
}
if(location.hasSpeed()){
builder.append("\n速度");
builder.append(location.getSpeed());
}
}else{
builder.append("没有位置信息");
}
tv.setText(builder);
}
}
2、跟踪
跟踪是通过注册监听器来实现的。下面的代码实现了设备的实时跟踪
public class MainActivity extends Activity {
private LocationManager locationManager;
private TextView tv;
StringBuilder builder = new StringBuilder("位置信息:\n");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
tv = (TextView) findViewById(R.id.gzTextView01);
String provider = locationManager.GPS_PROVIDER;
Location location = locationManager.getLastKnownLocation(provider);
updateMsg(location);
LocationListener ll = new LocationListener() {
@Override
public void onStatusChanged(String arg0, int arg1, Bundle arg2) {}
@Override
public void onProviderEnabled(String arg0) {}
@Override
public void onProviderDisabled(String arg0) {}
@Override
public void onLocationChanged(Location arg0) {}
};
locationManager.requestLocationUpdates(provider, 2000, 10, ll);
}
//更新信息方法
private void updateMsg(Location location) {
if(location != null){
//获得经度
double lat = location.getLatitude();
//获得纬度
double lng = location.getLongitude();
builder.append("(");
builder.append(lat);
builder.append(",");
builder.append(lng);
builder.append(")");
if(location.hasAccuracy()){
builder.append("\n精度");
builder.append(location.getAccuracy());
}
if(location.hasAltitude()){
builder.append("\n高度");
builder.append(location.getAltitude());
}
if(location.hasBearing()){
builder.append("\n方向");
builder.append(location.getBearing());
}
if(location.hasSpeed()){
builder.append("\n速度");
builder.append(location.getSpeed());
}
}else{
builder.append("没有位置信息");
}
tv.setText(builder);
}
}
五、趋近警告
如果有这样一种位置服务,能够对我们进入或退出某个设定的区域进行提示,这样的应用是很有意义的。
LocationManager提供了这一方法实现这一功能:addProximityAlert(double latitude, double longitude, float radius, long expiration, PendingIntent intent),该方法有5个参数,前两个是经纬度,第三个是区域半径,第四个是是否过期,第五个一般是一个广播PendingIntent。
要实现此功能需要两个步骤:一是获得LocationManager实例,调用其方法addProximityalert并添加趋近提示;二是定义一个广播接收器,当设备进入设定区域时提醒用户。
/**
* 趋近警告
*/
public class MainActivity extends Activity {
private static final String PROXIMITY_ALERT_ACTION_NAME = "mx.android.ch14.ProximityAlert";
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btn = (Button) findViewById(R.id.Button01);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
set();
}
});
}
//趋近提示方法
private void set(){
//定位i服务常量
String locService = Context.LOCATION_SERVICE;
//定位i服务管理器实例
LocationManager locationManager;
locationManager = (LocationManager) getSystemService(locService);
//声明经度
double lat = 37.4;
//声明纬度
double lng = 55.0;
//声明半径(单位米)
float radius = 200f;
//不过期
long expiration = -1;
Intent intent = new Intent(PROXIMITY_ALERT_ACTION_NAME);
PendingIntent pi = PendingIntent.getBroadcast(this, -1, intent, 0);
//添加趋近警告
locationManager.addProximityAlert(lat, lng, radius, expiration, pi);
}
}
/**
* 定义广播接收器,用来检测当设备进入设定区域时提示用户
*/
public class ProximityAlertReciever extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//趋近关键字
String key = LocationManager.KEY_PROXIMITY_ENTERING;
//从Intent获得额外信息,判断是否进入设置区域
boolean isEnter = intent.getBooleanExtra(key, false);
if(isEnter){
Toast.makeText(context, "你已经进入海淀区!", Toast.LENGTH_LONG).show();
}
}
}
在AndroidManifest.xml中声明广播接收器
<receiver android:name="ProximityAlertReciever">
<intent-filter>
<action android:name="mx.android.ch14.ProximityAlert" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</receiver>
六、Geocoder正逆向编码
Geocoder可以完成位置信息和经纬度坐标之间的相互转换。例如,你知道了某个地方的名称,想知道它的经纬度坐标,或者你知道了某个地方的经纬度坐标,想知道其名称。
解码方式有以下两种:
- 正向编码:通过位置名称获得经纬度坐标
- 反向编码:通过经纬度坐标获得位置名称
public class MainActivity extends Activity {
private TextView tv;
private Button b1,b2;
private LocationManager locationManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
tv = (TextView) findViewById(R.id.coder_TextView01);
b1 = (Button) findViewById(R.id.coder_Button01);
b2 = (Button) findViewById(R.id.coder_Button02);
//正向编码
b1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
forward();
}
});
//反向编码
b2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
reserse();
}
});
}
//正向编码
private void forward(){
Geocoder gc = new Geocoder(this, Locale.getDefault());
String address = "北京天安门";
List<Address> locations = null;
try {
locations = gc.getFromLocationName(address, 10);
if(locations.size() > 0){
Address a = locations.get(0);
double lat = a.getLatitude();
double lng = a.getLongitude();
tv.setText(lat + ":" + lng);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//反向编码
private void reserse(){
double lng = 116.46;
double lat = 39.92;
Geocoder gc = new Geocoder(this, Locale.getDefault());
List<Address> addresses = null;
try {
addresses = gc.getFromLocation(lat, lng, 10);
StringBuilder sb = new StringBuilder();
if(addresses.size() > 0){
Address a = addresses.get(0);
for (int i = 0; i < a.getMaxAddressLineIndex(); i++) {
sb.append(a.getAddressLine(i)).append("\n");
sb.append(a.getLocality()).append("\n");
sb.append(a.getPostalCode()).append("\n");
sb.append(a.getCountryName());
}
tv.setText(sb.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}