使用GetFusedLocationProviderClient获取用户位置时经纬度返回空值引发NullPointerException问题排查
Hey there, let's break down why you're hitting that NullPointerException and walk through how to fix it properly. The crash happens because you're using location!! to force a non-null value, but lastLocation is returning null—and there are a few common reasons for that:
- Your device's location services might be turned off
- The device has never recorded a location before (like a new device or after a reset)
- The cached location data was cleared by the system
- You're trying to access location before the user actually grants the requested permissions
Let's go through the fixes one by one:
1. Fix Permission Handling First
Right now, you request permissions in onCreate, but if the user taps the button before allowing permissions, you'll still try to fetch location without proper access. Let's add a check to ensure permissions are granted before proceeding:
binding.button2.setOnClickListener { // Check if permissions are granted first val hasFineLocation = ActivityCompat.checkSelfPermission( this, Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED val hasCoarseLocation = ActivityCompat.checkSelfPermission( this, Manifest.permission.ACCESS_COARSE_LOCATION ) == PackageManager.PERMISSION_GRANTED if (!hasFineLocation && !hasCoarseLocation) { // Re-request permissions if they're missing ActivityCompat.requestPermissions( this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION), 100 ) return@setOnClickListener } // Proceed with location fetch only if permissions are granted fusedLocation = LocationServices.getFusedLocationProviderClient(this) fusedLocation.lastLocation.addOnSuccessListener { location: Location? -> // Replace !! with a safe null check location?.let { validLocation -> val latLong = LatLng(validLocation.latitude, validLocation.longitude) myMap.addMarker(MarkerOptions().position(latLong).title("Found Location")) myMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLong, 10f)) } ?: run { // Handle null location case with a user-friendly dialog MaterialAlertDialogBuilder(this) .setTitle("Location Unavailable") .setMessage("We couldn't fetch your current location. Please check if location services are enabled, or try moving around to refresh your location.") .setPositiveButton("Got it") { dialog, _ -> dialog.dismiss() } .show() } } }
2. Don't Rely on lastLocation Alone
lastLocation only returns cached location data, which can be null. For reliable location data, use location updates to fetch a fresh location when the cached one is missing:
First, add the necessary variables and initialization in onCreate:
import com.google.android.gms.location.LocationCallback import com.google.android.gms.location.LocationRequest import com.google.android.gms.location.LocationResult import android.content.Context import android.content.Intent import android.provider.Settings private lateinit var locationRequest: LocationRequest private lateinit var locationCallback: LocationCallback override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) // Initialize location request for real-time updates locationRequest = LocationRequest.create().apply { interval = 10000 // Update every 10 seconds fastestInterval = 5000 // Fastest update interval (5 seconds) priority = LocationRequest.PRIORITY_HIGH_ACCURACY // Use high accuracy for best results } // Define callback to handle location updates locationCallback = object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult) { super.onLocationResult(locationResult) locationResult.lastLocation?.let { newLocation -> val latLong = LatLng(newLocation.latitude, newLocation.longitude) myMap.addMarker(MarkerOptions().position(latLong).title("Found Location")) myMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLong, 10f)) // Stop updates once we get a valid location fusedLocation.removeLocationUpdates(locationCallback) } } } // Rest of your onCreate code... }
Then update the button click listener to fall back to location updates when lastLocation is null:
binding.button2.setOnClickListener { // First check if location services are enabled if (!isLocationEnabled()) { MaterialAlertDialogBuilder(this) .setTitle("Location Services Disabled") .setMessage("Please enable location services to fetch your location.") .setPositiveButton("Go to Settings") { _, _ -> startActivity(Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)) } .setNegativeButton("Cancel") { dialog, _ -> dialog.dismiss() } .show() return@setOnClickListener } // Check permissions as before val hasFineLocation = ActivityCompat.checkSelfPermission( this, Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED val hasCoarseLocation = ActivityCompat.checkSelfPermission( this, Manifest.permission.ACCESS_COARSE_LOCATION ) == PackageManager.PERMISSION_GRANTED if (!hasFineLocation && !hasCoarseLocation) { ActivityCompat.requestPermissions( this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION), 100 ) return@setOnClickListener } fusedLocation = LocationServices.getFusedLocationProviderClient(this) fusedLocation.lastLocation.addOnSuccessListener { location: Location? -> location?.let { validLocation -> val latLong = LatLng(validLocation.latitude, validLocation.longitude) myMap.addMarker(MarkerOptions().position(latLong).title("Found Location")) myMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLong, 10f)) } ?: run { // No cached location—request real-time updates MaterialAlertDialogBuilder(this) .setTitle("Fetching Location") .setMessage("We're getting your current location now...") .setCancelable(false) .show() fusedLocation.requestLocationUpdates( locationRequest, locationCallback, Looper.getMainLooper() ) } } } // Helper method to check if location services are enabled private fun isLocationEnabled(): Boolean { val locationManager = getSystemService(Context.LOCATION_SERVICE) as android.location.LocationManager return locationManager.isProviderEnabled(android.location.LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(android.location.LocationManager.NETWORK_PROVIDER) }
3. Key Takeaways
- Never use
!!with nullable location data—always handle null cases gracefully lastLocationis unreliable for fresh or first-time location data; use location updates as a fallback- Always verify permissions and location service status before attempting to fetch location
- Add user-friendly feedback for edge cases (like disabled location services or missing location data)
内容的提问来源于stack exchange,提问作者thebluepandabear

