Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Source added by Style.addSource is destroyed after the MapView is destroyed #3269

Open
grab-liyanjin opened this issue Mar 3, 2025 · 3 comments
Labels

Comments

@grab-liyanjin
Copy link
Contributor

MapLibre Android Version

Newest master, last commit 691384b

Android SDK Version

Android 14

Device

Google Pixel 5

What happened?

App crash

Steps to reproduce

Add button in SimpleMapActivity in the demo app and add a click listener like this

           button.setOnClickListener {
            val sourceID = "geoJsonSource1"
            val layerID = "symbolLayer"
            val source1 = GeoJsonSource(sourceID)
            val layer = SymbolLayer(layerID, sourceID)
            val style: Style = mapLibreMap.style!!
            style.addSource(source1)
            style.addLayer(layer)
//            style.removeSource(source1.id)
//            val source2 = style.getSource(sourceID) // these two lines are for grab crash issue
            mapView.onDestroy()
            Log.d("SimpleMapActivity", "source id :${source1?.id} ")
        }

Renderer

OpenGL (choose this if you are unsure)

Relevant log output

2025-03-03 18:19:36.081 12537-12537 AndroidRuntime          org.maplibre.android.testapp         E  FATAL EXCEPTION: main
                                                                                                    Process: org.maplibre.android.testapp, PID: 12537
                                                                                                    java.lang.IllegalStateException: invalid native peer
                                                                                                    	at org.maplibre.android.style.sources.Source.nativeGetId(Native Method)
                                                                                                    	at org.maplibre.android.style.sources.Source.getId(Source.java:56)
                                                                                                    	at org.maplibre.android.testapp.activity.maplayout.SimpleMapActivity.onCreate$lambda$1(SimpleMapActivity.kt:63)
                                                                                                    	at org.maplibre.android.testapp.activity.maplayout.SimpleMapActivity.$r8$lambda$fHdMdfqknUyYDBH_FgnttCUew88(Unknown Source:0)
                                                                                                    	at org.maplibre.android.testapp.activity.maplayout.SimpleMapActivity$$ExternalSyntheticLambda2.onClick(D8$$SyntheticClass:0)
                                                                                                    	at android.view.View.performClick(View.java:7659)
                                                                                                    	at android.view.View.performClickInternal(View.java:7636)
                                                                                                    	at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
                                                                                                    	at android.view.View$PerformClick.run(View.java:30156)
                                                                                                    	at android.os.Handler.handleCallback(Handler.java:958)
                                                                                                    	at android.os.Handler.dispatchMessage(Handler.java:99)
                                                                                                    	at android.os.Looper.loopOnce(Looper.java:205)
                                                                                                    	at android.os.Looper.loop(Looper.java:294)
                                                                                                    	at android.app.ActivityThread.main(ActivityThread.java:8177)
                                                                                                    	at java.lang.reflect.Method.invoke(Native Method)
                                                                                                    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
                                                                                                    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)

Additional context

our app is using an old mapbox-based map, one difference I found is:
In old Mapbox, Android Style.clear is like this, need to call nativeMap.removeSource explicitly

void clear() {
  for (BitmapImageConversionTask task : tasks) {
    if (task == null) {
      continue;
    }
    if (!task.isCancelled()) {
      task.cancel(true);
    }
  }
  tasks.clear();

  fullyLoaded = false;
  clearLayers();
  // clearSources must be called after clearLayers
  clearSources();
  clearImages();
}

void clearSources() {
  for (Source source : sources.values()) {
    if (source != null) {
      source.setDetached();
      nativeMap.removeSource(source);
    }
  }
  sources.clear();
}

But in the newest master MapLibre, the Style.clear() just clear the java source peer, but how could its C++ peer be destroyed.. I checked the cpp part, didn't find any clue

void clear() {
  fullyLoaded = false;
  for (Layer layer : layers.values()) {
    if (layer != null) {
      layer.setDetached();
    }
  }

  for (Source source : sources.values()) {
    if (source != null) {
      source.setDetached();
    }
  }

  for (Map.Entry<String, Bitmap> bitmapEntry : images.entrySet()) {
    nativeMap.removeImage(bitmapEntry.getKey());
    bitmapEntry.getValue().recycle();
  }

  sources.clear();
  layers.clear();
  images.clear();
}
@louwers
Copy link
Collaborator

louwers commented Mar 3, 2025

Confirmed.

@louwers
Copy link
Collaborator

louwers commented Mar 3, 2025

This is by design, after the source is added to a map instance it is owned by that map instance. So when the map instance is gone so is the source. For this reason you need to create a new source before adding it to a map.

Is this a problem?

@grab-liyanjin
Copy link
Contributor Author

@louwers Understood, I think your point makes sense. Could you please help me understand how MapLibre implements this mechanism that the source can be destroyed automatically when the MapView is destroyed. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants