The Android Activity and GLSurfaceView from native code
Recently I had the chance to build a small OpenGL based Activity in Android. This Activity was to be primarily written in C++ with only the most minimal parts left out in Java. I wanted to leverage Android’s GLSurfaceView for managing the OpenGL context, surface and display. Staying in bounds was important. I did not want to have to significantly change my Activity code later on due to the use of undocumented methods. That said, there are some definite difficulties with this approach, not all of which I have solved. I made some compromises but in the end I am satisfied with the result. Here is how I approached the problem and what I learned.
The Android Activity has 7 main methods that the OS calls to manage its lifetime. The Android Developers site describes the basics of the Activity Lifecycle. What they don’t describe is what the OS expects you to do (or not do) during these calls. The first thing I learned was that you should not do anything that takes a long time in any of these methods. Android OS will consider your application hung and terminate it. My solution was to create a separate native thread that did any real work. When the main thread called my activity I would simply queue the request for the worker thread to take care of later. The worker thread mostly just slept on a semaphore. Whenever one of the 7 Activity requests came in it would wake up and handle it. This solution worked well. I could perform long running tasks (like loading a file off the sdcard) without having Android kill my activity.
The next hurdle was to integrate OpenGL via GLSurfaceView into my application. I wanted all my rendering to be done natively. There are 3 methods on the GLSurfaceView.Renderer interface that I stubbed in Java for implementation in C++. The most important thing to realize is that GLSurfaceView creates its own thread for rendering. These 3 interface methods will be called from that thread, not the main activity thread. There is also no way to get at the EGL context and therefore no way to attach that EGL context to the main thread. This is somewhat annoying. I am cool with having the renderer in its own thread, that makes sense. However, I would want to have access to the EGL context from other threads so I could do things like load textures or build shader programs while maintaining a decent frame rate. I have yet to find a solution to this EGL context problem besides not using GLSurfaceView. Maybe that’s a topic for a later post. For now I am trying hard to stay in bounds.
Android suffers from the same lost context problems as programming in DirectX on Windows. Whenever your activity is covered up or hidden Android OS will kill your Renderer, EGL context and all your loaded GL resources. When the Activity is resumed later you are expected to recreate or reload all those resources. The big catch, and I mean big, is that Android doesn’t actually tell the GLSurfaceView.Renderer that the context is being destroyed. It simply tosses the resources and lets all your GL handles become invalid. You do get a clue on the main Activity thread, the onPause method is called around the time that the Renderer is destroyed.
To overcome these problems I had to split my loading code across my worker thread and the GLSurfaceView.Renderer thread. The worker thread loads the vast majority of my resources. It does not load any GL related resources, it simply remembers where on disk they are. Later on, whenever the GLSurfaceView.Renderer starts up, the render thread detects it needs to load some GL resources and does so. This solution works well for both regular Activity startup as well as for the pause/resume cycle.
Ideally, I would have created a third thread that handled loading. Then the renderer thread could have maintained interactive frame rates while GL resources were being loaded. This unfortunately is not possible with the current GLSurfaceView implementation.
I am not sure where I am headed next. These compromises should hold for some time on Android. I have thought about completely tossing GLSurfaceView and implementing my own. That would be a lot of work for not much gain. The current Android provided GLSurfaceView is close to being really cool. All it needs are 2 small features. It needs a way to share the EGL context and attach it to other threads. For example a method called GLSurfaceView.attachContextToCurrentThread could suffice. It also needs to add another method, perhaps GLSurfaceView.Renderer.onSurfaceDestroyed, that signals when the Renderer is being destroyed. Those both seem relatively simple to add and would greatly enhance the ability of Android GL based activities.