Juce Demo using Android Studio (solved !)


#1

(I chose to create a new post for this to start from a clean sheet...)


=====================================================
Preliminary notes:
=====================================================
- This is definitely NOT the right way (nor the only one) to make it work, but I will leave that to the smart Juce/Roli engineers... Until they do it right, the purpose here is just to give a "manual" solution to be able to compile the Juce Demo project as produced by IntroJucer using Android Studio and run it on a Nexus 10 (Android 4.4.4 - API 19). A lot of what I've done here may obviously be optimized and automated.
- My dev setup is a Mac mini (MacOSX 10.10.3) with the latest android SDK (rev 24.2.0) and NDK (rev 10e), and of course Android Studio (1.2.1.1).
- I picked an arbitrary folder structure to put the source files (see below). This may be changed at your own risks. I would however recommend that you start with this one for testing purposes...
=====================================================
IntroJucer
=====================================================
Open JuceDemo.jucer
Set Modules folder
Copy modules locally
Set ndk and sdk paths:
/PathToYourDevTools/android-sdk-macosx
/PathToYourDevTools/android-ndk-r10e
Minimum SDK version: 19
Enable c++11flag
Architectures: armeabi-v7a
Save Project and Open in MacOSX
Run under XCode (Mac) just to make sure nothing is missing
=====================================================
Android Studio
=====================================================
Import Eclipse project from Builds/Android folder (leave all options checked) to a JuceDemo folder somewhere you like...
Copy JuceDemo/Source folder under app/src folder
Copy JuceLibraryCode folder under app folder
=====================================================
Modify paths in android.mk (note that this will be needed every time you save IntroJucer project unless you use a batch file to copy it before launching compile). This step consists only in modifying the "../" in the paths :
=====================================================


LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
ifeq ($(TARGET_ARCH_ABI), armeabi-v7a)
    LOCAL_ARM_MODE := arm
endif
LOCAL_MODULE := juce_jni
LOCAL_SRC_FILES := \
  ../../Source/Main.cpp\
  ../../Source/MainWindow.cpp\
  ../../Source/IntroScreen.cpp\
  ../../Source/Demos/AnimationDemo.cpp\
  ../../Source/Demos/AudioLatencyDemo.cpp\
  ../../Source/Demos/AudioPlaybackDemo.cpp\
  ../../Source/Demos/AudioRecordingDemo.cpp\
  ../../Source/Demos/AudioSettingsDemo.cpp\
  ../../Source/Demos/AudioSynthesiserDemo.cpp\
  ../../Source/Demos/Box2DDemo.cpp\
  ../../Source/Demos/CameraDemo.cpp\
  ../../Source/Demos/ChildProcessDemo.cpp\
  ../../Source/Demos/CodeEditorDemo.cpp\
  ../../Source/Demos/ComponentTransformsDemo.cpp\
  ../../Source/Demos/CryptographyDemo.cpp\
  ../../Source/Demos/DialogsDemo.cpp\
  ../../Source/Demos/FontsDemo.cpp\
  ../../Source/Demos/GraphicsDemo.cpp\
  ../../Source/Demos/ImagesDemo.cpp\
  ../../Source/Demos/JavaScript.cpp\
  ../../Source/Demos/KeyMappingsDemo.cpp\
  ../../Source/Demos/LiveConstantDemo.cpp\
  ../../Source/Demos/LookAndFeelDemo.cpp\
  ../../Source/Demos/MDIDemo.cpp\
  ../../Source/Demos/MidiDemo.cpp\
  ../../Source/Demos/MultithreadingDemo.cpp\
  ../../Source/Demos/MultiTouch.cpp\
  ../../Source/Demos/NetworkingDemo.cpp\
  ../../Source/Demos/OpenGLDemo.cpp\
  ../../Source/Demos/OpenGLDemo2D.cpp\
  ../../Source/Demos/PropertiesDemo.cpp\
  ../../Source/Demos/SystemInfoDemo.cpp\
  ../../Source/Demos/TimersAndEventsDemo.cpp\
  ../../Source/Demos/UnitTestsDemo.cpp\
  ../../Source/Demos/ValueTreesDemo.cpp\
  ../../Source/Demos/VideoDemo.cpp\
  ../../Source/Demos/WebBrowserDemo.cpp\
  ../../Source/Demos/WidgetsDemo.cpp\
  ../../Source/Demos/WindowsDemo.cpp\
  ../../Source/Demos/XMLandJSONDemo.cpp\
  ../../../JuceLibraryCode/BinaryData.cpp\
  ../../../JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp\
  ../../../JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp\
  ../../../JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.cpp\
  ../../../JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.cpp\
  ../../../JuceLibraryCode/modules/juce_audio_utils/juce_audio_utils.cpp\
  ../../../JuceLibraryCode/modules/juce_box2d/juce_box2d.cpp\
  ../../../JuceLibraryCode/modules/juce_core/juce_core.cpp\
  ../../../JuceLibraryCode/modules/juce_cryptography/juce_cryptography.cpp\
  ../../../JuceLibraryCode/modules/juce_data_structures/juce_data_structures.cpp\
  ../../../JuceLibraryCode/modules/juce_events/juce_events.cpp\
  ../../../JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp\
  ../../../JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.cpp\
  ../../../JuceLibraryCode/modules/juce_gui_extra/juce_gui_extra.cpp\
  ../../../JuceLibraryCode/modules/juce_opengl/juce_opengl.cpp\
  ../../../JuceLibraryCode/modules/juce_video/juce_video.cpp\
