您的位置:首页 > 移动开发 > Android开发

Android培训:在应用中使用定位

2013-04-18 16:59 148 查看
- 关键组件:LocationManager, LocationProvider, LocationListener,
Geocoder
- 移动设备通常随身携带
- 位置感知是移动应用独特的功能
- 获取位置信息冰善用之,可以带来更多的情境体验

使用定位管理器
- 在应用接收定位信息更新之前,有一些必要的设置步骤

声明相关权限
- 仅使用基于网络的位置信息提供者:ACCESS_COARSE_LOCATION

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />

- 更精确的GPS定位:ACCESS_FINE_LOCATION,其中已然包含前者

获取LocationManager的引用
- LocationManager是从安卓上访问位置服务的主类
- 程序如要在前台也即活动中接收定位更新,需要在onCreate()中执行:

LocationManager locationManager =

(LocationManager) this.getSystemService(Context.LOCATION_SERVICE);


取一个Location Provider
- 尽管不是必须的,大多数现代安卓设备能够通过多种底层技术接收位置更新
- 这些技术抽象为LocationProvider对象
- 各种location provider在速度、精度、费用、能耗等多方面存在差异
- 通常,location provider经度越高,耗时越多(GPS)
- 根据用例选择使用哪种或哪几种LP

LocationProvider provider =

locationManager.getProvider(LocationManager.GPS_PROVIDER);

- 也可以预设一些标准:精度,能耗,费用等,让安卓决定一个最匹配的LP:

// Retrieve a list of location providers that have fine accuracy, no monetary cost, etc
Criteria criteria = new Criteria();

criteria.setAccuracy(Criteria.ACCURACY_FINE);

criteria.setCostAllowed(false);
...
String providerName = locManager.getBestProvider(criteria, true);

// If no suitable provider is found, null is returned.
if (providerName != null) {

...
}


验证LP是否可用
- 在设置中可能将LP关闭
- 应当检查请求的LP当前是否可用:isProviderEnabled()
- 如关闭,可以提供给用户一个操作机会以开启之,发出一个携带ACTION_LOCATION_SOURCE_SETTINGS的intent

@Override
protected void onStart() {

super.onStart();

// This verification should be done during onStart() because the system calls

// this method when the user returns to the activity, which ensures the desired

// location provider is enabled each time the activity resumes from the stopped state.

LocationManager locationManager =

(LocationManager) getSystemService(Context.LOCATION_SERVICE);

final boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);

if (!gpsEnabled) {

// Build an alert dialog here that requests that the user enable

// the location services, then when the user clicks the "OK" button,

// call enableLocationSettings()

}
}

private void enableLocationSettings() {

Intent settingsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);

startActivity(settingsIntent);
}


获取当前位置

建立位置侦听
- 注册一个时间侦听器,指出你想要从哪个location manager接收位置更新,并置定最小的时间、距离间隔
- onLocationChanged()方法将按照指定的频率调用

private final LocationListener listener = new LocationListener() {

@Override

public void onLocationChanged(Location location) {

// A new location update is received.  Do something useful with it.  In this case,

// we're sending the update to a handler which then updates the UI with the new

// location.

Message.obtain(mHandler,

UPDATE_LATLNG,

location.getLatitude() + ", " +

location.getLongitude()).sendToTarget();

...

}

...
};

mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,

10000,          // 10-second interval.

10,             // 10 meters.

listener);


处理多个位置更新源
- 矛盾:越准越慢,越快越不准
- 用快的及时更新,等准的来了再做替换:同时为两种LP注册同一个location listener
-
在onLocationChanged()中,将接收到来自不同LP的位置更新,可能具有不同的时间戳和精确级别。需要你自行添加逻辑来避免冲突、忽略过时的或者精度差的更新:

private static final int TWO_MINUTES = 1000 * 60 * 2;

