Mapbox-gl-native: App crashes with "A/libc: Fatal signal 11 (SIGSEGV)" after switching between a mapbox fragment and another fragment multiple times

Created on 16 Oct 2018  路  13Comments  路  Source: mapbox/mapbox-gl-native


The app contains three fragments of which one is a fragment containing a mapView. After switching back and forth quickly approx. 10 times (sometimes less, sometimes more) between the map fragment and another fragment the app crashes.

Steps to reproduce

  1. Switch to fragment containing the MapView
  2. Switch back to previous fragment
  3. Repeat step 1. and 2. quickly

Expected behavior

App does not crash

Actual behavior

App crashes

Configuration

Android versions: Android Oreo (physical device)/Lollipop(Emulator)
Mapbox SDK versions: 6.5.0

class FragmentMap : Fragment(), LocationEngineListener, PermissionsListener{

private lateinit var v : View
private lateinit  var mapView: MapView

private lateinit var map : MapboxMap
private lateinit var originLocation : Location
private lateinit var permissionsManager: PermissionsManager
private var locationEngine: LocationEngine? = null
private var locationLayerPlugin : LocationLayerPlugin? = null

private val t = "FragmentMap"

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    v = inflater.inflate(R.layout.fragment_map, container, false)
    Mapbox.getInstance(this.context!!, "My_TOKEN")
    mapView = v.findViewById(R.id.mapView)
    mapView.onCreate(savedInstanceState)
    mapView.getMapAsync{mapboxMap ->
        map = mapboxMap
        map.uiSettings.isCompassEnabled = true
        for (n in ProfileActivity.nastycoins!!) {//adding 50 markers here
            val icon : Icon = IconFactory.getInstance(activity!!.applicationContext).fromBitmap(BitmapFactory.decodeResource(resources, getFittingIconId(n)))
            map.addMarker(MarkerOptions().setIcon(icon)
                    .position(LatLng(n.coordinates.second, n.coordinates.first))
                    .title(n.id)
                    .snippet(n.value.toString()))
        }
        enableLocation()
    }

    return v
}

fun getFittingIconId(n : NastyCoin): Int{//Get the Icon to display on the map
    return  resources.getIdentifier(n.currency + n.marker_symbol, "mipmap", context?.packageName)
}
//Location, Map
private fun setCameraPosition(location: Location){
    map.animateCamera(CameraUpdateFactory.newLatLng(LatLng(location.latitude, location.longitude)))
}

private fun enableLocation(){
    if (PermissionsManager.areLocationPermissionsGranted(this.context)){
        Log.d(t, "Permissions granted")
        initLocationEngine()
        initLocationLayer()
    }else{
        Log.d(t, "Permissions denied")
        permissionsManager = PermissionsManager(this)
        permissionsManager.requestLocationPermissions(this.activity)
    }
}

@SuppressWarnings("MissingPermission")
override fun onStart() {
    super.onStart()
    if(PermissionsManager.areLocationPermissionsGranted(this.context)){
        locationEngine?.requestLocationUpdates()
        locationLayerPlugin?.onStart()
    }
    mapView.onStart()
}

override fun onResume() {
    super.onResume()
    mapView.onResume()
}

override fun onPause() {
    super.onPause()
    mapView.onPause()
}

override fun onStop() {
    locationEngine?.removeLocationUpdates()
    locationLayerPlugin?.onStop()
    super.onStop()
    mapView.onStop()
}

override fun onLowMemory() {
    super.onLowMemory()
    mapView.onLowMemory()
}

override fun onDestroy() {
    locationEngine?.deactivate()
    super.onDestroy()
    mapView.onDestroy()
}

override fun onDestroyView() {
    locationEngine?.deactivate()
    mapView.onDestroy()
    super.onDestroyView()
}

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    mapView.onSaveInstanceState(outState)
}

@SuppressWarnings("MissingPermission")
override fun onConnected() {
    Log.d(t, "[onConnected] requesting location updates")
    locationEngine?.requestLocationUpdates()
}

override fun onExplanationNeeded(permissionsToExplain: MutableList<String>?) {
    Log.d(t, "Permissions: $permissionsToExplain")
}

override fun onLocationChanged(location: Location?) {
    location?.let{
        originLocation = location
        setCameraPosition(location)
    }
}