ifeq ($(NDK_DEBUG),1)
  LOCAL_CPPFLAGS += -fsigned-char -fexceptions -frtti -g -I "../../../JuceLibraryCode" -I "../../../JuceLibraryCode/modules" -O0 -std=c++11 -std=gnu++11 -D "JUCE_ANDROID=1" -D "JUCE_ANDROID_API_VERSION=19" -D "JUCE_ANDROID_ACTIVITY_CLASSNAME=com_juce_jucedemo_JuceDemo" -D JUCE_ANDROID_ACTIVITY_CLASSPATH=\"com/juce/jucedemo/JuceDemo\" -D "DEBUG=1" -D "_DEBUG=1" -D "JUCE_UNIT_TESTS=1" -D "JUCER_ANDROID_7F0E4A25=1" -D "JUCE_APP_VERSION=3.0.0" -D "JUCE_APP_VERSION_HEX=0x30000"
  LOCAL_LDLIBS := -llog -lGLESv2
  LOCAL_CFLAGS += -fsigned-char -fexceptions -frtti -g -I "../../../JuceLibraryCode" -I "../../../JuceLibraryCode/modules" -O0 -std=c++11 -std=gnu++11 -D "JUCE_ANDROID=1" -D "JUCE_ANDROID_API_VERSION=19" -D "JUCE_ANDROID_ACTIVITY_CLASSNAME=com_juce_jucedemo_JuceDemo" -D JUCE_ANDROID_ACTIVITY_CLASSPATH=\"com/juce/jucedemo/JuceDemo\" -D "DEBUG=1" -D "_DEBUG=1" -D "JUCE_UNIT_TESTS=1" -D "JUCER_ANDROID_7F0E4A25=1" -D "JUCE_APP_VERSION=3.0.0" -D "JUCE_APP_VERSION_HEX=0x30000"
  LOCAL_LDLIBS := -llog -lGLESv2
else
  LOCAL_CPPFLAGS += -fsigned-char -fexceptions -frtti -I "../../../JuceLibraryCode" -I "../../../JuceLibraryCode/modules" -O3 -std=c++11 -std=gnu++11 -D "JUCE_ANDROID=1" -D "JUCE_ANDROID_API_VERSION=19" -D "JUCE_ANDROID_ACTIVITY_CLASSNAME=com_juce_jucedemo_JuceDemo" -D JUCE_ANDROID_ACTIVITY_CLASSPATH=\"com/juce/jucedemo/JuceDemo\" -D "NDEBUG=1" -D "JUCE_UNIT_TESTS=1" -D "JUCER_ANDROID_7F0E4A25=1" -D "JUCE_APP_VERSION=3.0.0" -D "JUCE_APP_VERSION_HEX=0x30000"
  LOCAL_LDLIBS := -llog -lGLESv2
  LOCAL_CFLAGS += -fsigned-char -fexceptions -frtti -I "../../../JuceLibraryCode" -I "../../../JuceLibraryCode/modules" -O3 -std=c++11 -std=gnu++11 -D "JUCE_ANDROID=1" -D "JUCE_ANDROID_API_VERSION=19" -D "JUCE_ANDROID_ACTIVITY_CLASSNAME=com_juce_jucedemo_JuceDemo" -D JUCE_ANDROID_ACTIVITY_CLASSPATH=\"com/juce/jucedemo/JuceDemo\" -D "NDEBUG=1" -D "JUCE_UNIT_TESTS=1" -D "JUCER_ANDROID_7F0E4A25=1" -D "JUCE_APP_VERSION=3.0.0" -D "JUCE_APP_VERSION_HEX=0x30000"
  LOCAL_LDLIBS := -llog -lGLESv2
endif
include $(BUILD_SHARED_LIBRARY)


=====================================================
Modify build.gradle
=====================================================


apply plugin: 'com.android.application'
android {
    compileSdkVersion 19
    buildToolsVersion "22.0.1"
    defaultConfig {
        applicationId "com.juce.jucedemo"
        minSdkVersion 19
        targetSdkVersion 19
        ndk {
            moduleName "juce_jni"
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }
    sourceSets.main {
        jniLibs.srcDir 'src/main/libs' //set libs as .so's location instead of jniLibs
        jni.srcDirs = [] //disable automatic ndk-build call with auto-generated Android.mk
    }
    // call regular ndk-build(.cmd) script from app directory
    task ndkBuild(type: Exec) {
        commandLine '/PathToYourDevTools/android-ndk-r10e/ndk-build', '-C', file('/PathToYourAndroidStudioProjects/JuceDemo/app/src/main/jni')
    }
    tasks.withType(JavaCompile) {
        compileTask -> compileTask.dependsOn ndkBuild
    }
}

Tools/Android/Sync project with gradle file
Build/Re-build project
If no error (Build successful) :
Run...
Note that even the OpenGL demo works rather smoothly...
Please don't hesitate to add comments, in particular if you found easy shortcuts or details to make it work on other platforms (or on simulator).
 


#2

Thanks for this, will try it out next time I need to tweak some Java / Android code :)


#3

Hi!

 

does GBD/LLDB debugging works ?


#4

Not tried yet. But I don't see why it wouldn't worked (...uh... well except that Android tools usually don't work very well...)

:)

I'm busy on other stuff right now but would be interested too to know if anyone tried that.


#5

 

I'm following along with these instructions.  I get to the point were it is building the project but then I get the following error when it gets to juce_core.cpp:

In file included from /Users/nelsbruckner/StudioProjects/AndroidDemo/app/src/main/jni/../../../JuceLibraryCode/modules/juce_core/juce_core.cpp:187:0:
/Users/nelsbruckner/StudioProjects/AndroidDemo/app/src/main/jni/../../../JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h: In static member function 'static void juce::Thread::setCurrentThreadAffinityMask(juce::uint32)':
/Users/nelsbruckner/StudioProjects/AndroidDemo/app/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h
Error:(965, 74) error: 'pthread_setaffinity_np' was not declared in this scope
     pthread_setaffinity_np (pthread_self(), sizeof (cpu_set_t), &affinity);
                                                                          ^
make: *** [/Users/nelsbruckner/StudioProjects/AndroidDemo/app/src/main/obj/local/armeabi/objs/juce_jni/__/__/__/JuceLibraryCode/modules/juce_core/juce_core.o] Error 1
make: Leaving directory `/Users/nelsbruckner/StudioProjects/AndroidDemo/app/src/main/jni'
:app:ndkBuild FAILED
Error:Execution failed for task ':app:ndkBuild'.
> Process 'command '/Users/nelsbruckner/Library/Android/ndk/ndk-build'' finished with non-zero exit value 2
 

[UPDATE:]

If I remove the macro for enabling processor affinity the demo does build with no errors.  However, it does not run (at least on the emulator).

 

--Nels


#6

My procedure works for a hardware platform (Nexus 10). I have tried it with several of my own projects (in addition to Juce Demo), but not with the simulator.

Are you sure that you have the current version of Juce ? The code in the setCurrentThreadAffinityMask method (which  you can disable with "#define SUPPORT_AFFINITIES 0") looks like this in my code:

   #if SUPPORT_AFFINITIES
    cpu_set_t affinity;
    CPU_ZERO (&affinity);

    for (int i = 0; i < 32; ++i)
        if ((affinityMask & (1 << i)) != 0)
            CPU_SET (i, &affinity);

    /*
       N.B. If this line causes a compile error, then you've probably not got the latest
       version of glibc installed.

       If you don't want to update your copy of glibc and don't care about cpu affinities,
       then you can just disable all this stuff by setting the SUPPORT_AFFINITIES macro to 0.
    */

    sched_setaffinity (getpid(), sizeof (cpu_set_t), &affinity);
    sched_yield();

   #else

    /* affinities aren't supported because either the appropriate header files weren't found,
       or the SUPPORT_AFFINITIES macro was turned off
    */
    jassertfalse;
    (void) affinityMask;
   #endif

Also note that you have to get the latest version of glibc.


#7

 

     I discovered my error - the only emulator target set up by default was an x86 target.  Creating a non x86 target got it running in the emulator (albeit very slowly).  Also, was able to run on hardware (a Nexus 7, and again, performance was not good, but it did run).

 

     Not sure why performance on hardware was so bad.  Perhaps the lack of compiled affinity support is having an impact...?

 

     Is glibc distributed with the Android SDK/NDK?  If so I have the latest.

 

     --Nels


#8

Did you compile as release or debug? Debug compilation results in very poor performance. This took me a while to work out. I was trying all sorts of optimisations... until I tried a release compile, when everything ran super fast !! :)


#9

For now I only used Debug. As I said even the OpenGL demo works smoothly.

Are you running on hardware ? I only tested on Nexus 10 tablet, which is pretty high-end as an Android platform.

<EDIT> btw this procedure seems to only produce a debug version, while the gradle file states a release version... This means that this particular option (BuildTypes field) probably is overwritten somewhere else in the process.

The only way I found to generate a Release apk was to use the Build/Generate Signed APK  menu and enter the default defined in IntroJucer... but to tell the truth I don't think that the generated codes are different (about same size apk files) ...

I'm sorry to be so vague but I really don't have the time to search deeper unfortunately. I was just happy to get it to WORK and share those results with you guys. But clearly this needs further investigation to make a clear (and clean) procedure.