小工具知识网

当前位置: 首页 > 行业知识 > Android获取经纬度的最佳实现方式

行业知识

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

2026-11-02 21:43:57 来源:互联网转载

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

一,添加定位权限

 

二,添加依赖库

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

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

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

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

import android.Manifest.permissionimport android.location.*import android.os.Bundleimport android.util.Logimport androidx.annotation.RequiresPermissionimport kotlinx.coroutines.suspendCancellableCoroutineimport kotlinx.coroutines.withTimeoutimport kotlin.coroutines.resumeobject LocationManagerUtils {    val TAG = "LocationManagerUtils"    /**     * @mLocationManager 传入LocationManager对象     * @minDistance  位置变化最小距离:当位置距离变化超过此值时,将更新位置信息(单位:米)     * @timeOut 超时时间,如果超时未返回,则直接使用默认值     */    @RequiresPermission(anyOf = [permission.ACCESS_COARSE_LOCATION, permission.ACCESS_FINE_LOCATION])    suspend  fun getCurrentPosition(        mLocationManager: LocationManager,        timeOut: Long = 3000,    ):Location{        var locationListener : LocationListener?=null        return  try {            //超时未返回则直接获取失败,返回默认值            withTimeout(timeOut){                suspendCancellableCoroutine {continuation ->                    //获取最佳定位方式,如果获取不到则默认采用网络定位。                    var bestProvider = mLocationManager.getBestProvider(createCriteria(),true)                    if (bestProvider.isNullOrEmpty()||bestProvider == "passive"){                        bestProvider = "network"                    }                    Log.d(TAG, "getCurrentPosition:bestProvider:${bestProvider}")                    locationListener = object : LocationListener {                        override fun onLocationChanged(location: Location) {                            Log.d(TAG, "getCurrentPosition:onCompete:${location.latitude},${location.longitude}")                            if (continuation.isActive){                                continuation.resume(location)                                mLocationManager.removeUpdates(this)                            }                        }                        override fun onProviderDisabled(provider: String) {                        }                        override fun onProviderEnabled(provider: String) {                        }                    }                    //开始定位                    mLocationManager.requestLocationUpdates(bestProvider,                        1000,0f,                        locationListener!!)                }            }        }catch (e:Exception){            try {                locationListener?.let {                    mLocationManager.removeUpdates(it)                }            }catch (e:Exception){                Log.d(TAG, "getCurrentPosition:removeUpdate:${e.message}")            }            //超时直接返回默认的空对象            Log.d(TAG, "getCurrentPosition:onError:${e.message}")            return createDefaultLocation()        }    }    @RequiresPermission(anyOf = [permission.ACCESS_COARSE_LOCATION, permission.ACCESS_FINE_LOCATION])    suspend fun repeatLocation(mLocationManager: LocationManager):Location{        return  suspendCancellableCoroutine {continuation ->            //获取最佳定位方式,如果获取不到则默认采用网络定位。            var bestProvider = mLocationManager.getBestProvider(createCriteria(),true)            if (bestProvider.isNullOrEmpty()||bestProvider == "passive"){                bestProvider = "network"            }            Log.d(TAG, "getCurrentPosition:bestProvider:${bestProvider}")            val locationListener = object : LocationListener {                override fun onLocationChanged(location: Location) {                    Log.d(TAG, "getCurrentPosition:onCompete:${location.latitude},${location.longitude}")                    if (continuation.isActive){                        continuation.resume(location)                    }                    mLocationManager.removeUpdates(this)                }                override fun onProviderDisabled(provider: String) {                }                override fun onProviderEnabled(provider: String) {                }            }            //开始定位            mLocationManager.requestLocationUpdates(bestProvider,1000, 0f, locationListener)        }    }    @RequiresPermission(anyOf = [permission.ACCESS_COARSE_LOCATION, permission.ACCESS_FINE_LOCATION])    fun getLastLocation( mLocationManager: LocationManager): Location {        //获取最佳定位方式,如果获取不到则默认采用网络定位。        var currentProvider = mLocationManager.getBestProvider(createCriteria(), true)        if (currentProvider.isNullOrEmpty()||currentProvider == "passive"){            currentProvider = "network"        }        return mLocationManager.getLastKnownLocation(currentProvider) ?: createDefaultLocation()    }    //创建定位默认值        fun createDefaultLocation():Location{        val location = Location("network")        location.longitude = 0.0        location.latitude = 0.0        return location    }    private fun createCriteria():Criteria{        return  Criteria().apply {            accuracy = Criteria.ACCURACY_FINE            isAltitudeRequired = false            isBearingRequired = false            isCostAllowed = true            powerRequirement = Criteria.POWER_HIGH            isSpeedRequired = false        }    }    ///定位是否可用    fun checkLocationManagerAvailable(mLocationManager: LocationManager):Boolean{       return mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)||            mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)    }}

二,使用FusedLocationProviderClient

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

import android.Manifestimport android.app.Activityimport android.content.Contextimport android.content.Context.LOCATION_SERVICEimport android.content.Intentimport android.location.Geocoderimport android.location.Locationimport android.location.LocationManagerimport android.provider.Settingsimport android.util.Logimport androidx.annotation.RequiresPermissionimport com.google.android.gms.location.LocationServicesimport kotlinx.coroutines.Dispatchersimport kotlinx.coroutines.suspendCancellableCoroutineimport kotlinx.coroutines.withContextimport kotlinx.coroutines.withTimeoutimport java.io.IOExceptionimport java.util.*import kotlin.coroutines.resumeobject FusedLocationProviderUtils {    val TAG = "FusedLocationUtils"    @RequiresPermission(anyOf = ["android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION"])    suspend  fun checkFusedLocationProviderAvailable(fusedLocationClient: FusedLocationProviderClient):Boolean{        return  try {            withTimeout(1000){                suspendCancellableCoroutine { continuation ->                    fusedLocationClient.locationAvailability.addOnFailureListener {                        Log.d(TAG, "locationAvailability:addOnFailureListener:${it.message}")                        if (continuation.isActive){                            continuation.resume(false)                        }                    }.addOnSuccessListener {                        Log.d(TAG, "locationAvailability:addOnSuccessListener:${it.isLocationAvailable}")                        if (continuation.isActive){                            continuation.resume(it.isLocationAvailable)                        }                    }                }            }        }catch (e:Exception){            return false        }    }    ///获取最后已知的定位信息    @RequiresPermission(anyOf = ["android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION"])    suspend fun getLastLocation(fusedLocationClient: FusedLocationProviderClient):Location{        return  suspendCancellableCoroutine {continuation ->            fusedLocationClient.lastLocation.addOnSuccessListener {                if (continuation.isActive){                    Log.d(TAG, "current location success:$it")                    if (it != null){                        continuation.resume(it)                    }else{                        continuation.resume(createDefaultLocation())                    }                }            }.addOnFailureListener {                continuation.resume(createDefaultLocation())            }        }    }    /**     * 获取当前定位,需要申请定位权限     *     */    @RequiresPermission(anyOf = ["android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION"])    suspend fun getCurrentPosition(fusedLocationClient: FusedLocationProviderClient): Location {        return suspendCancellableCoroutine {continuation ->            fusedLocationClient.getCurrentLocation(createLocationRequest(),object : CancellationToken(){                override fun onCanceledRequested(p0: OnTokenCanceledListener): CancellationToken {                    return CancellationTokenSource().token                }                override fun isCancellationRequested(): Boolean {                    return false                }            }).addOnSuccessListener {                if (continuation.isActive){                    Log.d(TAG, "current location success:$it")                    if (it != null){                        continuation.resume(it)                    }else{                        continuation.resume(createDefaultLocation())                    }                }            }.addOnFailureListener {                Log.d(TAG, "current location fail:$it")                if (continuation.isActive){                    continuation.resume(createDefaultLocation())                }            }.addOnCanceledListener {                Log.d(TAG, "current location cancel:")                if (continuation.isActive){                    continuation.resume(createDefaultLocation())                }            }        }    }    //创建当前LocationRequest对象    private fun createLocationRequest():CurrentLocationRequest{        return CurrentLocationRequest.Builder()            .setDurationMillis(1000)            .setMaxUpdateAgeMillis(5000)            .setPriority(Priority.PRIORITY_HIGH_ACCURACY)            .build()    }    //创建默认值    private fun createDefaultLocation():Location{        val location = Location("network")        location.longitude = 0.0        location.latitude = 0.0        return location    }}

三,整合LocationManager和FusedLocationProviderClient

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

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

import android.Manifestimport android.app.Activityimport android.content.Contextimport android.content.Context.LOCATION_SERVICEimport android.content.Intentimport android.location.Geocoderimport android.location.Locationimport android.location.LocationManagerimport android.provider.Settingsimport android.util.Logimport androidx.annotation.RequiresPermissionimport com.google.android.gms.location.LocationServicesimport kotlinx.coroutines.Dispatchersimport kotlinx.coroutines.suspendCancellableCoroutineimport kotlinx.coroutines.withContextimport kotlinx.coroutines.withTimeoutimport java.io.IOExceptionimport java.util.*import kotlin.coroutines.resumeobject LocationHelper {    fun getLocationServiceStatus(context: Context):Boolean{        return (context.getSystemService(LOCATION_SERVICE) as LocationManager)            .isProviderEnabled(LocationManager.GPS_PROVIDER)    }    /**     * 打开定位服务设置     */    fun openLocationSetting(context: Context):Boolean{        return try {            val settingsIntent = Intent()            settingsIntent.action = Settings.ACTION_LOCATION_SOURCE_SETTINGS            settingsIntent.addCategory(Intent.CATEGORY_DEFAULT)            settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)            settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)            settingsIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)            context.startActivity(settingsIntent)            true        } catch (ex: java.lang.Exception) {            false        }    }    /**     * 获取当前定位     */    @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])    suspend fun getLocation(context: Activity,timeOut: Long = 2000):Location{        val location = getLocationByFusedLocationProviderClient(context)        //默认使用FusedLocationProviderClient 如果FusedLocationProviderClient不可用或获取失败,则使用LocationManager进行二次获取        Log.d("LocationHelper", "getLocation:$location")        return  if (location.latitude == 0.0){            getLocationByLocationManager(context, timeOut)        }else{            location        }    }    @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])    private suspend fun getLocationByLocationManager(context: Activity,timeOut: Long = 2000):Location{        Log.d("LocationHelper", "getLocationByLocationManager")        val locationManager =  context.getSystemService(LOCATION_SERVICE) as LocationManager        //检查LocationManager是否可用        return  if (LocationManagerUtils.checkLocationManagerAvailable(locationManager)){            //使用LocationManager获取当前经纬度            val location = LocationManagerUtils.getCurrentPosition(locationManager, timeOut)            if (location.latitude == 0.0){                LocationManagerUtils.getLastLocation(locationManager)            }else{                location            }        }else{            //获取失败,则采用默认经纬度            LocationManagerUtils.createDefaultLocation()        }    }    @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])    private suspend fun getLocationByFusedLocationProviderClient(context: Activity):Location{        Log.d("LocationHelper", "getLocationByFusedLocationProviderClient")        //使用FusedLocationProviderClient进行定位        val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)        return if (FusedLocationProviderUtils.checkFusedLocationProviderAvailable(fusedLocationClient)){           withContext(Dispatchers.IO){               //使用FusedLocationProviderClient获取当前经纬度               val location = FusedLocationProviderUtils.getCurrentPosition(fusedLocationClient)               if (location.latitude == 0.0){                   FusedLocationProviderUtils.getLastLocation(fusedLocationClient)               }else{                   location               }           }        }else{            LocationManagerUtils.createDefaultLocation()        }    }}
注:因为获取定位是比较耗电的操作,在实际使用时,可增加缓存机制,比如2分钟之内频繁,则返回上一次缓存的数据,如果超过2分钟则重新获取一次,并缓存起来。

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

1,获取当前经纬度
 @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])  fun getCurrentLocation(activity:Activity){    if (activity != null){      val exceptionHandler = CoroutineExceptionHandler { _, exception ->      }      viewModelScope.launch(exceptionHandler) {        val location = LocationHelper.getLocation(activity!!)        val map = HashMap()        map["latitude"] ="${location.latitude}"        map["longitude"] = "${location.longitude}"      }    }  }
2,经纬度转换地址
 /**     * @param latitude 经度     * @param longitude 纬度     * @return 详细位置信息     */    suspend fun convertAddress(context: Context, latitude: Double, longitude: Double): String {        return try {            withTimeout(3000){                suspendCancellableCoroutine {  continuation ->                    try {                        val mGeocoder = Geocoder(context, Locale.getDefault())                        val mStringBuilder = StringBuilder()                        if (Geocoder.isPresent()){                            val mAddresses = mGeocoder.getFromLocation(latitude, longitude, 1)                            if (mAddresses!= null &&mAddresses.size >0) {                                val address = mAddresses[0]                                Log.d("LocationUtils", "convertAddress()--->$address")                                mStringBuilder.append(address.getAddressLine(0)?:"")                                    .append(",")                                    .append(address.adminArea?:address.subAdminArea?:"")                                    .append(",")                                    .append(address.locality?:address.subLocality?:"")                                    .append(",")                                    .append(address.thoroughfare?:address.subThoroughfare?:"")                            }                        }                        if (continuation.isActive){                            continuation.resume(mStringBuilder.toString())                        }                    } catch (e: IOException) {                        Log.d("LocationUtils", "convertAddress()--IOException->${e.message}")                        if (continuation.isActive){                            continuation.resume("")                        }                    }                }            }        }catch (e:Exception){            Log.d("LocationUtils", "convertAddress()--->timeout")            return ""        }    }

调用时:

fun covertAddress(latitude:double,longitude:double){    if (activity != null){      val exceptionHandler = CoroutineExceptionHandler { _, exception ->      }      viewModelScope.launch(exceptionHandler) {        val hashMap = argument as HashMap        withContext(Dispatchers.IO){          val address = LocationHelper.convertAddress(activity!!,            "${hashMap["latitude"]}".toDouble(),            "${hashMap["longitude"]}".toDouble())        }      }    }  }

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

上一篇:域名后缀怎么来的?

下一篇:揭秘源码背后的故事,开发者的真实吐槽与体验分享