Search code examples
androidandroid-ndkjava-native-interfaceinstantiation

Instantiating multiple copies of a JNI library from Java


I have a JNI library that I've written to capture sensor (mostly accelerometer and gyro) data and do some feature detection algorithms on said data. The features detected are configurable via a few configuration files. When the features are detected, the JNI uses a callback to notify the java side of the application. All this works great.

Now I want to be able to have multiple instances of the same JNI library running simultaneously (so I can recognize features from multiple configuration files at once). To do this, I wrote a 'wrapper' class that implements the callbacks for the JNI library and takes care of all the initialization of the library as well. I was planning on simply instantiating this class and using each instance separately. What I've found is that while each wrapper instance is distinct, the library is reused across instances almost like it was statically declared. When I try to initialize the library from the second instance of the Wrapper class, I find it has already been initialized.

Here is a wrapper class similar to the code I've written:

public class JNIWrapper {

    public native int initializeJNI(String configPath);
    public native void endProcessing();
    public native int getInstanceIdFromJNI();

    public JNIWrapper(){
        try {
            System.loadLibrary("libjnicode.so");
        }
        catch (Exception e) {
            Log.e("JNI", "WARNING: Could not load libjnicode.so: " + e.getMessage());
        }
    }

    public int initialize(String configPath){
        return initializeJNI(configPath);
    }
    public void stop(){
        endProcessing();
    }
    public void callbackFromJNI(int output, int instanceId){
        //notify the subscribed application(s) of the feature detection
        //via message passing.
    }
}

Does anyone know how I can instantiate multiple copies of a JNI library?
Thanks!


Solution

  • You can't do that. The dynamic linker will only load a given .so file into a process once.

    Ideally you would modify the library to give it a light object-oriented style, allowing you to create instances and initialize those (rather than process-level static state) from your configuration files or calls. This isn't necessarily as complicated as it seems - basically put all your state in a struct and pass the pointer to it through all your calls. You'll have one marathon editor session resulting in a tired "paste" finger, and then some mistake cleanup. Fortunately once you remove the static variables you'll get compile errors on all remaining attempts to use them.

    A very hacky alternative might be to declare some remote-process services in your AndroidManifest.xml and load the library into each of those. Or, really breaking the android model (and at theoretical risk of random killing), load the library into multiple created-on-demand native executables.