Reputation: 571
For an app I'm making, I need to get the users' location which will then be displayed on the map.
To achieve this, I am using LocationServices.getFusedLocationProviderClient() in the code below.
When I get the location value in addOnSuccessListener, the latitude and longtitude values are null for some reason, so I'm getting a NullPointerException:
import android.Manifest
import android.annotation.SuppressLint
import android.location.Location
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.core.app.ActivityCompat
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.MarkerOptions
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.realtomjoney.mapsapp.databinding.ActivityMainBinding
import java.lang.NullPointerException
class MainActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var binding: ActivityMainBinding
lateinit var myMap: GoogleMap
lateinit var fusedLocation: FusedLocationProviderClient
@SuppressLint("MissingPermission")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
ActivityCompat.requestPermissions(this, arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION) , 100)
val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
binding.button2.setOnClickListener {
fusedLocation = LocationServices.getFusedLocationProviderClient(this)
fusedLocation.lastLocation.addOnSuccessListener { location: Location? ->
val latLong = LatLng(location!!.latitude, location.longitude)
myMap.addMarker(MarkerOptions().position(latLong).title("Found Location"))
myMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLong, 10f))
}
}
}
override fun onMapReady(p0: GoogleMap) {
myMap = p0
}
}
When I run the app, I get the following exception:
2021-10-19 16:46:09.859 8556-8556/com.realtomjoney.mapsapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.realtomjoney.mapsapp, PID: 8556
java.lang.NullPointerException
at com.realtomjoney.mapsapp.MainActivity.onCreate$lambda-1$lambda-0(MainActivity.kt:46)
at com.realtomjoney.mapsapp.MainActivity.$r8$lambda$wwcxhtPyugLtHLn65vCEt3JY33Q(Unknown Source:0)
at com.realtomjoney.mapsapp.MainActivity$$ExternalSyntheticLambda1.onSuccess(Unknown Source:4)
at com.google.android.gms.tasks.zzn.run(com.google.android.gms:play-services-tasks@@17.2.0:4)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7842)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
And yes, I have said yes to all permissions when I ran the app for the first time.
If this question is poorly written or a duplicate, please let me know as I strive to create good and well-structured questions.
Upvotes: 1
Views: 1579
Reputation: 71
It looks like you request the permission(ActivityCompat.requestPermissions(...)) but never handle the permission request(you need to override onRequestPermissionsResult(...)). More info on that: https://developer.android.com/training/permissions/requesting#manage-request-code-yourself
Or you can switch to the new method to allow the system to manage the request code: https://developer.android.com/training/permissions/requesting#allow-system-manage-request-code
One last thing, in some cases getLastLocation() can return null, in that case I call getCurrentLocation() which always returns new fresh device location, more info here: https://developer.android.com/training/location/retrieve-current#BestEstimate
Upvotes: 0
Reputation: 93
I had the same issue
Upvotes: 0
Reputation: 3258
I have faced this isue as well especially when i first install my app on a device. there are two things that you can try out that i think will help:
one is trying to open the google maps app and waiting for it to get a fix and then go on and open your app. this should provide your fused location provider client a last location that it can look up. check this answer for reference
the second is going a step further and requesting location updates using the provider. If you only need a single location fix you can then go on and deregister it once you get the first one. I am doing this for my app and usually last location is only null the first time i use the app after installation. after that it usually works. this is probably what you should do anyway as you cant expect your users to turn on google maps before using your app.
and just to be safe, be aware that fusedlocationproviderclient
sometimes stops working or behaves weirdly when you have pending google services updates on your device. so go ahead and make sure your device is up to date in this regard.
Upvotes: 1
Reputation: 63
First of all, the correct procedure not to get null when initializing user coordinates to map is to initialize the map on onConnected() callback instead of onCreate() in an activity or fragment, lastLocation can be null.
You can use REQUEST_CODE_ASK_PERMISSIONS
to handle the callback if not using location observer, the location observer basically would look something like this
if (location.provider == LocationManager.GPS_PROVIDER) {
getData()
}
I had the same case back in the days, my code was something like this on permission callback :
if (isGranted()) {
try {
fusedLocationProviderClient.lastLocation.addOnCompleteListener {
if (it.isSuccessful) {
if (it.result != null) {
setLocationData(it.result)
}
}
}
} catch (e: Exception) {
}
Basically on Permission granted, it will get your location and you can choose it whether you wanna use it to addMarker or anything else.
Upvotes: 0
Reputation: 63
CMMIW, your code went wrong around here
val latLong = LatLng(location!!.latitude, location.longitude)
myMap.addMarker(MarkerOptions().position(latLong).title("Found Location"))
myMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLong, 10f))
First of all, your location can be null whereas coordinates can't, so make sure to put null safety on coordinates(latitude and longitude).
You can use REQUEST_CODE_ASK_PERMISSIONS
to handle the callback if not using location observer, the location observer basically would look something like this
if (location.provider == LocationManager.GPS_PROVIDER) {
getData()
}
I had the same case back in the days, my code was something like this on permission callback :
if (isGranted()) {
try {
fusedLocationProviderClient.lastLocation.addOnCompleteListener {
if (it.isSuccessful) {
if (it.result != null) {
setLocationData(it.result)
}
}
}
} catch (e: Exception) {
}
Basically on Permission granted, it will get your location and you can choose it whether you wanna use it to addMarker or anything else.
Upvotes: 0
Reputation: 1388
lastLocation can be null for several reasons. They are listed here https://developer.android.com/training/location/retrieve-current#last-known
Upvotes: 0