android
android copied to clipboard
Crash: GLSurfaceView IllegalStateException: setRenderer has already been called for this instance
Description
GLSurfaceView IllegalStateException: setRenderer has already been called for this instance. Happens on line 211 of MapController.js (in call to view.setRenderer(this))
protected MapController(GLSurfaceView view) {
// Set up MapView
mapView = view;
view.setRenderer(this);
view.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
view.setPreserveEGLContextOnPause(true);
Here is the call stack:
06-06 22:33:19.824 16800-16800/AndroidRuntime: FATAL EXCEPTION: main
java.lang.IllegalStateException: setRenderer has already been called for this instance.
at android.opengl.GLSurfaceView.checkRenderThreadState(GLSurfaceView.java:1893)
at android.opengl.GLSurfaceView.setRenderer(GLSurfaceView.java:348)
at com.mapzen.tangram.MapController.
Steps to Reproduce
To reproduce you must rotate the phone 90 degree and rotate it back -90 degree quickly. It must be rotated back -90 degree so that your MainActivity onStop gets called before the onMapReady gets called.
Mapzen SDK & Android Version
Mapzen 1.6.1. Android 8 Oreo.
Possibly because the onSceneReady that was posted on the UI thread gets callled after the com.mapzen.tangram.MapView disposeMap() method was called.
// Called from JNI on worker or render-thread.
void sceneReadyCallback(final int sceneId, final SceneError error) {
final SceneLoadListener cb = sceneLoadListener;
if (cb != null) {
uiThreadHandler.post(new Runnable() {
@Override
public void run() {
cb.onSceneReady(sceneId, error);
}
});
}
}
The com.mapzen.tangram.MapView disposeMap() method sets the mapController to null but does not free the GLSurfaceView.
protected void disposeMap() {
if (mapController != null) {
// MapController has been initialized, so we'll dispose it now.
mapController.dispose();
}
mapController = null;
}
Then when the MapView.getMap gets called the mapController is null so it creates a new MapController.
public MapController getMap(MapController.SceneLoadListener listener) {
if (mapController != null) {
return mapController;
}
mapController = getMapInstance();
mapController.setSceneLoadListener(listener);
mapController.init();
return mapController;
}
protected MapController getMapInstance() {
return new MapController(glSurfaceView);
}
And the MapController constructor calls view.setRenderer(this), but the view (which is the old undisposed GLSurfaceView) already has a renderer. Then setRenderer throws this IllegalStateException.