mapbox / mapbox-gl-native-android

Interactive, thoroughly customizable maps in native Android powered by vector tiles and OpenGL
https://mapbox.com/mobile
Other
218 stars 114 forks source link

getSource(id) returns null and addSource(..) throws 'Cannot add source twice' #519

Open leocarona opened 4 years ago

leocarona commented 4 years ago

How come these 3 lines don't prevent an exception?

GeoJsonSource poisSource = new GeoJsonSource(SOURCE_ID, features); if (mapboxMap.getStyle().getSource(SOURCE_ID) == null) mapboxMap.getStyle().addSource(poisSource);

While debugging, getSource(SOURCE_ID) returns null, therefore addSource() is called. Though, addSource() throws this exception com.mapbox.mapboxsdk.style.sources.CannotAddSourceException: Cannot add source twice.

A bit of the StackTrace: com.mapbox.mapboxsdk.maps.NativeMapView.addSource(NativeMapView.java:905) com.mapbox.mapboxsdk.maps.Style.addSource(Style.java:117)

I'm using com.mapbox.mapboxsdk:mapbox-android-sdk:9.1.0-alpha.1.

A bit more about the context where the addSource() is called. I'm using data binding on a Fragment. The fragment observes changes on a LiveData object. mapViewModel.getLocaisDeInteresse().observe(getViewLifecycleOwner(), locais -> {})

When the Fragment is first loaded, it works. But if I navigate somewhere else then navigate back to this Fragment, the mentioned exception is thrown.

Any ideas why getSource would return null but addSource would say there is a source already added and the added source has the same id as the source I'm trying to add?

tobrun commented 4 years ago

A source object is tied to a style which is tied to a single MapView instance. Sources can not be reused across Activities/Fragments, you are trying to retain this object across the view lifecycle and trying to read it to a new map instance.This is not supported. You will need to create a new instance of a Source object. The exception thrown isn't very useful, that is something we should address.

leocarona commented 4 years ago

Didn't expect that. Really appreciated your help, thanks @tobrun.

To other people who might get here experiencing the same issue, here's how I changed my code in order to fix the exception:

if (mapboxMap.getStyle().getSource(SOURCE_ID) != null)
                mapboxMap.getStyle().removeSource(SOURCE_ID);

mapboxMap.getStyle().addSource(new GeoJsonSource(SOURCE_ID, features));

@tobrun if I may, I'd like to add a question to verify if I got it right. Is it correct to say that on the mentioned context (when navigating out and then navigating back), the style doesn't keep the source (what'd explain getSource() returning null) but it does keep the source id (what'd explain addSource() throwing Cannot add source twice exception)?

leocarona commented 4 years ago

The solution I mentioned on my last comment seemed to work in one scenario, but now I have another scenario where the same exception is thrown.

In what scenario that solution has helped - when navigating out and then navigating back to the fragment. In what scenario that solution didn't help - if I run 2 tasks and both try to add a source with same _SOURCEID to the style once the tasks finish, the exception is thrown once the second task finishes.

More detail on the exception:

com.mapbox.mapboxsdk.style.sources.CannotAddSourceException: Source spots-source already exists
    at com.mapbox.mapboxsdk.maps.NativeMapView.nativeAddSource(Native Method)
    at com.mapbox.mapboxsdk.maps.NativeMapView.addSource(NativeMapView.java:905)
    at com.mapbox.mapboxsdk.maps.Style.addSource(Style.java:117)
    at ..(MyFragment.java:1830)
    at ..(MyFragment.java:861)
    at ..MyFragment.access$700(MyFragment.java:106)
    at ..MyFragment$DrawAnnotationsTask.onPostExecute(MyFragment.java:1580)
    at ..MyFragment$DrawAnnotationsTask.onPostExecute(MyFragment.java:1477)
    at android.os.AsyncTask.finish(AsyncTask.java:755)
    at android.os.AsyncTask.access$900(AsyncTask.java:192)
    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:772)
    at android.os.Handler.dispatchMessage(Handler.java:107)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7403)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)
tobrun commented 4 years ago

@leocarona source_id should be unique

komuros commented 3 years ago

I have the same issue, in my case I can filter the GeoJson result by passing parameter in the query. I remove the previous source to reset my map and try to add new source with the new link create by the filter to the same source_id but take the same exception. Any solution to resolve it ? Can I create dynamic link was reload each time by the map when it's change ?