基于android的定位无非就两种:network、gps。两者各有优劣。
Network:定位快,准确度低,受环境影响小。
GPS:定位慢,准确度高,受环境影响大。
本文要解决的问题:
1. locationManager.getLastKnownLocation方法返回null。
2. 如何实现快速而又精确的定位。
E文好的话,直接看官网就好了
http://developer.android.com/guide/topics/location/strategies.html
在你的程序里如果有这样的代码你就要注意了(现在看来这些倒是多余了)
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);//高精度
criteria.setAltitudeRequired(false);//无海拔要求
criteria.setBearingRequired(false);//无方位要求
criteria.setCostAllowed(true);//允许产生资费
criteria.setPowerRequirement(Criteria.POWER_LOW);//低功耗
// 获取最佳服务对象
String provider = locationManager.getBestProvider(criteria,true);
locationManager.getLastKnownLocation(provider);
locationManager.getBestProvider(criteria,true);方法看起来很完美,但其实返回值就network、gps二选一。而且如果你要求高精度,它会优先检查GPS,如果手机开启了GPS就返回GPS,否则返回network。如果都没开启则返回null。
结合Network、GPS两种定位方式的优劣不难看出为什么getLastKnownLocation方法会返回null(这只针对第一次定位)。
当你开启GPS,provider的值为GPS。这时的定位方式为GPS,由于GPS定位慢(我测试的时间大约为50秒),所以它不可能立即返回你一个Location对象,所以就返回null了。还有人用下面的方法解决这个问题:
while (location ==null) {
location = locationManager.getLastKnownLocation(provider);
}
这绝对是个愚蠢的做法!举个例子:如果你在室内,gps无法定位到,你的程序将陷入死循环。当然使用requestLocationUpdates可以做到定位且不让程序陷入死循环,但是定位耗时长,甚至得不到定位。
如果使用网络定位呢,不得说这也是一个不错的选择。locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 0, 0,networkListener);
网络定位耗时一般在2秒左右(网络差,时间会更长),只要你接入网络,基本上都能获得定位。唯一的缺点就是精度不高。
那能不能将两者结合,这也是本文的重点。既然结合两者,就要同时为两者添加监听
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,1000 * 2,50,gpsListener);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0,networkListener);
这样,大概2秒我们就可以得到来自网络的定位,一分钟后得到来自GPS定位。这时用GPS定位替换网络定位就好了。当然这只是个理想的情况,现实要复杂的多。
比如:
你第一次定位成功返回location,由于网络问题第二次返回null。这时会发现,更新的location没有上次的精确,甚至是null,无法使用,这时我们要判断当前的location和新获得的location那个更好。可能你获得GPS定位后,由于天气、进入隧道等原因GPS服务器丢失,无法更新location(这时一个好的做法是切换到network定位)。还有可能用户没有开启GPS和network,根本就谈不上定位(其实每次定位成功都会有个定位缓存的,可以使用getLastKnownLocation获得)。
终上所述,我们要做的就是:
1. 尝试通过getLastKnownLocation获取上次定位信息
2. 开启network和gps监听
3. 获得network定位信息location
4. 比较当前location和新获取的location哪个更好(来自network)
5. 获得gps定位信息location
6. 停掉network监听
7. 比较当前location和新获取的location哪个更好(来自gps)
8. 如果gps服务器丢失,重新开启network监听
以GPS监听为例
// GPS监听的回调函数
private class GPSLocationListener implements LocationListener {
private boolean isRemove = false;//判断网络监听是否移除
@Override
public void onLocationChanged(Location location) {
// TODO Auto-generatedmethod stub
boolean flag =betterLocation.isBetterLocation(location,
currentBestLocation);
if (flag) {
currentBestLocation = location;
updateLocation(currentBestLocation);
}
// 获得GPS服务后,移除network监听
if (location !=null && !isRemove) {
locationManager.removeUpdates(networkListener);
isRemove = true;
}
}
@Override
public void onProviderDisabled(String provider) {
// TODO Auto-generatedmethod stub
}
@Override
public void onProviderEnabled(String provider) {
// TODO Auto-generatedmethod stub
}
@Override
public void onStatusChanged(String provider, int status, Bundleextras) {
// TODO Auto-generatedmethod stub
if (LocationProvider.OUT_OF_SERVICE == status) {
Toast.makeText(MainActivity.this,"GPS服务丢失,切换至网络定位",
Toast.LENGTH_SHORT).show();
locationManager
.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 0, 0,
networkListener);
}
}
}
其中isBetterLocation是用来判断哪个location更好的。这个方法来自android官网的,通过location获取的时间,精度等信息进行判断。
private static final int TWO_MINUTES = 1000 * 60 * 2;
/**
* Determines whether one Location reading is better than the current
* Location fix
*
* @param location
* The new Location that you want to evaluate
* @param currentBestLocation
* The current Location fix, to which you want to compare the new
* one
*/
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;
}
/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
因为之前上传的demo,大家觉得意义不大,所以就不再提供了。
下图的‘微秒’单位错了,应该是毫秒
andriod 自动切换网络和gps定位
获取到位置服务以后,同时请求网络和gps定位更新,然后就会同时上报网络和gps的Location 信息。在没有gps信号的时候,会自动获取网络定位的位置信息,如果有gps信号,则优先获取gps提供的位置信息.isBetterLocation 根据 时间、准确性、定位方式等判断是否更新当前位置信息,该方法来源于开发指南的Obtaining User Location 下。
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import android.content.Context;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import cn.tangdada.tangbang.R;
public class SecondFragment extends BaseFragment
{
private TextView tv;
LocationManager lm = null;
Location myLocation = null;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ");
public SecondFragment()
{
super();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_second, null);
tv = (TextView) view.findViewById(R.id.tv);
lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
return view;
}
@Override
public void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
}
@Override
public void onDestroy()
{
// TODO Auto-generated method stub
super.onDestroy();
}
@Override
public void onPause()
{
// TODO Auto-generated method stub
super.onPause();
lm.removeUpdates(listener);
}
@Override
public void onResume()
{
// TODO Auto-generated method stub
super.onResume();
lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, listener);
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, listener);
}
LocationListener listener = new LocationListener()
{
@Override
public void onLocationChanged(Location location)
{
// 实际上报时间
// String time = sdf.format(new Date(location.getTime()));
// timeText.setText("实际上报时间:" + time);
if (isBetterLocation(location, myLocation))
{
// 获取纬度
double lat = location.getLatitude();
// 获取经度
double lon = location.getLongitude();
// 位置提供者
String provider = location.getProvider();
// 位置的准确性
float accuracy = location.getAccuracy();
// 高度信息
double altitude = location.getAltitude();
// 方向角
float bearing = location.getBearing();
// 速度 米/秒
float speed = location.getSpeed();
String locationTime = sdf.format(new Date(location.getTime()));
String currentTime = null;
if (myLocation != null)
{
currentTime = sdf.format(new Date(myLocation.getTime()));
myLocation = location;
}
else
{
myLocation = location;
}
// 获取当前详细地址
StringBuffer sb = new StringBuffer();
if (myLocation != null)
{
Geocoder gc = new Geocoder(context);
List<Address> addresses = null;
try
{
addresses = gc.getFromLocation(myLocation.getLatitude(), myLocation.getLongitude(), 1);
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
if (addresses != null && addresses.size() > 0)
{
Address address = addresses.get(0);
sb.append(address.getCountryName() + address.getLocality());
sb.append(address.getSubThoroughfare());
}
}
tv.setText("经度:" + lon + "\n纬度:" + lat + "\n服务商:" + provider + "\n准确性:" + accuracy + "\n高度:" + altitude + "\n方向角:" + bearing
+ "\n速度:" + speed + "\n上次上报时间:" + currentTime + "\n最新上报时间:" + locationTime + "\n您所在的城市:" + sb.toString());
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras)
{
Log.i("tag", "onStatusChanged: " + provider);
}
@Override
public void onProviderEnabled(String provider)
{
Log.i("tag", "onProviderEnabled: " + provider);
}
@Override
public void onProviderDisabled(String provider)
{
Log.i("tag", "onProviderDisabled: " + provider);
}
};
private static final int TWO_MINUTES = 1000 * 1 * 2;
/**
* Determines whether one Location reading is better than the current Location fix
*
* @param location The new Location that you want to evaluate
* @param currentBestLocation The current Location fix, to which you want to compare the new one
*/
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;
}
/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2)
{
if (provider1 == null)
{
return provider2 == null;
}
return provider1.equals(provider2);
}
}
- 大小: 27.5 KB
- 查看图片附件