Android上的LocationManager API似乎对一个只需要偶尔粗略估计用户位置的应用程序来说有点麻烦。
我正在开发的应用程序本身并不是一个定位应用程序,但它确实需要获取用户的位置,以便显示附近企业的列表。它不需要担心用户是否在移动或类似的事情。
以下是我想做的:
向用户显示附近位置的列表。预加载用户的位置,以便在“活动X”中需要它时,它将可用。我并不特别关心更新的准确性或频率。只要不太远,只要抓住一个位置就足够了。也许如果我想变得更漂亮,我会每隔几分钟左右更新一次位置,但这不是一个很大的优先事项。适用于任何具有GPS或网络位置提供商的设备。
这似乎并不难,但在我看来,我必须组建两个不同的位置提供商(GPS和NETWORK),并管理每个提供商的生命周期。不仅如此,我还必须在多个活动中复制相同的代码以满足#2。过去,我曾尝试使用getBestProvider()将解决方案简化为仅使用一个位置提供程序,但这似乎只提供了最好的“理论”提供程序,而不是实际会为您提供最佳结果的提供程序。
有没有更简单的方法来实现这一点?
Kotlin版本的@Fedor Greate回答:
类的用法:
val locationResult = object : MyLocation.LocationResult() {
override fun gotLocation(location: Location?) {
val lat = location!!.latitude
val lon = location.longitude
Toast.makeText(context, "$lat --SLocRes-- $lon", Toast.LENGTH_SHORT).show()
}
}
val myLocation = MyLocation()
myLocation.getLocation(inflater.context, locationResult)
MyLocation类:
class MyLocation {
internal lateinit var timer1: Timer
internal var lm: LocationManager? = null
internal lateinit var locationResult: LocationResult
internal var gps_enabled = false
internal var network_enabled = false
internal var locationListenerGps: LocationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
timer1.cancel()
locationResult.gotLocation(location)
lm!!.removeUpdates(this)
lm!!.removeUpdates(locationListenerNetwork)
}
override fun onProviderDisabled(provider: String) {}
override fun onProviderEnabled(provider: String) {}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
}
internal var locationListenerNetwork: LocationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
timer1.cancel()
locationResult.gotLocation(location)
lm!!.removeUpdates(this)
lm!!.removeUpdates(locationListenerGps)
}
override fun onProviderDisabled(provider: String) {}
override fun onProviderEnabled(provider: String) {}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
}
fun getLocation(context: Context, result: LocationResult): Boolean {
//I use LocationResult callback class to pass location value from MyLocation to user code.
locationResult = result
if (lm == null)
lm = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager?
//exceptions will be thrown if provider is not permitted.
try {
gps_enabled = lm!!.isProviderEnabled(LocationManager.GPS_PROVIDER)
} catch (ex: Exception) {
}
try {
network_enabled = lm!!.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
} catch (ex: Exception) {
}
//don't start listeners if no provider is enabled
if (!gps_enabled && !network_enabled)
return false
if (ActivityCompat.checkSelfPermission(context,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) run {
ActivityCompat.requestPermissions(context as Activity,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION), 111)
}
if (gps_enabled)
lm!!.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0f, locationListenerGps)
if (network_enabled)
lm!!.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0f, locationListenerNetwork)
timer1 = Timer()
timer1.schedule(GetLastLocation(context), 20000)
return true
}
internal inner class GetLastLocation(var context: Context) : TimerTask() {
override fun run() {
lm!!.removeUpdates(locationListenerGps)
lm!!.removeUpdates(locationListenerNetwork)
var net_loc: Location? = null
var gps_loc: Location? = null
if (ActivityCompat.checkSelfPermission(context,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
) run {
ActivityCompat.requestPermissions(context as Activity,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION),111)
}
if (gps_enabled)
gps_loc = lm!!.getLastKnownLocation(LocationManager.GPS_PROVIDER)
if (network_enabled)
net_loc = lm!!.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)
//if there are both values use the latest one
if (gps_loc != null && net_loc != null) {
if (gps_loc.getTime() > net_loc.getTime())
locationResult.gotLocation(gps_loc)
else
locationResult.gotLocation(net_loc)
return
}
if (gps_loc != null) {
locationResult.gotLocation(gps_loc)
return
}
if (net_loc != null) {
locationResult.gotLocation(net_loc)
return
}
locationResult.gotLocation(null)
}
}
abstract class LocationResult {
abstract fun gotLocation(location: Location?)
}
}
最近进行了重构以获得代码的位置,学习了一些好的想法,最终实现了一个相对完善的库和演示。
//request all valid provider(network/gps)
private boolean requestAllProviderUpdates() {
checkRuntimeEnvironment();
checkPermission();
if (isRequesting) {
EasyLog.d("Request location update is busy");
return false;
}
long minTime = getCheckTimeInterval();
float minDistance = getCheckMinDistance();
if (mMapLocationListeners == null) {
mMapLocationListeners = new HashMap<>();
}
mValidProviders = getValidProviders();
if (mValidProviders == null || mValidProviders.isEmpty()) {
throw new IllegalArgumentException("Not available provider.");
}
for (String provider : mValidProviders) {
LocationListener locationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
if (location == null) {
EasyLog.e("LocationListener callback location is null.");
return;
}
printf(location);
mLastProviderTimestamp = location.getTime();
if (location.getProvider().equals(LocationManager.GPS_PROVIDER)) {
finishResult(location);
} else {
doLocationResult(location);
}
removeProvider(location.getProvider());
if (isEmptyValidProviders()) {
requestTimeoutMsgInit();
removeUpdates();
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
};
getLocationManager().requestLocationUpdates(provider, minTime, minDistance, locationListener);
mMapLocationListeners.put(provider, locationListener);
EasyLog.d("Location request %s provider update.", provider);
}
isRequesting = true;
return true;
}
//remove request update
public void removeUpdates() {
checkRuntimeEnvironment();
LocationManager locationManager = getLocationManager();
if (mMapLocationListeners != null) {
Set<String> keys = mMapLocationListeners.keySet();
for (String key : keys) {
LocationListener locationListener = mMapLocationListeners.get(key);
if (locationListener != null) {
locationManager.removeUpdates(locationListener);
EasyLog.d("Remove location update, provider is " + key);
}
}
mMapLocationListeners.clear();
isRequesting = false;
}
}
//Compared with the last successful position, to determine whether you need to filter
private boolean isNeedFilter(Location location) {
checkLocation(location);
if (mLastLocation != null) {
float distance = location.distanceTo(mLastLocation);
if (distance < getCheckMinDistance()) {
return true;
}
if (location.getAccuracy() >= mLastLocation.getAccuracy()
&& distance < location.getAccuracy()) {
return true;
}
if (location.getTime() <= mLastProviderTimestamp) {
return true;
}
}
return false;
}
private void doLocationResult(Location location) {
checkLocation(location);
if (isNeedFilter(location)) {
EasyLog.d("location need to filtered out, timestamp is " + location.getTime());
finishResult(mLastLocation);
} else {
finishResult(location);
}
}
//Return to the finished position
private void finishResult(Location location) {
checkLocation(location);
double latitude = location.getLatitude();
double longitude = location.getLongitude();
float accuracy = location.getAccuracy();
long time = location.getTime();
String provider = location.getProvider();
if (mLocationResultListeners != null && !mLocationResultListeners.isEmpty()) {
String format = "Location result:<%f, %f> Accuracy:%f Time:%d Provider:%s";
EasyLog.i(String.format(format, latitude, longitude, accuracy, time, provider));
mLastLocation = location;
synchronized (this) {
Iterator<LocationResultListener> iterator = mLocationResultListeners.iterator();
while (iterator.hasNext()) {
LocationResultListener listener = iterator.next();
if (listener != null) {
listener.onResult(location);
}
iterator.remove();
}
}
}
}
完整代码:https://github.com/bingerz/FastLocation/blob/master/fastlocationlib/src/main/java/cn/bingerz/fastlocation/FastLocation.java
*每次请求完成位置时,最好删除更新,否则手机状态栏将始终显示定位图标。
你可以一直使用LocationManager.getLastKnownLocation(),但就像它说的那样,它可能已经过时了。
获取一般位置的一个简单方法是注册网络(通常很快)。
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 1000, 1000, this);
然后做
locationManager.removeUpdates(this);
在侦听器的onLocationChanged()方法中。