Android:LocationManager与Google Play服务


151

我想构建一个应用程序,以获取用户的当前位置为中心,然后通过Google Places API查找与他/她最接近的兴趣点(例如酒吧,餐厅等)。

在网络上寻找起点时,我遇到了一些使用该LocationManager课程的教程,以及另一些使用Google Play服务的教程,以查找用户位置。

乍一看,他们俩都做同样的事情,但是由于我是新手,所以我有点困惑,我不知道哪种方法最适合我的需求。所以,我想问你:

这两种查找位置的方法(如果有)有什么区别?


Answers:


319

Android上的用户位置

在Android上获取用户的位置比在iOS上简单一些。要开始造成混乱,可以使用两种完全不同的方法。第一种是使用来自的Android API android.location.LocationListener,第二种是使用Google Play服务API com.google.android.gms.location.LocationListener。让我们来看看它们两者。

  1. Android的位置API

    Android的位置API使用三种不同的提供程序来获取位置-

    • LocationManager.GPS_PROVIDER—该提供商使用卫星确定位置。根据条件,此提供程序可能需要一段时间才能返回位置修复。
    • LocationManager.NETWORK_PROVIDER—该提供商根据蜂窝塔和WiFi接入点的可用性确定位置。通过网络查找来检索结果。
    • LocationManager.PASSIVE_PROVIDER—该提供者将返回其他提供者生成的位置。当其他应用程序或服务请求位置更新时,您会被动接收位置更新,而无需自己实际请求位置。

其要点是您LocationManager从系统中获取的对象,实施LocationListener,然后在requestLocationUpdates上调用LocationManager

这是一个代码片段:

    LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
// Define a listener that responds to location updates
LocationListener locationListener = new LocationListener() {
    public void onLocationChanged(Location location) {
      // Called when a new location is found by the network location provider.
      makeUseOfNewLocation(location);
    }

    public void onStatusChanged(String provider, int status, Bundle extras) {}

    public void onProviderEnabled(String provider) {}

    public void onProviderDisabled(String provider) {}
  };

// Register the listener with the Location Manager to receive location updates
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);

Google的位置策略API指南很好地解释了代码。但是他们也提到,在大多数情况下,通过使用Google Location Services API,您可以获得更好的电池性能以及更合适的准确性。现在混乱开始了!

  1. Google的位置服务API

Google的位置服务API是Google Play服务APK的一部分(以下是设置方法)。它们建立在Android API之上。这些API提供了“融合位置提供程序”,而不是上面提到的提供程序。该提供程序会根据准确性,电池使用情况等自动选择要使用的基础提供程序。这是快速的,因为您可以从不断更新它的系统范围服务中获取位置。您可以使用更高级的功能,例如地理围栏。

