【问题标题】:Android : LocationManager vs Google Play ServicesAndroid:LocationManager 与 Google Play 服务
【发布时间】:2016-01-06 11:33:04
【问题描述】:

我想构建一个以获取用户当前位置为中心的应用,然后通过 Google Places API找到他/她附近的兴趣点(如酒吧、餐馆等) >。

在网上搜索起点时,我发现了一些使用 LocationManager 类的教程和其他一些使用 Google Play 服务 来查找用户位置的教程。

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

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

【问题讨论】:

标签: android gps geolocation


【解决方案1】:

Android 上的用户位置

在 Android 上获取用户的位置比在 iOS 上要简单一些。要开始混淆,有两种完全不同的方法可以做到这一点。第一个是使用来自android.location.LocationListener 的Android API,第二个是使用Google Play Services API com.google.android.gms.location.LocationListener。让我们来看看它们。

  1. Android 的位置 API

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

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

其要点是从系统中获取LocationManager的对象,实现LocationListener,并在LocationManager上调用requestLocationUpdates

这是一个代码 sn-p:

    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’s API Guide on Location Strategies 很好地解释了代码。但他们也提到,在大多数情况下,通过使用Google Location Services API,您将获得更好的电池性能以及更合适的精度。现在混乱开始了!

  1. Google 的位置服务 API

Google 的位置服务 API 是 Google Play 服务 APK (here’s how to set it up) 的一部分。它们建立在 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() 从客户端获取最后一个已知位置。但是,Fused Location Provider 仅在至少有一个客户端连接到它时才会维护后台位置。一旦第一个客户端连接,它将立即尝试获取位置。如果您的活动是第一个连接的客户端,并且您立即在onConnected() 中调用getLastLocation(),则可能没有足够的时间让第一个位置进入。这将导致location 成为null

要解决此问题,您必须(不确定地)等待,直到提供者获取位置,然后调用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 的位置服务 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;

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

    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

【讨论】:

  • 您可以使用SettingsApi.checkLocationSettings()来检查用户是否启用了定位服务(参见此处:developers.google.com/android/reference/com/google/android/gms/…)。
  • LocationClient 不再存在。有关位置的更新和清晰指南,请查看这篇文章:blog.teamtreehouse.com/beginners-guide-location-android
  • 以及带有 SettingsApi 示例的培训页面:developer.android.com/training/location/…
  • 您也可以使用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 }
【解决方案2】:

根据我的经验,“更合适的准确性”并不意味着更好。除非我遗漏了什么,否则如果您想确保使用 GPS,LocationManager 是唯一的方法。我们使用我们的应用跟踪车辆,而且,除非我遗漏了什么,否则 Google Play 服务经常会提供一些非常不准确的位置。

【讨论】:

  • 是的,根据我的经验,Google Play 服务有时会提供不准确的位置。
  • 是的,Google Play 服务定位服务 API 可能会提供非常具有误导性的定位信息。 WiFi 调制解调器被移动,WiFi 调制解调器使用不正确的位置信息进行更新(即,如果位置被更新 WiFi 调制解调器位置的 Android 设备欺骗),还有许多其他情况可能导致来自 WiFi 调制解调器三角测量的位置数据不正确。在我们所有要求精确定位的应用中,我们仅使用 GPS。
【解决方案3】:

我使用 Google 位置服务 API 已经有一段时间了。它确实有优势,因为它封装了具有多个确定位置的来源的复杂性。但是它封装得太重了,所以当你得到一个奇怪的位置时,你无法确定那个奇怪的位置是从哪里来的。

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

这给您带来了必须根据对速度、加速度、方位等的合理性检查来过滤掉异常值的问题。

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

【讨论】:

    【解决方案4】:

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

    Google Play 服务位置 API 优于 Android 框架位置 API (android.location) 作为添加位置的一种方式 对您的应用的认识。如果您当前使用的是安卓 框架位置 API,强烈建议您切换到 尽快提供 Google Play 服务位置 API。

    至于为什么要切换,谷歌是这样说的:

    Google 位置服务 API,Google Play 服务的一部分, 提供了一个更强大的高级框架,可以自动 处理位置提供者、用户移动和位置准确性。它 还处理基于功耗的位置更新调度 您提供的参数。在大多数情况下,你会得到更好的电池 性能,以及更合适的精度,通过使用 位置服务 API。

    【讨论】:

    • 您是否有指向这些引用来自该文档的链接?看看谷歌现在是否有不同的说法会很有趣。
    • 他们当然这么说,因为他们想要你的位置数据!
    • 当您将 Google Play 服务添加到您的应用时,如果用户安装了您的应用,系统会提示他“更新”google play 服务以使用您的应用。万一他/她的手机快满了(带有whatsapp meme和东西)或者用户没有数据,对不起我的朋友
    • @Flyview 是的!大声笑,他们所说的方式使它听起来像是一个神奇的解决方案!最大的缺点是你需要在设备上使用 Google Play Services!!!
    【解决方案5】:

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

    Google Play Service Location APIAndroid Framework Location API这两个api更详细的区别可以看here

    【讨论】:

    • 如果可以的话,我用你的回答来回答我自己的问题。非常感谢! stackoverflow.com/questions/39852955/…
    • @Leniaal 所以你不认为我的回答值得投票吗?
    • 那是 3 年前的文件。但这是一个加号 1
    【解决方案6】:

    基于GPS服务

    的Google Play Service Location API和Android Framework Location API这两个api的区别

    FusedLocationProviderClient

    1. 对于第一次提取,位置不得为空(即:某些其他应用需要更新 GoogleplayService 数据库中的 Lastknown 位置。如果为空,需要解决)
    2. 对于下一个顺序 Fetch,它使用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 分辨率。
    【解决方案7】:

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

    【讨论】:

      【解决方案8】:

      Android 位置

      LocationManager - Context.getSystemService(Context.LOCATION_SERVICE)

      • ACCESS_COARSE_LOCATION - 提供不太准确的位置(城市街区),但速度更快,并且不会消耗太多电池

        • NETWORK_PROVIDER - 使用手机信号塔、wifi 接入点
        • PASSIVE_PROVIDER - 当系统中的其他人使用其他提供商时订阅位置更新
      • ACCESS_FINE_LOCATION - 提供更好和准确的位置(最多几米)。使用与 ACCESS_COARSE_LOCATION 相同的提供程序

        • GPS_PROVIDER - 使用卫星

      Google API 定位服务 - GoogleApiClient 基于 Google Play 服务。可以访问通过系统的位置事件的高级 API。它具有更好的电池性能,但无法安装在某些设备上

      • 融合位置提供商 - 根据您的需求和设备条件自动选择合适的提供商

      【讨论】:

        【解决方案9】:

        添加到接受的答案时,通过 Google 提供的AlertDialog 解决方案启用 GPS。 ActivityResultContract 的实现如下:

        // Global Activity Scope
        
        ActivityResultLauncher<IntentSenderRequest> gpsRequestLauncher = registerForActivityResult(
                new ActivityResultContracts.StartIntentSenderForResult(), callback -> {
                if(callback.getResultCode() == RESULT_CANCELED) {
                    // Request Cancelled
                } else {
                    //  GPS Enabled
                }
        });
        

        由于文档中的代码已过时,onActivityResults 已弃用

        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().
                            IntentSenderRequest request = new IntentSenderRequest.Builder(
                                e.resolution).build();
                            gpsRequestLauncher.launch(request);
                        } catch (IntentSender.SendIntentException sendEx) {
                            // Ignore the error.
                        }
                    }
                }
            });
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-06-16
          • 1970-01-01
          • 2012-11-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多