Fragment中Google Maps加载失败,求助排查问题
在Fragment中加载Google Maps空白,伴随consentverifier提示的问题排查
之前在Activity中能正常创建Google Maps,但现在在Fragment里加载地图显示空白界面,同时出现提示:Unable to update local snapshot for com.google.android.libraries.consentverifier may result in stale flags。
已检查Google Cloud平台的API密钥与SHA1码,Firebase登录功能正常,也参考过相关技术社区问题和官方文档,但问题仍未解决。以下是相关代码文件,请求定位错误:
SelectLocationFragment代码
package com.udacity.project4.locationreminders.savereminder.selectreminderlocation import android.Manifest import android.app.Activity import android.content.pm.PackageManager import android.content.res.Resources import android.location.Criteria import android.location.LocationManager import android.os.Bundle import android.util.Log import android.view.* import android.widget.Toast import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat.getSystemService import androidx.databinding.DataBindingUtil import androidx.navigation.fragment.findNavController import com.google.android.gms.maps.* import com.google.android.gms.maps.model.* import com.udacity.project4.R import com.udacity.project4.base.BaseFragment import com.udacity.project4.databinding.FragmentSelectLocationBinding import com.udacity.project4.locationreminders.savereminder.SaveReminderViewModel import com.udacity.project4.utils.setDisplayHomeAsUpEnabled import org.koin.android.ext.android.inject import java.util.* class SelectLocationFragment : BaseFragment(), OnMapReadyCallback { private lateinit var map: GoogleMap private lateinit var poiMarker: Marker private lateinit var mapView: MapView private val REQUEST_LOCATION_PERMISSION = 1 private lateinit var currentPOI: PointOfInterest //Use Koin to get the view model of the SaveReminder override val _viewModel: SaveReminderViewModel by inject() private lateinit var binding: FragmentSelectLocationBinding private val runningQOrLater = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { binding = DataBindingUtil.inflate(inflater, R.layout.fragment_select_location, container, false) // When the user confirms on the selected location, binding.saveLocationButton.setOnClickListener { // Send back the selected location details to the view model _viewModel.selectedPOI.value = currentPOI _viewModel.longitude.value = poiMarker.position.longitude _viewModel.latitude.value = poiMarker.position.latitude // Navigate back to the previous fragment to save the reminder and add the geofence findNavController().navigate(SelectLocationFragmentDirections.actionSelectLocationFragmentToSaveReminderFragment()) } return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.lifecycleOwner = this context?.let { MapsInitializer.initialize(it) } setHasOptionsMenu(true) setDisplayHomeAsUpEnabled(true) mapView = binding.map mapView.onCreate(savedInstanceState) mapView.getMapAsync(this) } override fun onMapReady(googleMap: GoogleMap) { googleMap.mapType = GoogleMap.MAP_TYPE_NORMAL map = googleMap setPoiClick(map) setMapStyle(map) /** * Inside this functions we check for permissions */ enableMyLocation() zoomOnMyLocation() //These coordinates represent the lattitude and longitude of the Googleplex. val latitude = 37.422160 val longitude = -122.084270 val zoomLevel = 15f val homeLatLng = LatLng(latitude, longitude) map.moveCamera(CameraUpdateFactory.newLatLngZoom(homeLatLng, zoomLevel)) map.addMarker(MarkerOptions().position(homeLatLng)) } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { inflater.inflate(R.menu.map_options, menu) } override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { // Change the map type based on the user's selection. R.id.normal_map -> { map.mapType = GoogleMap.MAP_TYPE_NORMAL true } R.id.hybrid_map -> { map.mapType = GoogleMap.MAP_TYPE_HYBRID true } R.id.satellite_map -> { map.mapType = GoogleMap.MAP_TYPE_SATELLITE true } R.id.terrain_map -> { map.mapType = GoogleMap.MAP_TYPE_TERRAIN true } else -> super.onOptionsItemSelected(item) } // Places a marker on the map and displays an info window that contains POI name. private fun setPoiClick(map: GoogleMap) { map.setOnPoiClickListener { poi -> poiMarker = map.addMarker( MarkerOptions() .position(poi.latLng) .title(poi.name) )!! poiMarker.showInfoWindow() currentPOI = poi } // de bagat o anulare a punctelor dinainte } private fun setMapStyle(map: GoogleMap) { try { // Customize the styling of the base map using a JSON raw object val success = map.setMapStyle( context?.let { MapStyleOptions.loadRawResourceStyle(it, R.raw.map_style) } ) if (!success) { Log.e("dra", "Style parsing failed.") } } catch (e: Resources.NotFoundException) { Log.e("dra", "Can't find style. Error: ", e) } } // Checks that users have given permission private fun isPermissionGranted() : Boolean { return context?.let { ContextCompat.checkSelfPermission(it, Manifest.permission.ACCESS_FINE_LOCATION) } === PackageManager.PERMISSION_GRANTED } // Checks if users have given their location and sets location enabled if so. private fun enableMyLocation() { if (isPermissionGranted()) { if (context?.let { ActivityCompat.checkSelfPermission(it, Manifest.permission.ACCESS_FINE_LOCATION) } != PackageManager.PERMISSION_GRANTED && context?.let { ActivityCompat.checkSelfPermission(it, Manifest.permission.ACCESS_COARSE_LOCATION) } != PackageManager.PERMISSION_GRANTED ) { return } map.isMyLocationEnabled = true } else { Toast.makeText(context, "App needs permissions to run!", Toast.LENGTH_LONG).show() ActivityCompat.requestPermissions( context as Activity, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_LOCATION_PERMISSION ) } } private fun zoomOnMyLocation() { val locationManager = getSystemService(requireContext(), LocationManager::class.java) val criteria = Criteria() if (context?.let { ActivityCompat.checkSelfPermission(it, Manifest.permission.ACCESS_FINE_LOCATION) } != PackageManager.PERMISSION_GRANTED && context?.let { ActivityCompat.checkSelfPermission(it, Manifest.permission.ACCESS_COARSE_LOCATION) } != PackageManager.PERMISSION_GRANTED ) { return } val location = locationManager!!.getLastKnownLocation( locationManager.getBestProvider(criteria, false)!! ) if (location != null) { map.animateCamera( CameraUpdateFactory.newLatLngZoom( LatLng(location.latitude, location.longitude), 13f ) ) val cameraPosition = CameraPosition.Builder() .target(LatLng(location.latitude, location.longitude)) // Sets the center of the map to location user .zoom(17f) // Sets the zoom .bearing(90f) // Sets the orientation of the camera to east .tilt(40f) // Sets the tilt of the camera to 40 degrees .build() // Creates a CameraPosition from the builder map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)) } } }
fragment_select_location.xml布局
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="viewModel" type="com.udacity.project4.locationreminders.savereminder.SaveReminderViewModel" /> </data> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.gms.maps.MapView android:id="@+id/map" android:name="com.google.android.gms.maps.SupportMapFragment" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.udacity.project4.locationreminders.savereminder.selectreminderlocation"/> <Button android:id="@+id/saveLocationButton" android:layout_width="150dp" android:layout_height="wrap_content" android:layout_marginBottom="20dp" android:background="#D71B60" android:textColor="@color/white" android:text="@string/save_location" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
问题定位与修复方案
1. MapView生命周期方法缺失
MapView在Fragment中必须完整实现对应生命周期回调,否则会导致地图初始化异常或显示空白。当前代码仅调用了mapView.onCreate(savedInstanceState),需补充以下方法:
override fun onStart() { super.onStart() mapView.onStart() } override fun onResume() { super.onResume() mapView.onResume() } override fun onPause() { super.onPause() mapView.onPause() } override fun onStop() { super.onStop() mapView.onStop() } override fun onDestroy() { super.onDestroy() mapView.onDestroy() } override fun onLowMemory() { super.onLowMemory() mapView.onLowMemory() }
2. MapView XML配置错误
布局中MapView的android:name属性设置错误,MapView不需要指定SupportMapFragment(该属性属于Fragment类),需删除该行:
<com.google.android.gms.maps.MapView android:id="@+id/map" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.udacity.project4.locationreminders.savereminder.selectreminderlocation"/>
3. 权限请求后未更新地图状态
用户授予位置权限后,当前代码未重新启用地图的myLocationEnabled,需重写权限回调方法:
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == REQUEST_LOCATION_PERMISSION) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { enableMyLocation() zoomOnMyLocation() } else { Toast.makeText(context, "位置权限被拒绝,无法显示当前位置", Toast.LENGTH_SHORT).show() } } }
4. 空指针风险修复
zoomOnMyLocation方法中,getBestProvider可能返回null,需添加空判断避免崩溃:
val provider = locationManager.getBestProvider(criteria, false) ?: return val location = locationManager.getLastKnownLocation(provider)
关于consentverifier提示的说明
该提示是Google Play服务内部警告,与地图空白无直接关联,更新Google Play服务到最新版本即可缓解,但不影响地图功能。
内容的提问来源于stack exchange,提问作者Dragos Toader
相关产品推荐
相关产品推荐