override fun onPermissionResult(granted: Boolean) {
    Log.d(t, "[onPermissionResult] granted == $granted")
    if(granted){
        enableLocation()
    }else{
        Toast.makeText(context, "Let me know where you are. Hide and seek is not my game.", Toast.LENGTH_LONG).show()
    }
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
    permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults)
}

@SuppressWarnings("MissingPermission")
private fun initLocationEngine(){
    locationEngine = LocationEngineProvider(this.context).obtainBestLocationEngineAvailable()
    locationEngine?.apply {
        interval = 5000
        fastestInterval = 1000
        priority = LocationEnginePriority.HIGH_ACCURACY
        activate()
    }
    val lastLocation = locationEngine?.lastLocation
    if(lastLocation != null){
        originLocation = lastLocation
        setCameraPosition(lastLocation)
    }else{
        locationEngine?.addLocationEngineListener(this)
    }
}

@SuppressWarnings("MissingPermission")
private fun initLocationLayer(){
    locationLayerPlugin = LocationLayerPlugin(mapView, map, locationEngine)
    locationLayerPlugin?.apply{
        setLocationLayerEnabled(true)
        cameraMode = CameraMode.TRACKING
        renderMode = RenderMode.NORMAL
    }
}

}

Debugger log:
D/FragmentMap: Permissions granted
D/FragmentMap: [onConnected] requesting location updates
V/AudioManager: playSoundEffect effectType: 0
W/libEGL: EGLNativeWindowType 0x711cf5f010 disconnect failed
D/CubicBezierInterpolator: CubicBezierInterpolator mControlPoint1x = 0.23, mControlPoint1y = 0.06, mControlPoint2x = 0.09, mControlPoint2y = 0.97
D/CubicBezierInterpolator: CubicBezierInterpolator mControlPoint1x = 0.6, mControlPoint1y = 0.9, mControlPoint2x = 0.8, mControlPoint2y = 1.0
D/CubicBezierInterpolator: CubicBezierInterpolator mControlPoint1x = 0.23, mControlPoint1y = 0.06, mControlPoint2x = 0.09, mControlPoint2y = 0.97
CubicBezierInterpolator mControlPoint1x = 0.6, mControlPoint1y = 0.9, mControlPoint2x = 0.8, mControlPoint2y = 1.0
V/AudioManager: playSoundEffect effectType: 0
V/Mbgl-ConnectivityReceiver: Connected: true
I/Mbgl-EGLConfigChooser: In emulator: false
D/mali_winsys: EGLint new_window_surface(egl_winsys_display , void *, EGLSurface, EGLConfig, egl_winsys_surface *, egl_color_buffer_format *, EGLBoolean) returns 0x3000
D/FragmentMap: Permissions granted
V/AudioManager: playSoundEffect effectType: 0
W/libEGL: EGLNativeWindowType 0x711cf5f010 disconnect failed
D/CubicBezierInterpolator: CubicBezierInterpolator mControlPoint1x = 0.23, mControlPoint1y = 0.06, mControlPoint2x = 0.09, mControlPoint2y = 0.97
D/CubicBezierInterpolator: CubicBezierInterpolator mControlPoint1x = 0.6, mControlPoint1y = 0.9, mControlPoint2x = 0.8, mControlPoint2y = 1.0
CubicBezierInterpolator mControlPoint1x = 0.23, mControlPoint1y = 0.06, mControlPoint2x = 0.09, mControlPoint2y = 0.97
CubicBezierInterpolator mControlPoint1x = 0.6, mControlPoint1y = 0.9, mControlPoint2x = 0.8, mControlPoint2y = 1.0
D/FragmentMap: [onConnected] requesting location updates
V/AudioManager: playSoundEffect effectType: 0
A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x30 in tid 17974 (.frederik.coinz)
Disconnected from the target VM, address: 'localhost:8620', transport: 'socket'

Android archived

Most helpful comment

@emerciercontexeo was able to reproduce. You seem to be missing MapView#OnStop. Adding this to MapboxFragment#onStop resolved the issue for me.

All 13 comments

@FreddoIsHere thank you for the report, can you clarify what the following means?

Switch to fragment containing the MapView
Switch back to previous fragment

show some code around this? I'm interested to see how you are handling your fragment transactions so I can reproduce this locally

