如果标记数量增加,应用程序会变慢
Slow application if the number of Markers increases
在带有 osmDroid 和 osmBonusPack 的 OpenStreetMap 地图上,我显示标记并通过单击它打开一个气泡,一切正常,因为我想要达到一定数量的标记。
我在地图上放置的标记越多,应用程序的响应就越慢。
例如,对于 1000 个标记,工具栏菜单出现需要 6 秒,移动到另一个 activity(例如简单文本显示)也需要同样多的时间。
我的代码。
private void creationMarker(GeoPoint arg,
String titre,
String proximite,
String description,
String identifiant) {
double doubleProximite;
Marker startMarker = new Marker(map);
startMarker.setPosition(arg);
startMarker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
InfoWindow infoWindow = new MyInfoWindow(R.layout.bonuspack_bubble_black, map);
((MyInfoWindow) infoWindow).setTitre(titre);
((MyInfoWindow) infoWindow).setDescription(description);
((MyInfoWindow) infoWindow).setSubDescription(identifiant);
startMarker.setTitle(((MyInfoWindow) infoWindow).getTitre());
startMarker.setTitle(((MyInfoWindow) infoWindow).getDescription());
startMarker.setTitle(((MyInfoWindow) infoWindow).getSubDescription());
startMarker.setIcon(getResources().getDrawable(R.drawable.croix_verte, null).mutate());
startMarker.setInfoWindow(infoWindow);
doubleProximite = Double.parseDouble(proximite);
Polygon circle = new Polygon();
circle.setPoints(Polygon.pointsAsCircle(arg, doubleProximite));
int myColorZone, myColorCloture;
myColorZone = this.getResources().getColor(R.color.SurfaceZoneActive, getTheme());
circle.setFillColor(myColorZone); // couleur avec arrière plan transparent
myColorCloture = this.getResources().getColor(R.color.ClotureActive, getTheme());
circle.setStrokeColor(myColorCloture);// couleur de la circonférence
circle.setStrokeWidth(3); // épaisseur du trait
map.getOverlays().add(circle);
map.getOverlays().add(startMarker);
}
我使用带有 SQL 数据库的循环来获取标记数据。
我想标记越多,应用程序必须处理的事件就越多。
什么解决方案可以解决我的问题。
预先感谢您的回答
标记是相当重量级的,或者至少在我遇到与您类似的性能问题时,它们曾经出现过。可能有优化它们的潜力,但这意味着修改 Osmdroid 库并创建包含此类更改的拉取请求。
尝试进行一些分析以确保问题不在您的代码中,而在库中 (https://developer.android.com/studio/profile/cpu-profiler.html)。
注意您如何从数据库中读取数据 - 不要在主线程上执行并测量需要多长时间以确保它不是主要问题。
试试 Osmdrid 的 SimpleFastPointOverlay - 它应该针对更多的标记进行优化(尽管它们必须具有相同的图标)。即使它不够灵活,无法满足您的需求,请尝试验证标记是真正的问题。之后您可以查看源代码并将其用作学习material来实现您自己的自定义Overlay。
一个月过去了,我有办法了,我研究了文件夹https://github.com/MKergall/osmbonuspack中的项目
https://github.com/MKergall/osmbonuspack/tree/master/OSMBonusPack/src/main/java/org/osmdroid/bonuspack/clustering
我对管理集群感兴趣的三个文件。
- MarkerClusterer.java
- 半径MarkerClusterer.java
- StaticCluster.java
感谢 M.Kergall 和 Roman Sidorov,他们是这些 java 来源的创造者。
我只是将这些文件复制到一个测试项目中。我受到管理标记以放置圆形多边形的方法的启发,聚类方面效果很好,但根据缩放级别,当绘制圆形多边形时,不再对聚类感兴趣。我通过管理屏幕上的多边形绘图来解决这个问题,我绘制了屏幕上的内容,标记也是如此。现在有了 20,000 个标记和多边形,它才刚刚开始阻碍应用程序的响应能力,而之前大约是 200 个。下面是一些代码片段。
下面是一些代码片段。
主要activity代码
private void getlireDataBaseMarkers() {
bar.setVisibility(View.VISIBLE);
bar.getLayoutParams().height = 6;
bar.requestLayout();
Thread t = new Thread(){
public void run() {
Bundle messageBundle=new Bundle();
nbrMarker = 0;
dbMarkers = new DBHelper(MainActivity.this);
if (dbMarkers.numberOfRows() == 0) {
return;
}
SQLiteDatabase db = dbMarkers.getReadableDatabase();
Cursor res = db.rawQuery(SqlOrderVisibilite, null);
res.moveToFirst();
int qtyMarkers = dbMarkers.numberOfRows();
bar.setMax(qtyMarkers);
bar.setProgress(0);
MyRadiusMarkerClusterer radiusMarkers = new MyRadiusMarkerClusterer(MainActivity.this);
Double zoom = 15.0;
radiusMarkers.setMaxPolygonZoomLevel(zoom);
GeoPoint lePoint = new GeoPoint(0.0, 0.0, 0.0);
while (!res.isAfterLast() &&
!isPausing.get() &&
isRunning.get()) {
lePoint.setLatitude(res.getDouble(res.getColumnIndex(MARKERS_COLUMN_LATITUDE)));
lePoint.setLongitude(res.getDouble(res.getColumnIndex(MARKERS_COLUMN_LONGITUDE)));
Marker startMarker = new Marker(map);
startMarker.setPosition(lePoint);
startMarker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
startMarker.setIcon(getResources().getDrawable(R.drawable.person, null).mutate());
Polygon circle = new Polygon(map);
int myColorZone, myColorCloture;
circle.setPoints(Polygon.pointsAsCircle(lePoint,getRandomDoubleBetweenRange(20,200)));
myColorZone = MainActivity.this.getResources().getColor(R.color.colorAccent, getTheme());
circle.setFillColor(myColorZone);
myColorCloture = MainActivity.this.getResources().getColor(colorPrimary, getTheme());
circle.getOutlinePaint().setColor(myColorCloture);
circle.getOutlinePaint().setStrokeWidth(4);
radiusMarkers.add(startMarker);
radiusMarkers.add(circle);
nbrMarker++;
myMessage = handler.obtainMessage();
messageBundle.putInt(WRITE_QTY_MARKERS,2); // incrémenter le progress bar
myMessage.setData(messageBundle);
handler.sendMessage(myMessage);
res.moveToNext();
}
map.getOverlays().add(radiusMarkers);
res.close();
dbMarkers.close();
myMessage = handler.obtainMessage();
messageBundle.putInt(WRITE_QTY_MARKERS,1); // afficher le nombre de markers
myMessage.setData(messageBundle);
handler.sendMessage(myMessage);
}
};
isRunning.set(true);
isPausing.set(false);
t.start();
}
部分 MyMarkerClusterers 代码
@Override
public void draw (Canvas canvas, MapView mapView,boolean shadow){
//if zoom has changed and mapView is now stable, rebuild clusters:
// si le zoom a changé et que mapView est maintenant stable, reconstruisez les groupes
Double zoomLevel = mapView.getZoomLevelDouble();
//if (zoomLevel != mLastZoomLevel && !mapView.isAnimating()) {
if (!mapView.isAnimating()) {
mClusters = clusterer(mapView);
pClusters = pclusterer(mapView);
renderer(mClusters, pClusters, canvas, mapView);
setmLastZoomLevel(zoomLevel);
}
for (MyStaticCluster cluster : mClusters) {
cluster.getMarker().draw(canvas, mapView, shadow);
}
for (MyStaticCluster cluster : pClusters) {
if (getmLastZoomLevel() > mMaxPolygonZoomLevel) {
cluster.getPolygon().draw(canvas, mapView, shadow);
}
}
}
部分 MyRadiusMarkerClusterer 代码
@Override
public ArrayList<MyStaticCluster> clusterer(MapView mapView) {
ArrayList<MyStaticCluster> clusters = new ArrayList<>();
convertRadiusToMeters(mapView);
mClonedMarkers = new ArrayList<>(); //shallow copy
ArrayList<Marker> mFiltreMarkers = new ArrayList<>(mItems);
Iterator<Marker> it = mFiltreMarkers.iterator();
while (it.hasNext()){
Marker valeur = it.next();
GeoPoint point = valeur.getPosition();
// limiter le nombre de Marker suivant l'écran
if( mapView.getProjection().getNorthEast().getLatitude() >= point.getLatitude() &&
mapView.getProjection().getNorthEast().getLongitude() >= point.getLongitude() &&
mapView.getProjection().getSouthWest().getLatitude() <= point.getLatitude() &&
mapView.getProjection().getSouthWest().getLongitude() <= point.getLongitude()) {
mClonedMarkers.add(valeur);
}
it.remove();
}
while (!mClonedMarkers.isEmpty()) {
Marker m = mClonedMarkers.get(0);
MyStaticCluster cluster = createCluster(m,null, mapView);
clusters.add(cluster);
}
return clusters;
}
@Override public ArrayList<MyStaticCluster> pclusterer(MapView mapView) {
ArrayList<MyStaticCluster> clusters = new ArrayList<>();
convertRadiusToMeters(mapView);
pClonedPolygon = new ArrayList<> (); //shallow copy
ArrayList<Polygon> pFiltrePolygon = new ArrayList<>(pItems);
Iterator<Polygon> it = pFiltrePolygon.iterator();
while (it.hasNext()){
Polygon valeur = it.next();
GeoPoint point = valeur.getInfoWindowLocation();
// limiter le nombre de Polygon suivant l'écran
if( mapView.getProjection().getNorthEast().getLatitude() >= point.getLatitude() &&
mapView.getProjection().getNorthEast().getLongitude() >= point.getLongitude() &&
mapView.getProjection().getSouthWest().getLatitude() <= point.getLatitude() &&
mapView.getProjection().getSouthWest().getLongitude() <= point.getLongitude()) {
pClonedPolygon.add(valeur);
}
it.remove();
}
while (!pClonedPolygon.isEmpty()) {
Polygon p = pClonedPolygon.get(0);
MyStaticCluster cluster = createCluster(null,p, mapView);
clusters.add(cluster);
}
return clusters;
}
在带有 osmDroid 和 osmBonusPack 的 OpenStreetMap 地图上,我显示标记并通过单击它打开一个气泡,一切正常,因为我想要达到一定数量的标记。 我在地图上放置的标记越多,应用程序的响应就越慢。 例如,对于 1000 个标记,工具栏菜单出现需要 6 秒,移动到另一个 activity(例如简单文本显示)也需要同样多的时间。 我的代码。
private void creationMarker(GeoPoint arg,
String titre,
String proximite,
String description,
String identifiant) {
double doubleProximite;
Marker startMarker = new Marker(map);
startMarker.setPosition(arg);
startMarker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
InfoWindow infoWindow = new MyInfoWindow(R.layout.bonuspack_bubble_black, map);
((MyInfoWindow) infoWindow).setTitre(titre);
((MyInfoWindow) infoWindow).setDescription(description);
((MyInfoWindow) infoWindow).setSubDescription(identifiant);
startMarker.setTitle(((MyInfoWindow) infoWindow).getTitre());
startMarker.setTitle(((MyInfoWindow) infoWindow).getDescription());
startMarker.setTitle(((MyInfoWindow) infoWindow).getSubDescription());
startMarker.setIcon(getResources().getDrawable(R.drawable.croix_verte, null).mutate());
startMarker.setInfoWindow(infoWindow);
doubleProximite = Double.parseDouble(proximite);
Polygon circle = new Polygon();
circle.setPoints(Polygon.pointsAsCircle(arg, doubleProximite));
int myColorZone, myColorCloture;
myColorZone = this.getResources().getColor(R.color.SurfaceZoneActive, getTheme());
circle.setFillColor(myColorZone); // couleur avec arrière plan transparent
myColorCloture = this.getResources().getColor(R.color.ClotureActive, getTheme());
circle.setStrokeColor(myColorCloture);// couleur de la circonférence
circle.setStrokeWidth(3); // épaisseur du trait
map.getOverlays().add(circle);
map.getOverlays().add(startMarker);
}
我使用带有 SQL 数据库的循环来获取标记数据。
我想标记越多,应用程序必须处理的事件就越多。 什么解决方案可以解决我的问题。 预先感谢您的回答
标记是相当重量级的,或者至少在我遇到与您类似的性能问题时,它们曾经出现过。可能有优化它们的潜力,但这意味着修改 Osmdroid 库并创建包含此类更改的拉取请求。
尝试进行一些分析以确保问题不在您的代码中,而在库中 (https://developer.android.com/studio/profile/cpu-profiler.html)。
注意您如何从数据库中读取数据 - 不要在主线程上执行并测量需要多长时间以确保它不是主要问题。
试试 Osmdrid 的 SimpleFastPointOverlay - 它应该针对更多的标记进行优化(尽管它们必须具有相同的图标)。即使它不够灵活,无法满足您的需求,请尝试验证标记是真正的问题。之后您可以查看源代码并将其用作学习material来实现您自己的自定义Overlay。
一个月过去了,我有办法了,我研究了文件夹https://github.com/MKergall/osmbonuspack中的项目
https://github.com/MKergall/osmbonuspack/tree/master/OSMBonusPack/src/main/java/org/osmdroid/bonuspack/clustering 我对管理集群感兴趣的三个文件。
- MarkerClusterer.java
- 半径MarkerClusterer.java
- StaticCluster.java
感谢 M.Kergall 和 Roman Sidorov,他们是这些 java 来源的创造者。
我只是将这些文件复制到一个测试项目中。我受到管理标记以放置圆形多边形的方法的启发,聚类方面效果很好,但根据缩放级别,当绘制圆形多边形时,不再对聚类感兴趣。我通过管理屏幕上的多边形绘图来解决这个问题,我绘制了屏幕上的内容,标记也是如此。现在有了 20,000 个标记和多边形,它才刚刚开始阻碍应用程序的响应能力,而之前大约是 200 个。下面是一些代码片段。
下面是一些代码片段。
主要activity代码
private void getlireDataBaseMarkers() {
bar.setVisibility(View.VISIBLE);
bar.getLayoutParams().height = 6;
bar.requestLayout();
Thread t = new Thread(){
public void run() {
Bundle messageBundle=new Bundle();
nbrMarker = 0;
dbMarkers = new DBHelper(MainActivity.this);
if (dbMarkers.numberOfRows() == 0) {
return;
}
SQLiteDatabase db = dbMarkers.getReadableDatabase();
Cursor res = db.rawQuery(SqlOrderVisibilite, null);
res.moveToFirst();
int qtyMarkers = dbMarkers.numberOfRows();
bar.setMax(qtyMarkers);
bar.setProgress(0);
MyRadiusMarkerClusterer radiusMarkers = new MyRadiusMarkerClusterer(MainActivity.this);
Double zoom = 15.0;
radiusMarkers.setMaxPolygonZoomLevel(zoom);
GeoPoint lePoint = new GeoPoint(0.0, 0.0, 0.0);
while (!res.isAfterLast() &&
!isPausing.get() &&
isRunning.get()) {
lePoint.setLatitude(res.getDouble(res.getColumnIndex(MARKERS_COLUMN_LATITUDE)));
lePoint.setLongitude(res.getDouble(res.getColumnIndex(MARKERS_COLUMN_LONGITUDE)));
Marker startMarker = new Marker(map);
startMarker.setPosition(lePoint);
startMarker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
startMarker.setIcon(getResources().getDrawable(R.drawable.person, null).mutate());
Polygon circle = new Polygon(map);
int myColorZone, myColorCloture;
circle.setPoints(Polygon.pointsAsCircle(lePoint,getRandomDoubleBetweenRange(20,200)));
myColorZone = MainActivity.this.getResources().getColor(R.color.colorAccent, getTheme());
circle.setFillColor(myColorZone);
myColorCloture = MainActivity.this.getResources().getColor(colorPrimary, getTheme());
circle.getOutlinePaint().setColor(myColorCloture);
circle.getOutlinePaint().setStrokeWidth(4);
radiusMarkers.add(startMarker);
radiusMarkers.add(circle);
nbrMarker++;
myMessage = handler.obtainMessage();
messageBundle.putInt(WRITE_QTY_MARKERS,2); // incrémenter le progress bar
myMessage.setData(messageBundle);
handler.sendMessage(myMessage);
res.moveToNext();
}
map.getOverlays().add(radiusMarkers);
res.close();
dbMarkers.close();
myMessage = handler.obtainMessage();
messageBundle.putInt(WRITE_QTY_MARKERS,1); // afficher le nombre de markers
myMessage.setData(messageBundle);
handler.sendMessage(myMessage);
}
};
isRunning.set(true);
isPausing.set(false);
t.start();
}
部分 MyMarkerClusterers 代码
@Override
public void draw (Canvas canvas, MapView mapView,boolean shadow){
//if zoom has changed and mapView is now stable, rebuild clusters:
// si le zoom a changé et que mapView est maintenant stable, reconstruisez les groupes
Double zoomLevel = mapView.getZoomLevelDouble();
//if (zoomLevel != mLastZoomLevel && !mapView.isAnimating()) {
if (!mapView.isAnimating()) {
mClusters = clusterer(mapView);
pClusters = pclusterer(mapView);
renderer(mClusters, pClusters, canvas, mapView);
setmLastZoomLevel(zoomLevel);
}
for (MyStaticCluster cluster : mClusters) {
cluster.getMarker().draw(canvas, mapView, shadow);
}
for (MyStaticCluster cluster : pClusters) {
if (getmLastZoomLevel() > mMaxPolygonZoomLevel) {
cluster.getPolygon().draw(canvas, mapView, shadow);
}
}
}
部分 MyRadiusMarkerClusterer 代码
@Override
public ArrayList<MyStaticCluster> clusterer(MapView mapView) {
ArrayList<MyStaticCluster> clusters = new ArrayList<>();
convertRadiusToMeters(mapView);
mClonedMarkers = new ArrayList<>(); //shallow copy
ArrayList<Marker> mFiltreMarkers = new ArrayList<>(mItems);
Iterator<Marker> it = mFiltreMarkers.iterator();
while (it.hasNext()){
Marker valeur = it.next();
GeoPoint point = valeur.getPosition();
// limiter le nombre de Marker suivant l'écran
if( mapView.getProjection().getNorthEast().getLatitude() >= point.getLatitude() &&
mapView.getProjection().getNorthEast().getLongitude() >= point.getLongitude() &&
mapView.getProjection().getSouthWest().getLatitude() <= point.getLatitude() &&
mapView.getProjection().getSouthWest().getLongitude() <= point.getLongitude()) {
mClonedMarkers.add(valeur);
}
it.remove();
}
while (!mClonedMarkers.isEmpty()) {
Marker m = mClonedMarkers.get(0);
MyStaticCluster cluster = createCluster(m,null, mapView);
clusters.add(cluster);
}
return clusters;
}
@Override public ArrayList<MyStaticCluster> pclusterer(MapView mapView) {
ArrayList<MyStaticCluster> clusters = new ArrayList<>();
convertRadiusToMeters(mapView);
pClonedPolygon = new ArrayList<> (); //shallow copy
ArrayList<Polygon> pFiltrePolygon = new ArrayList<>(pItems);
Iterator<Polygon> it = pFiltrePolygon.iterator();
while (it.hasNext()){
Polygon valeur = it.next();
GeoPoint point = valeur.getInfoWindowLocation();
// limiter le nombre de Polygon suivant l'écran
if( mapView.getProjection().getNorthEast().getLatitude() >= point.getLatitude() &&
mapView.getProjection().getNorthEast().getLongitude() >= point.getLongitude() &&
mapView.getProjection().getSouthWest().getLatitude() <= point.getLatitude() &&
mapView.getProjection().getSouthWest().getLongitude() <= point.getLongitude()) {
pClonedPolygon.add(valeur);
}
it.remove();
}
while (!pClonedPolygon.isEmpty()) {
Polygon p = pClonedPolygon.get(0);
MyStaticCluster cluster = createCluster(null,p, mapView);
clusters.add(cluster);
}
return clusters;
}