基于android的定位無非就兩種:network、gps。兩者各有優劣。
Network:定位快,準確度低,受環境影響小。
GPS:定位慢,準確度高,受環境影響大。
本文要解決的問題:
1.????? locationManager.getLastKnownLocation方法返回null。
2.????? 如何實現快速而又精確的定位。
E文好的話,直接看官網就好了 http://developer.android.com/guide/topics/location/strategies.html
在你的程序里如果有這樣的代碼你就要注意了(現在看來這些倒是多余了)
locationManager.getBestProvider(criteria,true);方法看起來很完美,但其實返回值就network、gps二選一。而且如果你要求高精度,它會優先檢查GPS,如果手機開啟了GPS就返回GPS,否則返回network。如果都沒開啟則返回null。
結合Network、GPS兩種定位方式的優劣不難看出為什么getLastKnownLocation方法會返回null(這只針對第一次定位)。
當你開啟GPS,provider的值為GPS。這時的定位方式為GPS,由于GPS定位慢(我測試的時間大約為50秒),所以它不可能立即返回你一個Location對象,所以就返回null了。還有人用下面的方法解決這個問題:
這絕對是個愚蠢的做法!舉個例子:如果你在室內,gps無法定位到,你的程序將陷入死循環。當然使用requestLocationUpdates可以做到定位且不讓程序陷入死循環,但是定位耗時長,甚至得不到定位。
如果使用網絡定位呢,不得說這也是一個不錯的選擇。locationManager.requestLocationUpdates(
????????????? LocationManager.NETWORK_PROVIDER, 0, 0,networkListener);
網絡定位耗時一般在2秒左右(網絡差,時間會更長),只要你接入網絡,基本上都能獲得定位。唯一的缺點就是精度不高。
那能不能將兩者結合,這也是本文的重點。既然結合兩者,就要同時為兩者添加監聽
這樣,大概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監聽為例
其中isBetterLocation是用來判斷哪個location更好的。這個方法來自android官網的,通過location獲取的時間,精度等信息進行判斷。
因為之前上傳的demo,大家覺得意義不大,所以就不再提供了。
下圖的‘微秒’單位錯了,應該是毫秒
andriod 自動切換網絡和gps定位
獲取到位置服務以后,同時請求網絡和gps定位更新,然后就會同時上報網絡和gps的Location 信息。在沒有gps信號的時候,會自動獲取網絡定位的位置信息,如果有gps信號,則優先獲取gps提供的位置信息.isBetterLocation 根據 時間、準確性、定位方式等判斷是否更新當前位置信息,該方法來源于開發指南的Obtaining User Location 下。
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);
}
}
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