protected boolean isBetterLocation(Location location, Location currentBestLocation) {

if (currentBestLocation == null) {

// A new location is always better than no location

return true;

}

// Check whether the new location fix is newer or older

long timeDelta = location.getTime() - currentBestLocation.getTime();

boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;

boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;

boolean isNewer = timeDelta > 0;

// If it's been more than two minutes since the current location, use the new location

// because the user has likely moved

if (isSignificantlyNewer) {

return true;

// If the new location is more than two minutes older, it must be worse

} else if (isSignificantlyOlder) {

return false;

}

// Check whether the new location fix is more or less accurate

int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());

boolean isLessAccurate = accuracyDelta > 0;

boolean isMoreAccurate = accuracyDelta < 0;

boolean isSignificantlyLessAccurate = accuracyDelta > 200;

// Check if the old and new location are from the same provider

boolean isFromSameProvider = isSameProvider(location.getProvider(),

currentBestLocation.getProvider());

// Determine location quality using a combination of timeliness and accuracy

if (isMoreAccurate) {

return true;

} else if (isNewer && !isLessAccurate) {

return true;

} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {

return true;

}

return false;
}

private boolean isSameProvider(String provider1, String provider2) {

if (provider1 == null) {

return provider2 == null;

}

return provider1.equals(provider2);
}


明智的使用getLastKnownLocation()
- 此方法提供上一次定位更新的信息
- 针对取不到当前位置信息的情况,可直接调用之获取最近一次的信息
- 要通过检查时间戳和经促来确定其返回的信息是否能用
- 如果选择忽略之而等待更新数据到来,那么最好在位置更新到来之前向用户提供一条信息

中断位置更新
- 当结束使用定位数据,应当终端定位以减少无必要的能耗和带宽

protected void onStop() {

super.onStop();

mLocationManager.removeUpdates(listener);
}


显示定位地址
- 接收到的位置更新数据为经纬坐标

执行反向地理编码
- 反向地理编码就是将经纬度翻译成人类可读的地址
- Geocoder API:基于web服务。
- isPresent()方法用于检测服务是否存在

private final LocationListener listener = new LocationListener() {

public void onLocationChanged(Location location) {

// Bypass reverse-geocoding if the Geocoder service is not available on the

// device. The isPresent() convenient method is only available on Gingerbread or above.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD && Geocoder.isPresent()) {

// Since the geocoding API is synchronous and may take a while.  You don't want to lock

// up the UI thread.  Invoking reverse geocoding in an AsyncTask.

(new ReverseGeocodingTask(this)).execute(new Location[] {location});

}

}

...
};

// AsyncTask encapsulating the reverse-geocoding API.  Since the geocoder API is blocked,
// we do not want to invoke it from the UI thread.
private class ReverseGeocodingTask extends AsyncTask<Location, Void, Void> {

Context mContext;

public ReverseGeocodingTask(Context context) {

super();

mContext = context;

}

@Override

protected Void doInBackground(Location... params) {

Geocoder geocoder = new Geocoder(mContext, Locale.getDefault());

Location loc = params[0];

List<Address> addresses = null;

try {

// Call the synchronous getFromLocation() method by passing in the lat/long values.

addresses = geocoder.getFromLocation(loc.getLatitude(), loc.getLongitude(), 1);

} catch (IOException e) {

e.printStackTrace();

// Update UI field with the exception.

Message.obtain(mHandler, UPDATE_ADDRESS, e.toString()).sendToTarget();

}

if (addresses != null && addresses.size() > 0) {

Address address = addresses.get(0);

// Format the first line of address (if available), city, and country name.

String addressText = String.format("%s, %s, %s",

address.getMaxAddressLineIndex() > 0 ? address.getAddressLine(0) : "",

address.getLocality(),

address.getCountryName());

// Update the UI via a message handler.

Message.obtain(mHandler, UPDATE_ADDRESS, addressText).sendToTarget();

}

return null;

}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: