当前位置:   article > 正文

Android获取经纬度的最佳实现方式_fusedlocationproviderclient

fusedlocationproviderclient

Android中获取定位信息的方式有很多种,系统自带的LocationManager,以及第三方厂商提供的一些定位sdk,都能帮助我们获取当前经纬度,但第三方厂商一般都需要申请相关的key,且调用量高时,还会产生资费问题。这里采用LocationManager + FusedLocationProviderClient 的方式进行经纬度的获取,以解决普通场景下获取经纬度和经纬度转换地址的功能。

一,添加定位权限

  1. <!--允许获取精确位置,精准定位必选-->
  2. <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  3. <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  4. <!--后台获取位置信息,若需后台定位则必选-->
  5. <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
  6. <!--用于申请调用A-GPS模块,卫星定位加速-->
  7. <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />

二,添加依赖库

  1. implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
  2. implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'
  3. implementation 'com.google.android.gms:play-services-location:21.0.1'

三,使用LocationManager获取当前经纬度

获取经纬度时,可根据自己的诉求进行参数自定义,如果对经纬度要求不是很精确的可以自行配置Criteria里面的参数。

获取定位前需要先判断相关的服务是否可用,获取定位的服务其实有很多种选择,因为个人项目对经纬度准确性要求较高,为了保证获取的成功率和准确性,只使用了GPS和网络定位两种,如果在国内还会有基站获取等方式,可以自行修改。

  1. import android.Manifest.permission
  2. import android.location.*
  3. import android.os.Bundle
  4. import android.util.Log
  5. import androidx.annotation.RequiresPermission
  6. import kotlinx.coroutines.suspendCancellableCoroutine
  7. import kotlinx.coroutines.withTimeout
  8. import kotlin.coroutines.resume
  9. object LocationManagerUtils {
  10. val TAG = "LocationManagerUtils"
  11. /**
  12. * @mLocationManager 传入LocationManager对象
  13. * @minDistance 位置变化最小距离:当位置距离变化超过此值时,将更新位置信息(单位:米)
  14. * @timeOut 超时时间,如果超时未返回,则直接使用默认值
  15. */
  16. @RequiresPermission(anyOf = [permission.ACCESS_COARSE_LOCATION, permission.ACCESS_FINE_LOCATION])
  17. suspend fun getCurrentPosition(
  18. mLocationManager: LocationManager,
  19. timeOut: Long = 3000,
  20. ):Location{
  21. var locationListener : LocationListener?=null
  22. return try {
  23. //超时未返回则直接获取失败,返回默认值
  24. withTimeout(timeOut){
  25. suspendCancellableCoroutine {continuation ->
  26. //获取最佳定位方式,如果获取不到则默认采用网络定位。
  27. var bestProvider = mLocationManager.getBestProvider(createCriteria(),true)
  28. if (bestProvider.isNullOrEmpty()||bestProvider == "passive"){
  29. bestProvider = "network"
  30. }
  31. Log.d(TAG, "getCurrentPosition:bestProvider:${bestProvider}")
  32. locationListener = object : LocationListener {
  33. override fun onLocationChanged(location: Location) {
  34. Log.d(TAG, "getCurrentPosition:onCompete:${location.latitude},${location.longitude}")
  35. if (continuation.isActive){
  36. continuation.resume(location)
  37. mLocationManager.removeUpdates(this)
  38. }
  39. }
  40. override fun onProviderDisabled(provider: String) {
  41. }
  42. override fun onProviderEnabled(provider: String) {
  43. }
  44. }
  45. //开始定位
  46. mLocationManager.requestLocationUpdates(bestProvider,
  47. 1000,0f,
  48. locationListener!!)
  49. }
  50. }
  51. }catch (e:Exception){
  52. try {
  53. locationListener?.let {
  54. mLocationManager.removeUpdates(it)
  55. }
  56. }catch (e:Exception){
  57. Log.d(TAG, "getCurrentPosition:removeUpdate:${e.message}")
  58. }
  59. //超时直接返回默认的空对象
  60. Log.d(TAG, "getCurrentPosition:onError:${e.message}")
  61. return createDefaultLocation()
  62. }
  63. }
  64. @RequiresPermission(anyOf = [permission.ACCESS_COARSE_LOCATION, permission.ACCESS_FINE_LOCATION])
  65. suspend fun repeatLocation(mLocationManager: LocationManager):Location{
  66. return suspendCancellableCoroutine {continuation ->
  67. //获取最佳定位方式,如果获取不到则默认采用网络定位。
  68. var bestProvider = mLocationManager.getBestProvider(createCriteria(),true)
  69. if (bestProvider.isNullOrEmpty()||bestProvider == "passive"){
  70. bestProvider = "network"
  71. }
  72. Log.d(TAG, "getCurrentPosition:bestProvider:${bestProvider}")
  73. val locationListener = object : LocationListener {
  74. override fun onLocationChanged(location: Location) {
  75. Log.d(TAG, "getCurrentPosition:onCompete:${location.latitude},${location.longitude}")
  76. if (continuation.isActive){
  77. continuation.resume(location)
  78. }
  79. mLocationManager.removeUpdates(this)
  80. }
  81. override fun onProviderDisabled(provider: String) {
  82. }
  83. override fun onProviderEnabled(provider: String) {
  84. }
  85. }
  86. //开始定位
  87. mLocationManager.requestLocationUpdates(bestProvider,1000, 0f, locationListener)
  88. }
  89. }
  90. @RequiresPermission(anyOf = [permission.ACCESS_COARSE_LOCATION, permission.ACCESS_FINE_LOCATION])
  91. fun getLastLocation( mLocationManager: LocationManager): Location {
  92. //获取最佳定位方式,如果获取不到则默认采用网络定位。
  93. var currentProvider = mLocationManager.getBestProvider(createCriteria(), true)
  94. if (currentProvider.isNullOrEmpty()||currentProvider == "passive"){
  95. currentProvider = "network"
  96. }
  97. return mLocationManager.getLastKnownLocation(currentProvider) ?: createDefaultLocation()
  98. }
  99. //创建定位默认值
  100. fun createDefaultLocation():Location{
  101. val location = Location("network")
  102. location.longitude = 0.0
  103. location.latitude = 0.0
  104. return location
  105. }
  106. private fun createCriteria():Criteria{
  107. return Criteria().apply {
  108. accuracy = Criteria.ACCURACY_FINE
  109. isAltitudeRequired = false
  110. isBearingRequired = false
  111. isCostAllowed = true
  112. powerRequirement = Criteria.POWER_HIGH
  113. isSpeedRequired = false
  114. }
  115. }
  116. ///定位是否可用
  117. fun checkLocationManagerAvailable(mLocationManager: LocationManager):Boolean{
  118. return mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)||
  119. mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
  120. }
  121. }

二,使用FusedLocationProviderClient

在获取经纬度时会出现各种异常的场景,会导致成功的回调一直无法触发,这里使用了协程,如果超过指定超时时间未返回,则直接默认为获取失败,进行下一步的处理。

  1. import android.Manifest
  2. import android.app.Activity
  3. import android.content.Context
  4. import android.content.Context.LOCATION_SERVICE
  5. import android.content.Intent
  6. import android.location.Geocoder
  7. import android.location.Location
  8. import android.location.LocationManager
  9. import android.provider.Settings
  10. import android.util.Log
  11. import androidx.annotation.RequiresPermission
  12. import com.google.android.gms.location.LocationServices
  13. import kotlinx.coroutines.Dispatchers
  14. import kotlinx.coroutines.suspendCancellableCoroutine
  15. import kotlinx.coroutines.withContext
  16. import kotlinx.coroutines.withTimeout
  17. import java.io.IOException
  18. import java.util.*
  19. import kotlin.coroutines.resume
  20. object FusedLocationProviderUtils {
  21. val TAG = "FusedLocationUtils"
  22. @RequiresPermission(anyOf = ["android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION"])
  23. suspend fun checkFusedLocationProviderAvailable(fusedLocationClient: FusedLocationProviderClient):Boolean{
  24. return try {
  25. withTimeout(1000){
  26. suspendCancellableCoroutine { continuation ->
  27. fusedLocationClient.locationAvailability.addOnFailureListener {
  28. Log.d(TAG, "locationAvailability:addOnFailureListener:${it.message}")
  29. if (continuation.isActive){
  30. continuation.resume(false)
  31. }
  32. }.addOnSuccessListener {
  33. Log.d(TAG, "locationAvailability:addOnSuccessListener:${it.isLocationAvailable}")
  34. if (continuation.isActive){
  35. continuation.resume(it.isLocationAvailable)
  36. }
  37. }
  38. }
  39. }
  40. }catch (e:Exception){
  41. return false
  42. }
  43. }
  44. ///获取最后已知的定位信息
  45. @RequiresPermission(anyOf = ["android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION"])
  46. suspend fun getLastLocation(fusedLocationClient: FusedLocationProviderClient):Location{
  47. return suspendCancellableCoroutine {continuation ->
  48. fusedLocationClient.lastLocation.addOnSuccessListener {
  49. if (continuation.isActive){
  50. Log.d(TAG, "current location success:$it")
  51. if (it != null){
  52. continuation.resume(it)
  53. }else{
  54. continuation.resume(createDefaultLocation())
  55. }
  56. }
  57. }.addOnFailureListener {
  58. continuation.resume(createDefaultLocation())
  59. }
  60. }
  61. }
  62. /**
  63. * 获取当前定位,需要申请定位权限
  64. *
  65. */
  66. @RequiresPermission(anyOf = ["android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION"])
  67. suspend fun getCurrentPosition(fusedLocationClient: FusedLocationProviderClient): Location {
  68. return suspendCancellableCoroutine {continuation ->
  69. fusedLocationClient.getCurrentLocation(createLocationRequest(),object : CancellationToken(){
  70. override fun onCanceledRequested(p0: OnTokenCanceledListener): CancellationToken {
  71. return CancellationTokenSource().token
  72. }
  73. override fun isCancellationRequested(): Boolean {
  74. return false
  75. }
  76. }).addOnSuccessListener {
  77. if (continuation.isActive){
  78. Log.d(TAG, "current location success:$it")
  79. if (it != null){
  80. continuation.resume(it)
  81. }else{
  82. continuation.resume(createDefaultLocation())
  83. }
  84. }
  85. }.addOnFailureListener {
  86. Log.d(TAG, "current location fail:$it")
  87. if (continuation.isActive){
  88. continuation.resume(createDefaultLocation())
  89. }
  90. }.addOnCanceledListener {
  91. Log.d(TAG, "current location cancel:")
  92. if (continuation.isActive){
  93. continuation.resume(createDefaultLocation())
  94. }
  95. }
  96. }
  97. }
  98. //创建当前LocationRequest对象
  99. private fun createLocationRequest():CurrentLocationRequest{
  100. return CurrentLocationRequest.Builder()
  101. .setDurationMillis(1000)
  102. .setMaxUpdateAgeMillis(5000)
  103. .setPriority(Priority.PRIORITY_HIGH_ACCURACY)
  104. .build()
  105. }
  106. //创建默认值
  107. private fun createDefaultLocation():Location{
  108. val location = Location("network")
  109. location.longitude = 0.0
  110. location.latitude = 0.0
  111. return location
  112. }
  113. }

三,整合LocationManager和FusedLocationProviderClient

在获取定位时,可能会出现GPS定位未开启的情况,所以不管是LocationManager或FusedLocationProviderClient都需要判断当前服务是否可用,获取定位时,如果GPS信号较弱等异常情况下,就需要考虑到获取定位超时的情况,这里使用了协程,如FusedLocationProviderClient超过3秒未获取成功,则直接切换到LocationManager进行二次获取,这是提升获取经纬度成功的关键。

在实际项目中,如果对获取经纬度有较高的考核要求时,通过结合LocationManager和FusedLocationProviderClient如果还是获取不到,可考虑集成第三方的进行进一步获取,可以考虑使用华为的免费融合定位服务,因为我们使用过百度地图的sdk,每天会出现千万分之五左右的定位错误和定位漂移问题。

  1. import android.Manifest
  2. import android.app.Activity
  3. import android.content.Context
  4. import android.content.Context.LOCATION_SERVICE
  5. import android.content.Intent
  6. import android.location.Geocoder
  7. import android.location.Location
  8. import android.location.LocationManager
  9. import android.provider.Settings
  10. import android.util.Log
  11. import androidx.annotation.RequiresPermission
  12. import com.google.android.gms.location.LocationServices
  13. import kotlinx.coroutines.Dispatchers
  14. import kotlinx.coroutines.suspendCancellableCoroutine
  15. import kotlinx.coroutines.withContext
  16. import kotlinx.coroutines.withTimeout
  17. import java.io.IOException
  18. import java.util.*
  19. import kotlin.coroutines.resume
  20. object LocationHelper {
  21. fun getLocationServiceStatus(context: Context):Boolean{
  22. return (context.getSystemService(LOCATION_SERVICE) as LocationManager)
  23. .isProviderEnabled(LocationManager.GPS_PROVIDER)
  24. }
  25. /**
  26. * 打开定位服务设置
  27. */
  28. fun openLocationSetting(context: Context):Boolean{
  29. return try {
  30. val settingsIntent = Intent()
  31. settingsIntent.action = Settings.ACTION_LOCATION_SOURCE_SETTINGS
  32. settingsIntent.addCategory(Intent.CATEGORY_DEFAULT)
  33. settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
  34. settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
  35. settingsIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
  36. context.startActivity(settingsIntent)
  37. true
  38. } catch (ex: java.lang.Exception) {
  39. false
  40. }
  41. }
  42. /**
  43. * 获取当前定位
  44. */
  45. @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])
  46. suspend fun getLocation(context: Activity,timeOut: Long = 2000):Location{
  47. val location = getLocationByFusedLocationProviderClient(context)
  48. //默认使用FusedLocationProviderClient 如果FusedLocationProviderClient不可用或获取失败,则使用LocationManager进行二次获取
  49. Log.d("LocationHelper", "getLocation:$location")
  50. return if (location.latitude == 0.0){
  51. getLocationByLocationManager(context, timeOut)
  52. }else{
  53. location
  54. }
  55. }
  56. @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])
  57. private suspend fun getLocationByLocationManager(context: Activity,timeOut: Long = 2000):Location{
  58. Log.d("LocationHelper", "getLocationByLocationManager")
  59. val locationManager = context.getSystemService(LOCATION_SERVICE) as LocationManager
  60. //检查LocationManager是否可用
  61. return if (LocationManagerUtils.checkLocationManagerAvailable(locationManager)){
  62. //使用LocationManager获取当前经纬度
  63. val location = LocationManagerUtils.getCurrentPosition(locationManager, timeOut)
  64. if (location.latitude == 0.0){
  65. LocationManagerUtils.getLastLocation(locationManager)
  66. }else{
  67. location
  68. }
  69. }else{
  70. //获取失败,则采用默认经纬度
  71. LocationManagerUtils.createDefaultLocation()
  72. }
  73. }
  74. @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])
  75. private suspend fun getLocationByFusedLocationProviderClient(context: Activity):Location{
  76. Log.d("LocationHelper", "getLocationByFusedLocationProviderClient")
  77. //使用FusedLocationProviderClient进行定位
  78. val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
  79. return if (FusedLocationProviderUtils.checkFusedLocationProviderAvailable(fusedLocationClient)){
  80. withContext(Dispatchers.IO){
  81. //使用FusedLocationProviderClient获取当前经纬度
  82. val location = FusedLocationProviderUtils.getCurrentPosition(fusedLocationClient)
  83. if (location.latitude == 0.0){
  84. FusedLocationProviderUtils.getLastLocation(fusedLocationClient)
  85. }else{
  86. location
  87. }
  88. }
  89. }else{
  90. LocationManagerUtils.createDefaultLocation()
  91. }
  92. }
  93. }
