赞
踩
Android中获取定位信息的方式有很多种,系统自带的LocationManager,以及第三方厂商提供的一些定位sdk,都能帮助我们获取当前经纬度,但第三方厂商一般都需要申请相关的key,且调用量高时,还会产生资费问题。这里采用LocationManager + FusedLocationProviderClient 的方式进行经纬度的获取,以解决普通场景下获取经纬度和经纬度转换地址的功能。
- <!--允许获取精确位置,精准定位必选-->
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
-
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- <!--后台获取位置信息,若需后台定位则必选-->
- <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
- <!--用于申请调用A-GPS模块,卫星定位加速-->
- <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
- 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'
获取经纬度时,可根据自己的诉求进行参数自定义,如果对经纬度要求不是很精确的可以自行配置Criteria里面的参数。
获取定位前需要先判断相关的服务是否可用,获取定位的服务其实有很多种选择,因为个人项目对经纬度准确性要求较高,为了保证获取的成功率和准确性,只使用了GPS和网络定位两种,如果在国内还会有基站获取等方式,可以自行修改。
- import android.Manifest.permission
- import android.location.*
- import android.os.Bundle
- import android.util.Log
- import androidx.annotation.RequiresPermission
- import kotlinx.coroutines.suspendCancellableCoroutine
- import kotlinx.coroutines.withTimeout
- import kotlin.coroutines.resume
-
-
- object 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)
- }
- }
在获取经纬度时会出现各种异常的场景,会导致成功的回调一直无法触发,这里使用了协程,如果超过指定超时时间未返回,则直接默认为获取失败,进行下一步的处理。
- import android.Manifest
- import android.app.Activity
- import android.content.Context
- import android.content.Context.LOCATION_SERVICE
- import android.content.Intent
- import android.location.Geocoder
- import android.location.Location
- import android.location.LocationManager
- import android.provider.Settings
- import android.util.Log
- import androidx.annotation.RequiresPermission
- import com.google.android.gms.location.LocationServices
- import kotlinx.coroutines.Dispatchers
- import kotlinx.coroutines.suspendCancellableCoroutine
- import kotlinx.coroutines.withContext
- import kotlinx.coroutines.withTimeout
- import java.io.IOException
- import java.util.*
- import kotlin.coroutines.resume
-
- object 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
- }
- }
在获取定位时,可能会出现GPS定位未开启的情况,所以不管是LocationManager或FusedLocationProviderClient都需要判断当前服务是否可用,获取定位时,如果GPS信号较弱等异常情况下,就需要考虑到获取定位超时的情况,这里使用了协程,如FusedLocationProviderClient超过3秒未获取成功,则直接切换到LocationManager进行二次获取,这是提升获取经纬度成功的关键。
在实际项目中,如果对获取经纬度有较高的考核要求时,通过结合LocationManager和FusedLocationProviderClient如果还是获取不到,可考虑集成第三方的进行进一步获取,可以考虑使用华为的免费融合定位服务,因为我们使用过百度地图的sdk,每天会出现千万分之五左右的定位错误和定位漂移问题。
- import android.Manifest
- import android.app.Activity
- import android.content.Context
- import android.content.Context.LOCATION_SERVICE
- import android.content.Intent
- import android.location.Geocoder
- import android.location.Location
- import android.location.LocationManager
- import android.provider.Settings
- import android.util.Log
- import androidx.annotation.RequiresPermission
- import com.google.android.gms.location.LocationServices
- import kotlinx.coroutines.Dispatchers
- import kotlinx.coroutines.suspendCancellableCoroutine
- import kotlinx.coroutines.withContext
- import kotlinx.coroutines.withTimeout
- import java.io.IOException
- import java.util.*
- import kotlin.coroutines.resume
-
-
- object 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()
- }
- }
- }
- @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<String,String>()
- map["latitude"] ="${location.latitude}"
- map["longitude"] = "${location.longitude}"
- }
- }
- }
- /**
- * @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())
- }
- }
- }
- }
注:经纬度转换地址时,需要开启一个线程或者协程进行转换,不然会阻塞主线程,引发异常。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。