安卓Google Maps DirectionsAPI路线更新流畅度优化求助
实时路线更新卡顿优化方案
问题背景
基于Google Maps API开发Android应用,已实现getPlaceDetails、getDirections、drawRouteOnMap等功能,目标是实现类似Waze的实时路线更新:用户移动时自动删除旧路线并绘制新路线。当前在startLocationUpdates()中设置用户移动超过10米时调用getDirections()更新路线,但主线程频繁出现帧丢失(如“Skipped 33 frames!”),应用卡顿,尝试AsyncTask、Handler优化未解决。
核心卡顿原因
getDirections()同步阻塞主线程:使用request.await()同步调用Google Directions API,阻塞主线程直到API返回结果,直接导致UI卡顿。GeoApiContext重复创建:每次调用getDirections()都新建上下文对象,造成不必要的资源开销。- 位置更新触发过于频繁:10米的更新阈值会导致短时间内多次发起路线请求,加重主线程负担。
- 路线解码与绘制未异步处理:路线polyline解码和地图绘制操作直接在主线程执行,占用UI线程资源。
- Firebase操作阻塞主线程:位置回调中的GeoFire位置更新属于IO操作,直接在主线程执行会拖慢UI响应。
优化方案
1. 异步执行Directions API请求,避免阻塞主线程
将同步调用改为异步回调,复用全局GeoApiContext,并将路线解码逻辑放到后台线程执行,仅在主线程更新地图。
// 全局复用GeoApiContext,在Activity onCreate中初始化一次 private GeoApiContext geoApiContext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ...其他初始化代码 geoApiContext = new GeoApiContext.Builder() .apiKey("YOUR_API_KEY") .build(); } private void getDirections(LatLng origin, LatLng destination){ DirectionsApiRequest request = DirectionsApi.getDirections(geoApiContext, String.format("%f,%f", origin.latitude, origin.longitude), String.format("%f,%f", destination.latitude, destination.longitude)); // 异步请求,避免阻塞主线程 request.setCallback(new com.google.maps.PendingResult.Callback<DirectionsResult>() { @Override public void onResult(DirectionsResult result) { // 后台线程解码路线polyline new Thread(() -> { List<LatLng> path = decodeRoutePolyline(result.routes[0]); // 切换到主线程更新地图 runOnUiThread(() -> drawRouteOnMap(path)); }).start(); } @Override public void onFailure(Throwable e) { e.printStackTrace(); } }); } // 提取路线解码逻辑,在后台线程执行 private List<LatLng> decodeRoutePolyline(DirectionsRoute route) { List<LatLng> path = new ArrayList<>(); for (DirectionsLeg leg : route.legs) { for (DirectionsStep step : leg.steps) { EncodedPolyline polyline = step.polyline; List<com.google.maps.model.LatLng> decodedPolyline = polyline.decodePath(); for (com.google.maps.model.LatLng point : decodedPolyline) { path.add(new LatLng(point.lat, point.lng)); } } } return path; } // 修改绘制逻辑,直接接收解码后的路径 private void drawRouteOnMap(List<LatLng> path) { if(currentPolyLine == null){ currentPolyLine = mMap.addPolyline(new PolylineOptions() .color(Color.GREEN) .width(10)); } currentPolyLine.setPoints(path); // 复用Polyline对象,直接更新点集 }
2. 调整位置更新阈值与频率
- 提高路线更新的距离阈值,减少不必要的API请求;
- 优化
LocationRequest参数,降低位置更新频率,同时限制最快更新间隔。
private void startLocationUpdates() { // ...其他代码 LocationRequest locationRequest = new LocationRequest(); locationRequest.setInterval(10000); // 基础更新间隔10秒 locationRequest.setFastestInterval(5000); // 最快更新间隔5秒,避免过于频繁 locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); // ...其他代码 // 调整路线更新距离阈值 float distance = prevLocation.distanceTo(smoothLocate); if(distance >= 25){ // 改为25米更新一次路线 prevLocation=smoothLocate; if(destinationLocation!=null){ getDirections(userL,destinationLocation); } } }
3. 分离位置平滑与距离判断的历史位置
避免prevLocation同时用于位置平滑和距离判断,单独维护平滑用的历史位置,减少逻辑混淆。
private Location smoothLocationHistory; // 专门用于位置平滑的历史位置 private Location smoothLocationUpdate(Location newLocation) { if (smoothLocationHistory == null) { smoothLocationHistory = newLocation; return newLocation; } double weight = 0.7; // 调整权重,减少过度平滑导致的位置延迟 double lat = smoothLocationHistory.getLatitude() + weight * (newLocation.getLatitude() - smoothLocationHistory.getLatitude()); double lng = smoothLocationHistory.getLongitude() + weight * (newLocation.getLongitude() - smoothLocationHistory.getLongitude()); Location smoothedLocation = new Location(newLocation); smoothedLocation.setLatitude(lat); smoothedLocation.setLongitude(lng); smoothLocationHistory = smoothedLocation; return smoothedLocation; }
4. 后台执行Firebase GeoFire操作
将位置回调中的Firebase IO操作放到后台线程,避免阻塞主线程。
// 在位置回调中修改GeoFire更新逻辑 new Thread(() -> { String userId = FirebaseAuth.getInstance().getCurrentUser().getUid(); DatabaseReference ref = FirebaseDatabase.getInstance().getReference("BikersAvailable"); GeoFire geoFire = new GeoFire(ref); geoFire.setLocation(userId, new GeoLocation(smoothLocate.getLatitude(),smoothLocate.getLongitude())); }).start();
内容的提问来源于stack exchange,提问作者Carlolong
相关产品推荐
相关产品推荐

