osmdroid如何绘制RTK-GPS实时位置及移动轨迹并避免应用卡顿
问题解答
- 原有思路核心逻辑可行,卡顿闪退是实现细节存在性能问题,并非思路本身不可行。
- 安卓端完全可以实现该类移动轨迹绘制功能,市面上大量运动类、导航类应用均已实现同类功能,性能可以得到保障。
- osmdroid自带
Polyline组件,专门用于绘制线条类图形,可直接用来实现移动轨迹绘制,无需通过Marker拼接轨迹。 - 可参考osmdroid官方提供的Polyline使用示例、开源运动轨迹类应用的地图绘制模块逻辑。
现有代码性能问题点
- 每次
onReceive回调都重复执行findViewById查找MapView、TextView等控件,重复设置地图缩放、触控属性,上述操作仅需在onCreate中执行一次,高频回调中重复执行会大幅消耗性能。 - 使用Marker承载轨迹点是错误实现:Marker是独立的标注控件,每个实例都会占用独立的绘制资源,大量Marker叠加会快速耗尽内存导致闪退,轨迹绘制无需使用Marker实现。
- 每次新增点都调用
invalidate重绘全地图,1秒10次的重绘频率远高于视觉所需,会带来大量不必要的渲染消耗。 - 静态变量持有MapView、Marker列表容易引发内存泄漏,也是闪退的诱因之一。
优化实现方案
核心优化逻辑
- 所有控件初始化、地图基础配置仅在
onCreate中执行一次,onReceive中仅做数据更新操作。 - 仅保留1个当前位置Marker,新位置数据到达时直接更新该Marker的坐标即可,无需每次新建Marker实例。
- 全局仅初始化1个
Polyline实例用于承载轨迹,初始化时就添加到地图Overlay中,后续新轨迹点直接追加到Polyline的点列表即可。 - 新增冗余点过滤逻辑:可判断新点与上一个轨迹点的距离小于1米时不加入轨迹,也可保留每10个采样点取1个的逻辑,降低轨迹点密度。
- 降低重绘频率:每追加3-5个轨迹点或间隔200ms再触发一次地图重绘即可,不影响视觉流畅度的同时大幅降低性能消耗。
核心修改代码示例
public class MapGeneratorMainActivity extends Activity { private static final double MAP_ZOOM = 15.0; private static final double MAP_ZOOM2 = 17.0; // 不要用静态变量持有MapView private MapView mapGeneratorMainMap; private IMapController mapController; private TextView currentLocatonTextView; private Marker currentPtMarker; private Polyline trajectoryPolyline; private ArrayList<GeoPoint> trajectoryPoints = new ArrayList<>(); private int receiveCount = 0; private GeoPoint lastTrajectoryPoint; private static final float MIN_DISTANCE_THRESHOLD = 1f; // 最小距离阈值1米 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); org.osmdroid.config.Configuration.getInstance().load(getApplicationContext(), PreferenceManager.getDefaultSharedPreferences(getApplicationContext())); setContentView(R.layout.map_generator_main_activity); // 所有初始化操作仅在这里执行一次 mapGeneratorMainMap = findViewById(R.id.MapGaneratorMainMap); currentLocatonTextView = findViewById(R.id.CurrentLocation); mapGeneratorMainMap.setMultiTouchControls(true); mapController = mapGeneratorMainMap.getController(); mapController.setZoom(MAP_ZOOM); GeoPoint centerPoint = new GeoPoint(aveLat, aveLon); mapController.setCenter(centerPoint); mapGeneratorMainMap.setTilesScaledToDpi(true); final MapTileProviderBasic tileProvider = new MapTileProviderBasic(getApplicationContext()); ITileSource tileSource = new XYTileSource("GSI", 14, 24, 256, ".jpg", new String[]{TILE_SEVER}); tileProvider.setTileSource(tileSource); final TilesOverlay tilesOverlay = new TilesOverlay(tileProvider, this.getApplicationContext()); tilesOverlay.setLoadingBackgroundColor(Color.TRANSPARENT); mapGeneratorMainMap.getOverlays().add(tilesOverlay); // 初始化当前位置Marker 仅初始化一次 currentPtMarker = new Marker(mapGeneratorMainMap); Drawable currentMarkerIcon = ResourcesCompat.getDrawable(getResources(), R.drawable.current_point_marker, null); currentPtMarker.setIcon(currentMarkerIcon); currentPtMarker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER); mapGeneratorMainMap.getOverlayManager().add(currentPtMarker); // 初始化轨迹Polyline 仅初始化一次 trajectoryPolyline = new Polyline(); trajectoryPolyline.setColor(Color.RED); trajectoryPolyline.setWidth(5f); mapGeneratorMainMap.getOverlayManager().add(trajectoryPolyline); mapGeneratorMainMap.invalidate(); FloatingActionButton myLocationButton = findViewById(R.id.myLocationButton); myLocationButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intentService = new Intent(getApplication(), gpsService.class); intentService.putExtra("REQUEST_CODE", 1); startForegroundService(intentService); } }); UpdateReceiver receiver = new UpdateReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("DO_ACTION"); registerReceiver(receiver, filter); } protected class UpdateReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { Bundle extras = intent.getExtras(); String msg = extras.getString("message"); currentLocatonTextView.setText(msg); String[] currentLocaton = msg.split(",", -1); double lat = Double.parseDouble(currentLocaton[0]); double lon = Double.parseDouble(currentLocaton[1]); GeoPoint currentP = new GeoPoint(lat, lon); // 更新当前位置Marker currentPtMarker.setPosition(currentP); mapController.setCenter(currentP); receiveCount++; // 每10个点采1个 同时过滤距离过近的冗余点 if(receiveCount % 10 == 0){ if(lastTrajectoryPoint == null || currentP.distanceToAsDouble(lastTrajectoryPoint) >= MIN_DISTANCE_THRESHOLD){ trajectoryPoints.add(currentP); trajectoryPolyline.setPoints(trajectoryPoints); lastTrajectoryPoint = currentP; mapGeneratorMainMap.invalidate(); } } } } }
内容的提问来源于stack exchange,提问作者takes.it.easy.324
相关产品推荐
相关产品推荐