I have three fragments. Each one is accessible over a BottomNavigationView. Switching back and forth multiple times between two fragments of which one contains a map (class in my original post) provided by mapbox produced the error. The code handling the transactions is provided by an activity named ProfileActivity.
The relevant code for handling the transactions is:

class ProfileActivity : AppCompatActivity() {

private var bottomNavigationView : BottomNavigationView? = null
private var startFrame : FrameLayout? = null

private var fragmentDepot : FragmentDepot? = null
private var fragmentMap : FragmentMap? = null
private var fragmentSettings : FragmentSettings? = null

    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_profile)

    startFrame = findViewById(R.id.start_frame)
    bottomNavigationView = findViewById(R.id.navigation_bar)

    fragmentDepot = FragmentDepot()
    fragmentMap = FragmentMap()
    fragmentSettings = FragmentSettings()

    val transaction = supportFragmentManager.beginTransaction()
    transaction.replace(R.id.start_frame, fragmentDepot)
    transaction.commit()

    bottomNavigationView?.setOnNavigationItemSelectedListener {
        var selectedFragment: Fragment? = fragmentDepot
        when (it.itemId) {
            R.id.depot_tab -> selectedFragment = fragmentDepot
            R.id.map_tab -> selectedFragment = fragmentMap
            R.id.settings_tab -> selectedFragment = fragmentSettings
        }
        val trans = supportFragmentManager.beginTransaction()
        trans.replace(R.id.start_frame, selectedFragment)
        trans.commit()
        return@setOnNavigationItemSelectedListener true
    }
}
}

@FreddoIsHere thank you for providing that additional code snippet, I quickly refactored some similar code to use the same construct as you in terms of replacing the fragment. Code for that can be found here. Atm I'm not able to reproduce your issue with this atm. Would you be able to look into how this code differs from your setup?

One thing I'm seeing when looking at your provided code is the following:

override fun onDestroy() {
    locationEngine?.deactivate()
    super.onDestroy()
    mapView.onDestroy()
}

Can you remove mapView.onDestroy(), it should only be called from Fragment#onDestroyView().

I did not see any differences that could have caused the problem. Removing mapView.onDestroy() did not change anything.
However using replace() for switching in between fragments might not have been the best choice for my app since the loading of the map includes location features, 50 markers with individual icons and a bit more. I now worked around the error using hide() and show() for fragment switching. I did not encounter any problems with hide() and show(). Therefore either it was to much work for the app or there is some error in one of the lifecycle functions of my map fragment. Since you could not reproduce the error I am guessing that it was too much work for the app to handle or a library function was called that used references that did not exist at that point.
Nevertheless I am probably more of an beginner Android Development and don't know for sure. Could "A/libc: Fatal signal 11 (SIGSEGV)" have been caused by too much processing when rapidly switching in between fragments?
If so I apologize for posting this here since it is only indirectly an issue that is related to mapbox.

@tobrun This issue is related to https://github.com/mapbox/mapbox-gl-native/issues/12885. Same crash, crash source and workaround.

If it can help you, I created a small project to reproduce.

https://github.com/emerciercontexeo/MapboxIssuesDemonstrator/tree/%2313116

Branch #13116

Etienne

@emerciercontexeo thank you for providing the code, will look into it

@emerciercontexeo was able to reproduce. You seem to be missing MapView#OnStop. Adding this to MapboxFragment#onStop resolved the issue for me.

@tobrun Sorry for this, I am a bit tired this morning :)

Anyway, adding the onStop method resolve this for me too.

@tobrun When I integrated mapbox into may app about 2 years ago there was a sample on how to do so in a fragment. (Back then I think the MapView#onStop function was not included in the sample.)
I could not find any current sample addressing how to add a MapView in a fragment. I would be helpful if you'd re-add such an example again.

@p-fischer here is an example of adding a MapFragment to your app and you can inspect MapFragment class further to learn how to manage the lifecycle if you choose to implement the fragment manually.

Although I'm not using a MapFragment, its implementation serves as a reference. Thank you @LukasPaczos
Still I think using a MapView in a custom fragment is such a common scenario, it would be helpful if an example for this existed in the documentation.

This issue has been automatically detected as stale because it has not had recent activity and will be archived. Thank you for your contributions.

Was this page helpful?
0 / 5 - 0 ratings