Android Marshmallow+ Google Maps我的位置层首次启动不显示问题
问题根因
首次启动不显示定位图层、重启后恢复正常,是典型的时序问题,两个点叠加导致故障:
SupportMapFragment在你调用findFragmentById的时候就已经开始异步加载地图,不会等权限申请流程走完。用户还在看权限弹窗的时候,地图就可能已经加载完成触发onMapReady回调,此时权限未授予,你在回调里调用setMyLocationEnabled(true)会直接触发安全异常,被你加的@SuppressWarnings("all")全量压制吞掉报错,定位图层根本没启用成功。- 用户点击同意授权后,你虽然再次调用了
getMapAsync,但已经初始化完成的GoogleMap实例在不少SDK版本下不会重复走完整的onMapReady初始化逻辑,之前因为权限不足没开成的定位图层不会自动重试,自然看不到。重启应用时权限已经是预授予状态,onMapReady触发时权限检查直接通过,图层就能正常加载。
另外你把INTERNET这种安装时自动授予的普通权限加到运行时申请列表里,属于多余操作,不需要动态申请。
修复方案
按以下步骤调整代码即可:
- 把
INTERNET权限从运行时申请的权限数组中移除,仅在Manifest中声明即可。 - 新增独立的定位图层启用方法,不要把图层启用逻辑完全绑定在
onMapReady回调里,不管是地图加载完成还是权限申请通过,只要条件满足就调用该方法检查并启用图层。 - 权限申请通过后,先判断GoogleMap实例是否已经存在,如果存在直接尝试启用图层,不用等重复的
onMapReady回调。 - 所有调用位置敏感API的位置都补上显式权限检查,不要靠注解压制吞掉异常。
- 补全位置获取的空判断,避免特殊情况下
getLastLocation返回null触发空指针。
关键修改代码
首先调整initData中的权限数组:
private void initData() { // 移除不需要动态申请的INTERNET权限 PERMISSIONS = new String[]{ Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, }; googleMap = null; currentLocation = null; mapFragment = (SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map); mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this); }
新增独立的图层启用方法:
private void enableMyLocationLayer() { // 先检查权限,权限不足直接返回 if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return; } if (googleMap == null) { // 地图未就绪,等onMapReady触发后会再次调用该方法 return; } // 权限、地图实例都就绪后再开启图层 googleMap.setMyLocationEnabled(true); // 图层开启后再获取当前位置、缩放视角 getLastLocation(); }
调整onMapReady逻辑,不要直接硬编码开启图层:
@Override public void onMapReady(@NonNull GoogleMap googleMap) { Toast.makeText(this, "Start OnMapReady", Toast.LENGTH_LONG).show(); this.googleMap = googleMap; this.googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL); this.googleMap.setOnMyLocationButtonClickListener(this); this.googleMap.setOnMyLocationClickListener(this); this.googleMap.getUiSettings().setZoomControlsEnabled(true); this.googleMap.setOnMapClickListener(latLng -> { Toast.makeText(MainActivity.this, "Click on " + latLng, Toast.LENGTH_SHORT).show(); this.googleMap.addMarker(new MarkerOptions().position(latLng).icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE))); }); // 统一走图层启用方法 enableMyLocationLayer(); }
调整权限申请回调,权限通过后直接尝试启用图层:
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { boolean permissionsAreAccepted = true; if (requestCode == LOCATION_PERMISSION_REQUEST_CODE && grantResults.length > 0) { for (int grantResult : grantResults) { if (grantResult != PackageManager.PERMISSION_GRANTED) { permissionsAreAccepted = false; break; } } if (permissionsAreAccepted) { // 权限到手先直接尝试开图层,地图已就绪的话会立刻生效 enableMyLocationLayer(); // 地图还没初始化完成的话,再触发地图加载回调 if (googleMap == null) { mapFragment.getMapAsync(this); } } else { askLocationPermission(); } } }
补全getLastLocation的权限检查和空判断:
private void getLastLocation() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return; } Task<Location> locationTask = mFusedLocationClient.getLastLocation(); locationTask.addOnSuccessListener(location -> { if (location == null) { Toast.makeText(this, "暂时无法获取当前位置,请检查设备定位开关", Toast.LENGTH_SHORT).show(); return; } currentLocation = location; zoomOnCurrentPlace(); }); }
额外修复:你
onMyLocationClick方法里的Toast字符串写了硬换行,会直接编译报错,记得改成转义换行符:"Current location:\n" + location。
内容的提问来源于stack exchange,提问作者mabdelouahad
相关产品推荐
相关产品推荐

