In 2019 Best Offical Solution in Kotlin
Google API Client/FusedLocationApi are deprecated and Location Manager is not useful at all. So Google prefer Fused Location Provider Using the Google Play services location APIs "FusedLocationProviderClient" is used to get location and its better way for battery saving and accuracy
Here is sample code in kotlin to get the last known location /one-time location( equivalent to the current location)
// declare a global variable of FusedLocationProviderClient
private lateinit var fusedLocationClient: FusedLocationProviderClient
// in onCreate() initialize FusedLocationProviderClient
fusedLocationClient = LocationServices.getFusedLocationProviderClient(context!!)
/**
* call this method for receive location
* get location and give callback when successfully retrieve
* function itself check location permission before access related methods
*
*/
fun getLastKnownLocation() {
fusedLocationClient.lastLocation
.addOnSuccessListener { location->
if (location != null) {
// use your location object
// get latitude , longitude and other info from this
}
}
}
If your app can continuously track the location then you have to receive Receive location updates
Check the sample for that in kotlin
// declare a global variable FusedLocationProviderClient
private lateinit var fusedLocationClient: FusedLocationProviderClient
// in onCreate() initialize FusedLocationProviderClient
fusedLocationClient = LocationServices.getFusedLocationProviderClient(context!!)
// globally declare LocationRequest
private lateinit var locationRequest: LocationRequest
// globally declare LocationCallback
private lateinit var locationCallback: LocationCallback
/**
* call this method in onCreate
* onLocationResult call when location is changed
*/
private fun getLocationUpdates()
{
fusedLocationClient = LocationServices.getFusedLocationProviderClient(context!!)
locationRequest = LocationRequest()
locationRequest.interval = 50000
locationRequest.fastestInterval = 50000
locationRequest.smallestDisplacement = 170f // 170 m = 0.1 mile
locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY //set according to your app function
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
locationResult ?: return
if (locationResult.locations.isNotEmpty()) {
// get latest location
val location =
locationResult.lastLocation
// use your location object
// get latitude , longitude and other info from this
}
}
}
}
//start location updates
private fun startLocationUpdates() {
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
null /* Looper */
)
}
// stop location updates
private fun stopLocationUpdates() {
fusedLocationClient.removeLocationUpdates(locationCallback)
}
// stop receiving location update when activity not visible/foreground
override fun onPause() {
super.onPause()
stopLocationUpdates()
}
// start receiving location update when activity visible/foreground
override fun onResume() {
super.onResume()
startLocationUpdates()
}
Make sure you take care about Mainfaist permission and runtime permission for location
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
and for Gradle add this
implementation 'com.google.android.gms:play-services-location:17.0.0'
For more details follow these official documents
https://developer.android.com/training/location/retrieve-current
https://developer.android.com/training/location/receive-location-updates
https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderClient
Answer from Mayank Sharma on Stack OverflowIn 2019 Best Offical Solution in Kotlin
Google API Client/FusedLocationApi are deprecated and Location Manager is not useful at all. So Google prefer Fused Location Provider Using the Google Play services location APIs "FusedLocationProviderClient" is used to get location and its better way for battery saving and accuracy
Here is sample code in kotlin to get the last known location /one-time location( equivalent to the current location)
// declare a global variable of FusedLocationProviderClient
private lateinit var fusedLocationClient: FusedLocationProviderClient
// in onCreate() initialize FusedLocationProviderClient
fusedLocationClient = LocationServices.getFusedLocationProviderClient(context!!)
/**
* call this method for receive location
* get location and give callback when successfully retrieve
* function itself check location permission before access related methods
*
*/
fun getLastKnownLocation() {
fusedLocationClient.lastLocation
.addOnSuccessListener { location->
if (location != null) {
// use your location object
// get latitude , longitude and other info from this
}
}
}
If your app can continuously track the location then you have to receive Receive location updates
Check the sample for that in kotlin
// declare a global variable FusedLocationProviderClient
private lateinit var fusedLocationClient: FusedLocationProviderClient
// in onCreate() initialize FusedLocationProviderClient
fusedLocationClient = LocationServices.getFusedLocationProviderClient(context!!)
// globally declare LocationRequest
private lateinit var locationRequest: LocationRequest
// globally declare LocationCallback
private lateinit var locationCallback: LocationCallback
/**
* call this method in onCreate
* onLocationResult call when location is changed
*/
private fun getLocationUpdates()
{
fusedLocationClient = LocationServices.getFusedLocationProviderClient(context!!)
locationRequest = LocationRequest()
locationRequest.interval = 50000
locationRequest.fastestInterval = 50000
locationRequest.smallestDisplacement = 170f // 170 m = 0.1 mile
locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY //set according to your app function
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
locationResult ?: return
if (locationResult.locations.isNotEmpty()) {
// get latest location
val location =
locationResult.lastLocation
// use your location object
// get latitude , longitude and other info from this
}
}
}
}
//start location updates
private fun startLocationUpdates() {
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
null /* Looper */
)
}
// stop location updates
private fun stopLocationUpdates() {
fusedLocationClient.removeLocationUpdates(locationCallback)
}
// stop receiving location update when activity not visible/foreground
override fun onPause() {
super.onPause()
stopLocationUpdates()
}
// start receiving location update when activity visible/foreground
override fun onResume() {
super.onResume()
startLocationUpdates()
}
Make sure you take care about Mainfaist permission and runtime permission for location
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
and for Gradle add this
implementation 'com.google.android.gms:play-services-location:17.0.0'
For more details follow these official documents
https://developer.android.com/training/location/retrieve-current
https://developer.android.com/training/location/receive-location-updates
https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderClient
When GetUserLocation returns, locationManager goes out of scope and presumably is destroyed, preventing onLocationChanged from being called and providing updates.
Also, you've defined mylocation inside of GetUserLocation so it also goes out of scope and further kills any chance or your getting an update.
You have not shown where and how the outer mylocation is declared (outside of GetUserLocation), but how ever it is declared, it is being shadowed by the one inside of GetUserLocation. So you aren't getting much.
Here is an example of how you might do it. (The variable thetext is defined within the layout xml and accessed with Kotlin extensions.)
// in the android manifest
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
// allow these through Appliation Manager if necessary
// inside a basic activity
private var locationManager : LocationManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
// Create persistent LocationManager reference
locationManager = getSystemService(LOCATION_SERVICE) as LocationManager?
fab.setOnClickListener { view ->
try {
// Request location updates
locationManager?.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0L, 0f, locationListener)
} catch(ex: SecurityException) {
Log.d("myTag", "Security Exception, no location available")
}
}
}
//define the listener
private val locationListener: LocationListener = object : LocationListener {
override fun onLocationChanged(location: Location) {
thetext.text = ("" + location.longitude + ":" + location.latitude)
}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
override fun onProviderEnabled(provider: String) {}
override fun onProviderDisabled(provider: String) {}
}
So here's how you can get your current location. I am skipping the permission part as you can do that yourself.
private fun isLocationEnabled(): Boolean {
val locationManager: LocationManager =
getSystemService(Context.LOCATION_SERVICE) as LocationManager
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(
LocationManager.NETWORK_PROVIDER
)
}
private fun getLastLocation() {
if (isLocationEnabled()) {
fusedLocationClient.lastLocation.addOnCompleteListener(this) { task ->
val location: Location? = task.result
if (location != null) {
//use the location latitude and logitude as per your use.
val latitude = location.latitude
val longitude = location.longitude
}
}
}
you need to request location updates try the following for instance
val fusedLocationProviderClient =
LocationServices.getFusedLocationProviderClient(activity)
val locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
val location = locationResult.lastLocation
if (location != null) {
val latitude = location.latitude
val longitude = location.longitude
// Use latitude and longitude for your purposes
Log.d("Location", "Latitude: $latitude, Longitude: $longitude")
// Remove location updates after receiving one
fusedLocationProviderClient.removeLocationUpdates(this)
} else {
Log.w("Location", "Failed to get location update")
}
}
}
if (ContextCompat.checkSelfPermission(
activity,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
// Request permissions if not granted
val permissions = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
activity.requestPermissions(permissions, LOCATION_PERMISSION_CODE)
return
}
val locationRequest =
LocationRequest.Builder(0L).setPriority(Priority.PRIORITY_HIGH_ACCURACY).build()
fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, null)
Videos
The last comment (Jan 28) makes this question clear enough to answer:
- A lat,lng location is already available, that is the starting point.
- The goal is to obtain the the... a street address.
- Currently this is done using
geocoder.getFromLocation(latitute,longitude,1) - The problem is that this method is unstable and does not work sometimes.
- The question is then: Is there anything else that I can use?
And the answer is: yes, with caveats.
First, please note that geocoder.getFromLocation() is provided by Android, for free but without guarantees:
Warning: Geocoding services may provide no guarantees on availability or accuracy. Results are a best guess, and are not guaranteed to be meaningful or correct. Do not use this API for any safety-critical or regulatory compliance purpose.
Since your concern seems to be the reliability of this service, a better solution may be provided by the Google Maps Platform Geocoding API, which does offer certain guarantees of latency and availability, as it is one of the Google Maps Platform Core Services.
The first caveat of this solution is that it is not entirely free of charge, see here How the Geocoding API is billed and consider what the cost would be for based on your user base and how often a latlng needs to be converted to an address. Make sure to restrict your API keys and optimize your usage.
The second caveat, which you might even like, is that this API doesn't return just one address, it tends to return a few. You can try this out using the Geocoder Tool at https://developers.google.com/maps/documentation/utils/geocoder (click on the map and it will reverse geocode that point).
Take a look at simple and step by step Google code lab:
https://codelabs.developers.google.com/codelabs/while-in-use-location/#1
You can also use and modify their complete template where is available on GitHub