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

Android Google Maps显示用户定位与服务端站点问题修复

问题根因

你代码里存在多个直接导致启动崩溃、功能异常的核心问题:

  • FusedLocationProviderClient 从未初始化,调用getLastLocation()时直接触发空指针
  • 位置权限逻辑不一致:申请权限用的请求码是101,权限回调里判断的请求码是0,授权成功后不会自动触发位置获取
  • 时序逻辑错误:onCreateView里调用的fetchLocation()是异步操作,立刻同步执行getMapAsync时currentLocation大概率为null,直接调用currentLocation.getLatitude()触发空指针
  • Fragment管理器用错:嵌套在当前Fragment里的SupportMapFragment应该用getChildFragmentManager()查找,你在fetchLocation里用了getParentFragmentManager()找不到Map实例,还做了错误的类型强转(当前Fragment没实现OnMapReadyCallback接口,强转直接崩溃)
  • 地图操作顺序错误:刚添加完当前位置Marker就调用mMap.clear(),直接把刚加的当前位置标记清掉
  • 权限申请回调没有后续逻辑:用户同意权限后没有重新调用位置获取方法
  • 经纬度解析用Float.parseFloat会丢失精度,且循环内重复设置Polyline颜色、重复移动相机属于无效冗余操作
修复方案
  1. 在onCreate生命周期里初始化位置客户端
  2. 统一权限请求码,修正权限回调逻辑,授权后重新拉取位置
  3. 调整地图加载时序:等位置获取成功后再触发地图加载,避免空指针
  4. 统一用getChildFragmentManager()查找嵌套的MapFragment,去掉错误的类型强转
  5. 调整地图clear的时机:在添加所有标记之前调用clear,避免误删已添加的标记
  6. 经纬度解析改用Double.parseDouble保证精度,把Polyline颜色设置、相机边界调整放到循环外执行
  7. 增加位置为null、权限被拒绝的兜底处理,避免直接崩溃
修复后完整代码
public class MapsLine extends Fragment {
    private String sid_user;
    private int did;
    GoogleMap map;
    // 统一权限请求码
    private static final int REQUEST_LOCATION_PERMISSION = 101;
    Location currentLocation;
    FusedLocationProviderClient fusedLocationProviderClient;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 初始化位置服务客户端
        fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(requireContext());
    }
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_maps_line, container, false);
        // 先检查权限拉取位置,位置拿到后再加载地图
        fetchLocation();
        return rootView;
    }

    private void initMap() {
        // 用childFragmentManager查找嵌套的地图Fragment
        SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map);
        if (mapFragment == null) return;
        
        mapFragment.getMapAsync(mMap -> {
            map = mMap;
            mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
            // 先清空地图旧标记,再添加新内容
            mMap.clear();

            // 渲染当前位置标记
            if (currentLocation != null) {
                LatLng currentLatLng = new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude());
                mMap.addMarker(new MarkerOptions().position(currentLatLng).title("我的位置"));
                mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(currentLatLng, 10));
            }

            // 拉取服务端站点数据
            List<LatLng> routeArray = new ArrayList<>();
            PolylineOptions polylineOptions = new PolylineOptions().color(Color.RED).width(8f);
            JSONObject requestParam = new JSONObject();
            String GET_STATIONS = "https://helpme.php";
            try {
                requestParam.put("did", did);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            JsonObjectRequest req = new JsonObjectRequest(
                    Request.Method.POST,
                    GET_STATIONS,
                    requestParam,
                    response -> {
                        try {
                            JSONArray stations_array = response.getJSONArray("stations");
                            Log.d("main_activity_sid_map", stations_array.toString());
                            Log.d("main_activity_sid_map", String.valueOf(stations_array.length()));
                            LatLngBounds.Builder boundsBuilder = LatLngBounds.builder();
                            // 把当前位置加入边界计算,打开地图可同时看到当前位置和所有站点
                            if (currentLocation != null) {
                                boundsBuilder.include(new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude()));
                            }

                            for (int j = 0; j < stations_array.length(); j++) {
                                JSONObject stations = stations_array.getJSONObject(j);
                                String name = stations.getString("sname");
                                // 用double解析经纬度避免精度丢失
                                double lat = stations.getDouble("lat");
                                double lon = stations.getDouble("lon");
                                Log.d("main_activity_sid_map", name + " " + lat + " " + lon);
                                
                                LatLng stationLatLng = new LatLng(lat, lon);
                                // 添加站点标记
                                mMap.addMarker(new MarkerOptions().position(stationLatLng).title(name));
                                // 加入路线点集合
                                if (!routeArray.contains(stationLatLng)) {
                                    routeArray.add(stationLatLng);
                                    boundsBuilder.include(stationLatLng);
                                }
                            }
                            // 循环外统一绘制路线
                            if (!routeArray.isEmpty()) {
                                polylineOptions.addAll(routeArray);
                                mMap.addPolyline(polylineOptions);
                                // 调整相机到包含所有点的合适范围
                                LatLngBounds bounds = boundsBuilder.build();
                                int padding = 120;
                                mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, padding));
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    },
                    Throwable::printStackTrace
            );
            if (getActivity() != null) {
                Volley.newRequestQueue(requireContext()).add(req);
            }
        });
    }

    private void fetchLocation() {
        if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) !=
                PackageManager.PERMISSION_GRANTED &&
                ActivityCompat.checkSelfPermission(requireContext(),
                        Manifest.permission.ACCESS_COARSE_LOCATION) !=
                        PackageManager.PERMISSION_GRANTED) {
            // 统一权限请求码,同时申请精确定位和粗略定位权限
            requestPermissions(
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION},
                    REQUEST_LOCATION_PERMISSION);
            return;
        }
        Task<Location> task = fusedLocationProviderClient.getLastLocation();
        task.addOnSuccessListener(location -> {
            // 兜底处理:lastLocation为null时给默认坐标,避免空指针
            if (location != null) {
                currentLocation = location;
                Toast.makeText(requireContext(), "定位成功:" + currentLocation.getLatitude() + "," + currentLocation.getLongitude(), Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(requireContext(), "获取历史位置失败,请开启定位后重试", Toast.LENGTH_SHORT).show();
                // 默认坐标设为北京,可根据需求修改
                currentLocation = new Location("default");
                currentLocation.setLatitude(39.9042);
                currentLocation.setLongitude(116.4074);
            }
            // 位置获取完成后再初始化地图
            initMap();
        }).addOnFailureListener(e -> {
            e.printStackTrace();
            // 定位失败也走默认逻辑加载地图
            currentLocation = new Location("default");
            currentLocation.setLatitude(39.9042);
            currentLocation.setLongitude(116.4074);
            initMap();
        });
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_LOCATION_PERMISSION) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Log.d("Location", "定位权限已授予");
                // 授权成功后重新拉取位置
                fetchLocation();
            } else {
                Log.d("Location", "定位权限被拒绝");
                Toast.makeText(requireContext(), "需要定位权限才能展示当前位置", Toast.LENGTH_SHORT).show();
                // 权限拒绝也加载地图,使用默认坐标
                currentLocation = new Location("default");
                currentLocation.setLatitude(39.9042);
                currentLocation.setLongitude(116.4074);
                initMap();
            }
        }
    }
}
配置检查项
  • 确认AndroidManifest.xml中已经添加必要权限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
  • 确认布局文件fragment_maps_line.xml中MapFragment的id为map,类型为SupportMapFragment
  • 确认项目中已经正确配置Google Maps API密钥,否则地图会显示黑屏

内容的提问来源于stack exchange,提问作者Elly

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 23:09:59