There are many ways to obtain positioning information in Android. The system's own LocationManager and some positioning SDKs provided by third-party manufacturers can help us obtain the current latitude and longitude, but third-party manufacturers generally need to apply for relevant keys, and when the call volume is high, there will also be tariff problems. Here, the LocationManager + FusedLocationProviderClient is used to obtain latitude and longitude to solve the function of obtaining latitude and longitude and longitude conversion addresses in ordinary scenarios.
1. Add positioning permissions
<!--Allows to obtain exact locations,A must-have for precise positioning--> <uses-permission android:name=".ACCESS_FINE_LOCATION" /> <uses-permission android:name=".ACCESS_COARSE_LOCATION" /> <!--Get location information in the background,If you need backend positioning, you must choose--> <uses-permission android:name=".ACCESS_BACKGROUND_LOCATION" /> <!--Used to apply for callsA-GPSModule,Satellite positioning acceleration--> <uses-permission android:name=".ACCESS_LOCATION_EXTRA_COMMANDS" />
2. Add dependency library
implementation ':kotlinx-coroutines-core:1.6.4' implementation ':lifecycle-viewmodel-ktx:2.6.2' implementation ':play-services-location:21.0.1'
3. Use LocationManager to obtain the current latitude and longitude
When obtaining latitude and longitude, you can customize parameters according to your own demands. If the latitude and longitude requirements are not very accurate, you can configure the parameters in Criteria by yourself.
Before obtaining positioning, you need to determine whether the relevant services are available. There are actually many options for obtaining positioning services, because individual projects have high requirements for latitude and longitude accuracy. In order to ensure the success rate and accuracy of acquisition, only two types of GPS and network positioning are used. If there are base station acquisition methods in China, you can modify them yourself.
import import .* import import import import import import object LocationManagerUtils { val TAG = "LocationManagerUtils" /** * @mLocationManager Pass in the LocationManager object * @minDistance Minimum location change distance: When the location distance changes more than this value, the location information (unit: meters) will be updated * @timeOut Timeout time, if the timeout does not return, the default value will be used directly */ @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 { // If the timeout does not return, the acquisition will fail directly and return the default value withTimeout(timeOut){ suspendCancellableCoroutine {continuation -> //Get the best positioning method. If it cannot be obtained, network positioning is used by default. var bestProvider = (createCriteria(),true) if (()||bestProvider == "passive"){ bestProvider = "network" } (TAG, "getCurrentPosition:bestProvider:${bestProvider}") locationListener = object : LocationListener { override fun onLocationChanged(location: Location) { (TAG, "getCurrentPosition:onCompete:${},${}") if (){ (location) (this) } } override fun onProviderDisabled(provider: String) { } override fun onProviderEnabled(provider: String) { } } //Start positioning (bestProvider, 1000,0f, locationListener!!) } } }catch (e:Exception){ try { locationListener?.let { (it) } }catch (e:Exception){ (TAG, "getCurrentPosition:removeUpdate:${}") } //Timeout directly returns the default empty object (TAG, "getCurrentPosition:onError:${}") return createDefaultLocation() } } @RequiresPermission(anyOf = [permission.ACCESS_COARSE_LOCATION, permission.ACCESS_FINE_LOCATION]) suspend fun repeatLocation(mLocationManager: LocationManager):Location{ return suspendCancellableCoroutine {continuation -> //Get the best positioning method. If it cannot be obtained, network positioning is used by default. var bestProvider = (createCriteria(),true) if (()||bestProvider == "passive"){ bestProvider = "network" } (TAG, "getCurrentPosition:bestProvider:${bestProvider}") val locationListener = object : LocationListener { override fun onLocationChanged(location: Location) { (TAG, "getCurrentPosition:onCompete:${},${}") if (){ (location) } (this) } override fun onProviderDisabled(provider: String) { } override fun onProviderEnabled(provider: String) { } } //Start positioning (bestProvider,1000, 0f, locationListener) } } @RequiresPermission(anyOf = [permission.ACCESS_COARSE_LOCATION, permission.ACCESS_FINE_LOCATION]) fun getLastLocation( mLocationManager: LocationManager): Location { //Get the best positioning method. If it cannot be obtained, network positioning is used by default. var currentProvider = (createCriteria(), true) if (()||currentProvider == "passive"){ currentProvider = "network" } return (currentProvider) ?: createDefaultLocation() } //Create the default value of positioning fun createDefaultLocation():Location{ val location = Location("network") = 0.0 = 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 } } ///Is the positioning available fun checkLocationManagerAvailable(mLocationManager: LocationManager):Boolean{ return (LocationManager.NETWORK_PROVIDER)|| (LocationManager.GPS_PROVIDER) } }
4. Use FusedLocationProviderClient
Various abnormal scenarios will appear when obtaining latitude and longitude, which will cause successful callbacks to fail to trigger. Coroutines are used here. If the specified timeout is exceeded and the specified timeout is not returned, the default is to obtain failure and the next step is processed.
import import import import .LOCATION_SERVICE import import import import import import import import import import import import import import .* import object FusedLocationProviderUtils { val TAG = "FusedLocationUtils" @RequiresPermission(anyOf = [".ACCESS_COARSE_LOCATION", ".ACCESS_FINE_LOCATION"]) suspend fun checkFusedLocationProviderAvailable(fusedLocationClient: FusedLocationProviderClient):Boolean{ return try { withTimeout(1000){ suspendCancellableCoroutine { continuation -> { (TAG, "locationAvailability:addOnFailureListener:${}") if (){ (false) } }.addOnSuccessListener { (TAG, "locationAvailability:addOnSuccessListener:${}") if (){ () } } } } }catch (e:Exception){ return false } } ///Get the last known location information @RequiresPermission(anyOf = [".ACCESS_COARSE_LOCATION", ".ACCESS_FINE_LOCATION"]) suspend fun getLastLocation(fusedLocationClient: FusedLocationProviderClient):Location{ return suspendCancellableCoroutine {continuation -> { if (){ (TAG, "current location success:$it") if (it != null){ (it) }else{ (createDefaultLocation()) } } }.addOnFailureListener { (createDefaultLocation()) } } } /** * To obtain the current positioning, you need to apply for positioning permissions * */ @RequiresPermission(anyOf = [".ACCESS_COARSE_LOCATION", ".ACCESS_FINE_LOCATION"]) suspend fun getCurrentPosition(fusedLocationClient: FusedLocationProviderClient): Location { return suspendCancellableCoroutine {continuation -> (createLocationRequest(),object : CancellationToken(){ override fun onCanceledRequested(p0: OnTokenCanceledListener): CancellationToken { return CancellationTokenSource().token } override fun isCancellationRequested(): Boolean { return false } }).addOnSuccessListener { if (){ (TAG, "current location success:$it") if (it != null){ (it) }else{ (createDefaultLocation()) } } }.addOnFailureListener { (TAG, "current location fail:$it") if (){ (createDefaultLocation()) } }.addOnCanceledListener { (TAG, "current location cancel:") if (){ (createDefaultLocation()) } } } } //Create the current LocationRequest object private fun createLocationRequest():CurrentLocationRequest{ return () .setDurationMillis(1000) .setMaxUpdateAgeMillis(5000) .setPriority(Priority.PRIORITY_HIGH_ACCURACY) .build() } //Create default value private fun createDefaultLocation():Location{ val location = Location("network") = 0.0 = 0.0 return location } }
5. Integrate LocationManager and FusedLocationProviderClient
When acquiring positioning, GPS positioning may not be enabled. Therefore, whether it is the LocationManager or FusedLocationProviderClient, it is necessary to determine whether the current service is available. When acquiring positioning, if the GPS signal is weak or other abnormal situations, it is necessary to consider the acquisition positioning timeout. A coroutine is used here. If the FusedLocationProviderClient fails to obtain more than 3 seconds, it is directly switched to the LocationManager for secondary acquisition, which is the key to improving the success of obtaining latitude and longitude.
In actual projects, if there are high assessment requirements for obtaining latitude and longitude, if the LocationManager and FusedLocationProviderClient are still not available, you can consider integrating third parties for further acquisition. You can consider using Huawei'sFree integrated positioning service, because we have used Baidu Map's SDK, about 5/10 million of positioning errors and positioning drift problems will occur every day.
import import import import .LOCATION_SERVICE import import import import import import import import import import import import import import .* import object LocationHelper { fun getLocationServiceStatus(context: Context):Boolean{ return ((LOCATION_SERVICE) as LocationManager) .isProviderEnabled(LocationManager.GPS_PROVIDER) } /** * Open Location Service Settings */ fun openLocationSetting(context: Context):Boolean{ return try { val settingsIntent = Intent() = Settings.ACTION_LOCATION_SOURCE_SETTINGS (Intent.CATEGORY_DEFAULT) (Intent.FLAG_ACTIVITY_NEW_TASK) (Intent.FLAG_ACTIVITY_NO_HISTORY) (Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) (settingsIntent) true } catch (ex: ) { false } } /** * Get the current location */ @RequiresPermission(anyOf = [.ACCESS_COARSE_LOCATION, .ACCESS_FINE_LOCATION]) suspend fun getLocation(context: Activity,timeOut: Long = 2000):Location{ val location = getLocationByFusedLocationProviderClient(context) //Default is used to use FusedLocationProviderClient. If FusedLocationProviderClient is unavailable or the acquisition fails, use LocationManager for secondary acquisition ("LocationHelper", "getLocation:$location") return if ( == 0.0){ getLocationByLocationManager(context, timeOut) }else{ location } } @RequiresPermission(anyOf = [.ACCESS_COARSE_LOCATION, .ACCESS_FINE_LOCATION]) private suspend fun getLocationByLocationManager(context: Activity,timeOut: Long = 2000):Location{ ("LocationHelper", "getLocationByLocationManager") val locationManager = (LOCATION_SERVICE) as LocationManager //Check whether LocationManager is available return if ((locationManager)){ //Use LocationManager to get the current latitude and longitude val location = (locationManager, timeOut) if ( == 0.0){ (locationManager) }else{ location } }else{ //Finished, the default latitude and longitude will be used () } } @RequiresPermission(anyOf = [.ACCESS_COARSE_LOCATION, .ACCESS_FINE_LOCATION]) private suspend fun getLocationByFusedLocationProviderClient(context: Activity):Location{ ("LocationHelper", "getLocationByFusedLocationProviderClient") //Use FusedLocationProviderClient for positioning val fusedLocationClient = (context) return if ((fusedLocationClient)){ withContext(){ //Use FusedLocationProviderClient to get the current latitude and longitude val location = (fusedLocationClient) if ( == 0.0){ (fusedLocationClient) }else{ location } } }else{ () } } }
Note: Because acquiring positioning is a relatively power-consuming operation, a cache mechanism can be added when actually used. For example, frequent within 2 minutes, the data cached will be returned. If it exceeds 2 minutes, it will be re-acquisitioned and cached.
6. Get the current latitude and longitude information or latitude and longitude translation address
1. Get the current latitude and longitude
@RequiresPermission(anyOf = [.ACCESS_COARSE_LOCATION, .ACCESS_FINE_LOCATION]) fun getCurrentLocation(activity:Activity){ if (activity != null){ val exceptionHandler = CoroutineExceptionHandler { _, exception -> } (exceptionHandler) { val location = (activity!!) val map = HashMap<String,String>() map["latitude"] ="${}" map["longitude"] = "${}" } } }
2. Latitude and longitude conversion address
/** * @param latitude longitude * @param longitude Latitude * @return Detailed location information */ suspend fun convertAddress(context: Context, latitude: Double, longitude: Double): String { return try { withTimeout(3000){ suspendCancellableCoroutine { continuation -> try { val mGeocoder = Geocoder(context, ()) val mStringBuilder = StringBuilder() if (()){ val mAddresses = (latitude, longitude, 1) if (mAddresses!= null && >0) { val address = mAddresses[0] ("LocationUtils", "convertAddress()--->$address") ((0)?:"") .append(",") .append(?:?:"") .append(",") .append(?:?:"") .append(",") .append(?:?:"") } } if (){ (()) } } catch (e: IOException) { ("LocationUtils", "convertAddress()--IOException->${}") if (){ ("") } } } } }catch (e:Exception){ ("LocationUtils", "convertAddress()--->timeout") return "" } }
When called:
fun covertAddress(latitude:double,longitude:double){ if (activity != null){ val exceptionHandler = CoroutineExceptionHandler { _, exception -> } (exceptionHandler) { val hashMap = argument as HashMap<*, *> withContext(){ val address = (activity!!, "${hashMap["latitude"]}".toDouble(), "${hashMap["longitude"]}".toDouble()) } } } }
Note: When converting the address of the latitude and longitude, you need to open a thread or coroutine for conversion, otherwise it will block the main thread and raise an exception.
This is the article about the best way to obtain latitude and longitude of Android. For more relevant content on Android to obtain latitude and longitude, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!