本文详解如何避免因过早调用依赖定位权限的方法(如 getLastKnownLocation())导致应用崩溃,通过合理设计异步流程、延迟地图初始化,并统一入口点确保 currentLocation 和 GoogleMap 在就绪后才协同工作。
本文详解如何避免因过早调用依赖定位权限的方法(如 `getlastknownlocation()`)导致应用崩溃,通过合理设计异步流程、延迟地图初始化,并统一入口点确保 `currentlocation` 和 `googlemap` 在就绪后才协同工作。
在 Android 开发中,位置权限请求(ActivityCompat.requestPermissions)和 FusedLocationProviderClient.getLastLocation() 均为异步操作——它们不会阻塞主线程,也不会立即返回结果。而你的 onCreate() 方法却在未等待权限授予或定位获取完成的情况下,直接调用 getLastKnownLocation() 并尝试初始化地图,这正是应用首次启动或 GPS 关闭时崩溃的根本原因:currentLocation 为 null,后续调用 currentLocation.getLatitude() 触发 NullPointerException。
核心原则是:所有依赖位置数据与地图就绪的操作,必须在两者均可用后才执行。不应在 onCreate() 中提前调用 getLastKnownLocation() 或 getMapAsync() 相关逻辑,而应将其封装为可复用的初始化方法,并仅在安全时机触发。
将原本分散在 onCreate() 和 OnSuccessListener 中的地图准备代码,统一提取为 initMapAndLocation() 方法:
private void initMapAndLocation() { // 确保 Fragment 已存在且 Map 尚未加载 if (suppMapFragment == null) { suppMapFragment = (SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.maps); } if (suppMapFragment != null && mMap == null) { suppMapFragment.getMapAsync(this); }}
修改后的关键方法如下:
private void fetchLastLocation() { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CODE); return; // ✅ 立即返回,不继续执行 } Task<Location> task = client.getLastLocation(); task.addOnSuccessListener(location -> { if (location != null) { currentLocation = location; initMapAndLocation(); // ✅ 位置就绪 → 启动初始化 } else { // 可选:回退到 getLastKnownLocation() 或提示用户手动触发 currentLocation = getLastKnownLocation(); initMapAndLocation(); } }).addOnFailureListener(e -> { Log.e("MainActivity", "Location fetch failed", e); currentLocation = getLastKnownLocation(); initMapAndLocation(); });}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_CODE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { fetchLastLocation(); // ✅ 授权后重新尝试获取位置 } else { Toast.makeText(this, "Location permission denied. Map may not show your position.", Toast.LENGTH_LONG).show(); // 即使无权限,仍可初始化地图(仅禁用 my-location 功能) initMapAndLocation(); } }}
此时 currentLocation 已由前述流程保障非空(或有合理 fallback),onMapReady() 仅聚焦地图配置:
@Overridepublic void onMapReady(@NonNull GoogleMap googleMap) { this.mMap = googleMap; // ✅ 安全使用 currentLocation —— 它已在 initMapAndLocation() 前被赋值 LatLng latLng = currentLocation != null ? new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude()) : new LatLng(0.0, 0.0); // fallback center MarkerOptions markerOptions = new MarkerOptions() .position(latLng) .title("You are here!"); googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 12)); googleMap.addMarker(markerOptions); googleMap.getUiSettings().setMyLocationButtonEnabled(true); if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { googleMap.setMyLocationEnabled(true); }}
通过将“位置获取 → 地图初始化”解耦为受控的异步链路,并以单一入口(initMapAndLocation())协调执行,即可彻底规避竞态问题,提升应用稳定性与用户体验。