Another option might be to import your existing C++ code into your JUCE project (maybe wrap it in a JUCE module) and compile it for Android... that way you save messing around with static libraries which in my experience can be a bit of a pain.
Well it’s semi-true. Timur is right that a lot of JUCE code needs JUCE’s AppActivity to be the app activity of your android app. Basically (almost) any code that needs to call into java will likely fail when linked as a shared/static library.
Off the top of my head you won’t be able to use the following:
juce_graphics: all font and text stuff
juce_events: anything to do with the message loop
juce_core: anything that requires loading a URL, realtime audio threading priorities, some stuff in SystemStats (a lot of stuff in JUCE indirectly uses SystemStats )
I think most of the stuff in juce_audio_basics, juce_audio_processors and juce_dsp should be fine.
Using JUCE inside a native shared library on Android depends on how your shared library is used by your Android app. There are three common options:
Your android app is a JUCE app. Then you really shouldn’t need to do anything. Let the Projucer create your android project and simply add the shared libraries you want to use to the Projucer. Once your android project is created, you could choose to continue in Android Studio, for example.
You android app is a native kotlin/java app and directly loads your native shared library which uses JUCE. For this, you will probably have created your native android app with Android Studio. For the shared library, create a shared library JUCE project with the Projucer (or cmake). Make sure that your JUCE project compiles fine. Then your native android app will need to load the shared library from Java/Kotlin via System.loadLibrary. After this you will need to call com.roli.juce.Java.initialiseJUCE() from Java on every thread that will call into your native shared library.
Your android app is a native kotlin/java app which loads some third-party shared library which in turn loads your shared library which uses JUCE. Here, in addition to calling com.roli.juce.Java.initialiseJUCE() on every thread, you first need to call JNI_OnLoad (which is defined in JUCE) before calling anything else (including com.roli.juce.Java.initialiseJUCE()). This is because Java/Kotlin’s System.loadLibrary normally does this automatically (as in 2) but here, your shared library isn’t being loaded by Java/Kotlin but by another native library.
ok, thanks. So the situation I’m currently trying to get working is a JUCE android app and a shared library using juce. I’ve been using DynamicLibrary to load the library though - which ultimately calls dylib - are you saying that I shouldn’t be doing this but should should load the library from java?
( I don’t know exactly what the final target architecture will be yet, it’ll probably be 2 I would imagine, but would like to get this use case working as a POC)
Hmm probably a dumb question, but if your android app is already a JUCE app, why not just copy the code of your DynamicLibrary into the JUCE app’s project?
I’ve been using DynamicLibrary to load the library though - which ultimately calls dylib - are you saying that I shouldn’t be doing this but should should load the library from java?
It’s ok for you to do it this way. Then you essentially have the third case that I mentioned above, i.e. you need to call JNI_OnLoad symbol inside the loaded dynamic library (you can do this from JUCE) and then call JNIClassBase::initialiseAllClasses (env, context) once and then Thread::initialiseJUCE (env, context) for any thread that needs it. Make sure to always call the symbol inside the loaded library and not the symbol in the JUCE app’s copy of JUCE.
I’m not entirely sure that this will work smoothly though as you now have two instances of JUCE trying to register Java classes. Java will call into these classes and potentially be routed into the wrong JUCE shared library instance. I’ve never tried this before.
No, a sensible question, but we want to mimic the target environment as much as possible which will be loading a dynamic library and having to deal with the fact that it’s running juce. We want the setup to be able to run on Mac as well as Java as we’ll be doing most of our dev that way due to Android being a pain to develop with :).
Ok, looks like the piece I’ve been missing in the JNI_OnLoad. I’ve just looked at this and it requires a java VM pointer to be passed in - how do I get this in the C++ that’s loading the library to pass across to the first function call into the library?
So, heres an example of what I’m currently trying implement:
Thx, got the OnLoad working, but still don’t have a context for the call to initialiseAllClasses(), should this be passed over from the loading process also? We’re not actually going with the dynamic loading so this isn’t actually necessary any more, but would like to get it working for completeness and understanding on my part.