注:因为获取定位是比较耗电的操作,在实际使用时,可增加缓存机制,比如2分钟之内频繁,则返回上一次缓存的数据,如果超过2分钟则重新获取一次,并缓存起来。

四,获取当前经纬度信息或经纬度转换地址

1,获取当前经纬度
  1. @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])
  2. fun getCurrentLocation(activity:Activity){
  3. if (activity != null){
  4. val exceptionHandler = CoroutineExceptionHandler { _, exception ->
  5. }
  6. viewModelScope.launch(exceptionHandler) {
  7. val location = LocationHelper.getLocation(activity!!)
  8. val map = HashMap<String,String>()
  9. map["latitude"] ="${location.latitude}"
  10. map["longitude"] = "${location.longitude}"
  11. }
  12. }
  13. }
2,经纬度转换地址
  1. /**
  2. * @param latitude 经度
  3. * @param longitude 纬度
  4. * @return 详细位置信息
  5. */
  6. suspend fun convertAddress(context: Context, latitude: Double, longitude: Double): String {
  7. return try {
  8. withTimeout(3000){
  9. suspendCancellableCoroutine { continuation ->
  10. try {
  11. val mGeocoder = Geocoder(context, Locale.getDefault())
  12. val mStringBuilder = StringBuilder()
  13. if (Geocoder.isPresent()){
  14. val mAddresses = mGeocoder.getFromLocation(latitude, longitude, 1)
  15. if (mAddresses!= null &&mAddresses.size >0) {
  16. val address = mAddresses[0]
  17. Log.d("LocationUtils", "convertAddress()--->$address")
  18. mStringBuilder.append(address.getAddressLine(0)?:"")
  19. .append(",")
  20. .append(address.adminArea?:address.subAdminArea?:"")
  21. .append(",")
  22. .append(address.locality?:address.subLocality?:"")
  23. .append(",")
  24. .append(address.thoroughfare?:address.subThoroughfare?:"")
  25. }
  26. }
  27. if (continuation.isActive){
  28. continuation.resume(mStringBuilder.toString())
  29. }
  30. } catch (e: IOException) {
  31. Log.d("LocationUtils", "convertAddress()--IOException->${e.message}")
  32. if (continuation.isActive){
  33. continuation.resume("")
  34. }
  35. }
  36. }
  37. }
  38. }catch (e:Exception){
  39. Log.d("LocationUtils", "convertAddress()--->timeout")
  40. return ""
  41. }
  42. }

调用时:

  1. fun covertAddress(latitude:double,longitude:double){
  2. if (activity != null){
  3. val exceptionHandler = CoroutineExceptionHandler { _, exception ->
  4. }
  5. viewModelScope.launch(exceptionHandler) {
  6. val hashMap = argument as HashMap<*, *>
  7. withContext(Dispatchers.IO){
  8. val address = LocationHelper.convertAddress(activity!!,
  9. "${hashMap["latitude"]}".toDouble(),
  10. "${hashMap["longitude"]}".toDouble())
  11. }
  12. }
  13. }
  14. }

注:经纬度转换地址时,需要开启一个线程或者协程进行转换,不然会阻塞主线程,引发异常。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/1000935
推荐阅读
相关标签
  

闽ICP备14008679号