要使用Google的位置服务,您的应用需要连接GooglePlayServicesClient。要连接到客户端,您的活动(或片段等)需要实现GooglePlayServicesClient.ConnectionCallbacksGooglePlayServicesClient.OnConnectionFailedListener接口。这是一个示例代码:

    public class MyActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener {
    LocationClient locationClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        locationClient = new LocationClient(this, this, this);
    }

    @Override
    public void onConnected(Bundle bundle) {
    Location location = locationClient.getLastLocation() ;
        Toast.makeText(this, "Connected to Google Play Services", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onDisconnected() {
    Toast.makeText(this, "Connected from Google Play Services.", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        // code to handle failed connection
        // this code can be found here — http://developer.android.com/training/location/retrieve-current.html 
    }
  • 为什么为locationClient.getLastLocation()空?

locationClient.getLastLocation()得到来自客户端的最后已知位置。但是,只有在至少一个客户端与其连接的情况下,融合位置提供程序才会维护后台位置。一旦第一个客户端连接,它将立即尝试获取位置。如果您的活动是第一个客户端连接,并调用getLastLocation()在马上onConnected(),可能没有足够的时间在第一位置进来,这将导致locationnull

要解决此问题,您必须(不确定地)等待提供者获取位置,然后致电getLastLocation(),这是不可能知道的。另一个(更好)的选择是实现该com.google.android.gms.location.LocationListener接口以接收定期的位置更新(并在第一次更新后将其关闭)。

    public class MyActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener, LocationListener {
    // . . . . . . . . more stuff here 
    LocationRequest locationRequest;
    LocationClient locationClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // . . . . other initialization code
        locationClient = new LocationClient(this, this, this);
    locationRequest = new LocationRequest();
    // Use high accuracy
    locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        // Set the update interval to 5 seconds
    locationRequest.setInterval(UPDATE_INTERVAL);
        // Set the fastest update interval to 1 second
    locationRequest.setFastestInterval(FASTEST_INTERVAL);
    }
    // . . . . . . . . other methods 
    @Override
    public void onConnected(Bundle bundle) {
        Location location = locationClient.getLastLocation();
        if (location == null)
            locationClient.requestLocationUpdates(locationRequest, this);
        else
            Toast.makeText(getActivity(), "Location: " + location.getLatitude() + ", " + location.getLongitude(), Toast.LENGTH_SHORT).show();
    }
    // . . . . . . . . other methods
    @Override
    public void onLocationChanged(Location location) {
        locationClient.removeLocationUpdates(this);
        // Use the location here!!!
    }

在这段代码中,您正在检查客户端是否已经有最后一个位置(在中onConnected)。否则,您要请求位置更新,并在onLocationChanged()获得更新后立即关闭请求(在回调中)。

请注意,locationClient.requestLocationUpdates(locationRequest, this);必须位于onConnected回调函数之内,否则您将得到一个,IllegalStateException因为您将尝试请求未连接到Google Play服务客户端的位置。

  • 用户已禁用位置服务

很多时候,用户会禁用位置服务(以节省电池或出于隐私原因)。在这种情况下,上面的代码仍将请求位置更新,但onLocationChanged永远不会被调用。您可以通过检查用户是否已禁用位置服务来停止请求。

如果您的应用要求他们启用位置服务,则需要显示一条消息或祝酒词。不幸的是,无法检查用户是否已在Google的Location Services API中禁用了位置服务。为此,您将不得不使用Android的API。

在您的onCreate方法中:

    LocationManager manager = (LocationManager) getActivity().getSystemService(Context.LOCATION_SERVICE);
if (!manager.isProviderEnabled(LocationManager.GPS_PROVIDER) && !manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
    locationEnabled = false;
    Toast.makeText(getActivity(), "Enable location services for accurate data", Toast.LENGTH_SHORT).show();
}
else locationEnabled = true;

locationEnabled在您的onConnected方法中使用标志,如下所示:

    if (location != null) {
    Toast.makeText(getActivity(), "Location: " + location.getLatitude() + ", " + location.getLongitude(), Toast.LENGTH_SHORT).show();
}
else if (location == null && locationEnabled) {
    locationClient.requestLocationUpdates(locationRequest, this);
}

更新

文档已更新,LocationClient已删除,并且该API支持通过单击对话框即可启用GPS:

task.addOnSuccessListener(this, new OnSuccessListener<LocationSettingsResponse>() {
@Override
public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
    // All location settings are satisfied. The client can initialize
    // location requests here.
    // ...
}
});

task.addOnFailureListener(this, new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception e) {
        if (e instanceof ResolvableApiException) {
            // Location settings are not satisfied, but this can be fixed
            // by showing the user a dialog.
            try {
                // Show the dialog by calling startResolutionForResult(),
                // and check the result in onActivityResult().
                ResolvableApiException resolvable = (ResolvableApiException) e;
                resolvable.startResolutionForResult(MainActivity.this,
                        REQUEST_CHECK_SETTINGS);
            } catch (IntentSender.SendIntentException sendEx) {
                // Ignore the error.
            }
        }
    }
});

链接https://developer.android.com/training/location/change-location-settings#prompt

新位置客户端:FusedLocationProviderClient

  private FusedLocationProviderClient fusedLocationClient;

@Override
protected void onCreate(Bundle savedInstanceState) {
    fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
}

建议您在执行任何定位任务之前先经过https://developer.android.com/training/location


9
您可以SettingsApi.checkLocationSettings()用来检查用户是否已启用位置服务(请参阅此处:developers.google.com/android/reference/com/google/android/gms/…)。
kaolick 2015年

3
LocationClient不再存在。有关位置信息的更新和清晰的指南,请查看以下文章:blog.teamtreehouse.com/beginners-guide-location-android
lm2a

1
与SettingsApi例子和培训页:developer.android.com/training/location/...
androidguy

2
您也可以使用来创建地理围栏LocationManager。融合的位置api是否可以在没有Google Play服务的设备上使用?
穆罕默德·巴巴尔

这可能是检查GPS启用/禁用状态的另一种更好的方法 fun isLocationEnabled(context: Context): Boolean { val locationMode: Int try { locationMode = Settings.Secure.getInt( context.contentResolver, Settings.Secure.LOCATION_MODE ) } catch (e: SettingNotFoundException) { e.printStackTrace() return false } return locationMode != Settings.Secure.LOCATION_MODE_OFF }
Wahib Ul Haq '18

32

以我的经验,“更适当的准确性”绝不意味着更好。除非我缺少任何东西,否则如果要确保使用GPS,则LocationManager是唯一的选择。我们会使用我们的应用程序来跟踪车辆,并且再次提醒您,除非我遗漏了一些东西,否则Google Play服务经常会提供一些非常不准确的位置。


2
是的,根据我的经验,Google Play服务有时会提供不正确的位置。
VY

2
是的,Google Play服务的位置服务API可能会误导位置信息。WiFi调制解调器会移动,WiFi调制解调器会使用不正确的位置信息进行更新(即,如果位置被更新WiFi调制解调器位置的Android设备所欺骗),并且还有许多其他情况可能会导致WiFi调制解调器三角测量产生不正确的位置数据。在我们所有要求精确定位的应用中,我们仅使用GPS。
user1608385

