You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 13:20:04