While I did not think I would continue hacking on
VoiDroid after achieving a semi-broken proof-of-concept implementation, it seems that I can t get out of it easily :-). I spent today trying to implement a solution which would allow the native C++ code to call into Java - for example, if we have a call in progress, and want to update the call status displayed to the user when it changes. The JNI library has some native callbacks which are getting called on call state changes, but getting the information to Java turned out to be a quite an experience, which, I thought, would be amusing to document, and, hopefully, save someone some quality bang-head-on-desk time.
1. Some of the threads calling native callbacks were initiated in Java (and thus known to JVM), and some where not, they were started in the native code. As JVM did not know anything about them, JNI calls would fail without too much explanation. Eventually I figured that out, and started attaching them as appropriate (JNI provides facilities for that).
2. Even after attaching, I could not resolve references to my classes from the native code, until I found a mysterious line in
JNI docs:
When a thread is attached to the VM, the context class loader is the bootstrap loader.. Basically, it means that when you attach a native thread, it is only capable of resolving built-in Java classes.
3. I later found a
reference to a technique, suggesting to cache a good classloader from a Java-initiated thread, and later set the classloader for the attached thread to it. I implemented that, only to find out that Android s JVM is not your regular JVM, so things are slightly different here, and this technique does not work.
4. Luckily, there is an
incredibly useful post describing the problem, and suggesting a workaround: caching an instance of needed class globally in native code, after calling
NewGlobalRef
on it, to avoid garbage collection on exit from a native function. I did that, however I only had access to the class, and Android JNI only supports calling
NewGlobalRef
on an object, so I had to create a dummy object in the native code (
JNI_OnLoad()
function, actually) and cache that.
5. That finally enabled me to call into Java from native, attached threads (w00t!). Unfortunately, you are not allowed to modify the UI (exactly what I wanted to do from my Java callback!) from a non-UI thread, which brought me right back to the drawing board.
6. As callbacks initiated from native code would not work for this reason, it was suggested to me to use a classic producer-consumer mechanism, getting Java call into the native library block until new information gets delivered by a native thread. Easily done with some pthread mutexes, and the goal is close, right?
7. Wrong! Now the problem was that if I started a UI Java thread, which would call the native function in a loop to retrieve the data (it would not actually spin, as the native function blocks until new data is available), then it would be declared not responding by the UI framework in a few seconds. I could start a non-UI thread for that, but you guessed it a non-UI thread is not allowed to update UI! Instead of resolving the problem we just shifted it into the Java land.
8. While I was simply thinking about stuffing the result into a class variable and passing it to the UI thread this way, this
tutorial, showing how to display a progress indicator while doing some heavy calculations in the background, provided a more elegant idea. One can use Android s
android.os.Handler
class to pass messages (with arbitrary payload) between threads.
That was the last piece of the puzzle, which allowed me to finally achieve a working implementation (some 10 hours or so later :-).