27

您应该使用Google Play服务位置api而不是LocationManager。根据文档:

Google Play服务位置API比Android框架位置API(android.location)更为可取,可为您的应用增加位置感知功能。如果您当前正在使用Android框架位置API,则强烈建议您尽快切换到Google Play服务位置API。

至于为什么要切换,谷歌说:

作为Google Play服务的一部分的Google Location Services API提供了更强大的高级框架,该框架可自动处理位置提供者,用户移动和位置准确性。它还根据您提供的功耗参数处理位置更新计划。在大多数情况下,通过使用Location Services API,您将获得更好的电池性能以及更合适的精度。


2
您是否有这些引用来自的文档的链接?看看Google现在是否有不同的说法将是很有趣的。
爱德华·布雷

3
他们之所以这样说,是因为他们想要您的位置数据!
Flyview

当您将Google Play服务添加到您的应用程序时,如果用户安装了您的应用程序,则会提示他“更新” Google Play服务以使用您的应用程序。如果他/她的手机快满了(包含whatsapp模因和内容)或用户没有数据,请为我抱歉
Deo Dr.

2
@Flyview是的!大声笑,他们说的方式听起来像是一个神奇的解决方案!最大的缺点是您需要在设备上使用Google Play服务!!!
varun

25

我已经使用Google定位服务API已有一段时间了。它确实具有优势,因为它封装了具有多个用于确定位置的来源的复杂性。但是,它封装得太重,因此当您获得一个奇怪的位置时,您将无法确定该奇怪位置的来源。

在现实生活中,我弹出了几个怪胎值,离实际位置10公里。唯一的解释是,这些疯狂的地点源于Google Wi-Fi或NWK数据库中的错误-由于Wi-Fi和网络拓扑每天都在变化,因此错误始终存在。但是不幸的是(而且令人惊讶的是)API没有提供有关如何得出个人头寸的信息。

在此处输入图片说明

这给您带来了必须根据对速度,加速度,轴承等的合理性检查来过滤出异常值的问题。

...或者回到旧的框架API并仅使用GPS,这是我决定在Google改进融合API之前所做的事情。


7

Google Location Services API是Google Play服务的一部分,它提供了功能更强大的高级框架,可自动处理位置提供者用户移动位置准确性。它还根据您提供的功耗参数处理位置更新计划。在大多数情况下,通过使用Location Services API,您将获得更好的电池性能以及更合适的精度。

可以在此处找到两个API之间的Google Play服务位置APIAndroid框架位置API的更详细的区别


如果可以的话,我用您的回答回答了自己的问题。非常感谢!stackoverflow.com/questions/39852955/…–
Leniaal

@Leniaal,所以您不认为我的答案值得推荐吗?
Dhruvam Gupta

那是一个有3年历史的文件。但是这里有一个加号1
Drew

5

是的,Google Play服务的位置服务API可能会误导位置信息。WiFi调制解调器会移动,WiFi调制解调器会使用不正确的位置信息进行更新(即,如果位置被更新WiFi调制解调器位置的Android设备所欺骗),并且还有许多其他情况可能会导致WiFi调制解调器三角剖分产生不正确的位置数据。在我们所有要求精确定位的应用中,我们仅使用GPS。


4

基于GPS服务的两个API Google Play服务位置API和Android框架位置API之间的区别

FusedLocationProviderClient

  1. 对于第一次抓取,该位置不能为空(即:某些其他应用需要更新GoogleplayService数据库中的Lastknown位置。如果为空,则需要解决)
  2. 对于下一个顺序的访存,它使用requestLocationUpdates()方法来访存位置。
  3. 位置提取仅基于locationRequest.setInterval(milliseconds)setFastestInterval(milliseconds),而不基于用户位置更改
  4. 返回的LatLng值包含7个十进制值(例如:11.9557996、79.8234599),不那么准确
  5. 推荐,当您的应用要求当前位置距离可忽略不计(50-100米精度)
  6. 有效使用电池。

LocationManager Api

  1. 使用locationManager.requestLocationUpdates()调用用户位置提取
  2. 根据用户位置更改时间间隔获取位置 locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, milliseconds, mindistance, Mylocationlistener)

  3. 返回的LatLng值包含14个十进制值 (例如:11.94574594963342 79.81166719458997) 准确的位置值

  4. 推荐用于基于位置的应用程序,当需要更高的精度时(甚至以米为单位)。
  5. 电池使用情况取决于获取间隔和获取距离。

嘿!1°最高为111_111米(20_000_000 / 180)。因此,0.0000001°是0.011m = 1 cm。我不知道您在哪里需要更好的GPS分辨率。而且我什至都不知道,如何才能获得更好的GPS分辨率。
babay
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.