Communication between java and C++


#1

Hey Jucers

I know it's a noob question, but I did try  for a while and didn't manage to get this to work so...

I have a label called messageLabel defined under Juce (using IntroJucer's great graphics tools). I also have a simple method defined under a class like this:

void testClass::displayMyMessage (String myMessage)
{
    messageLabel->setText(myMessage, dontSendNotification);
}

and I have to call this method to fill in the label space FROM THE JAVA project file ("myProject.java") using (I suppose) a declaration similar to this:

public native void displayMyMessage (String myMessage);

How should I do this ? What do I need to declare in the C++ world and in the Java world ?

I will REALLY appreciate any help on this !

Thx

Phil


#2

The whole JNI thing is a huge PITA to use.. In doing the android port, I created a ton of behind-the-scenes helper macros and classes, which you can look at in juce_android_JNIHelpers.h. It's a bit scary though!


#3

Yes I agree with you re. the whole android mess... I had seen the JNIHelpers.h and... got scared just like you guessed ha ha ha !

If you could just help me on the specific case above (what do I need to include in the C++ file and in the Java file, that would get me started, I think, and I promise I wont bother you anymore with noob questions (as I know you must be pretty busy with all the excellent stuff you are putting together) !

Pleeeeease !

:)

Phil


#4

I couldn't really give you a quick summary, TBH, I can't remember all the details. I think it's just a single header that you need, but I'm sure there are plenty of JNI tutorials out there that'll explain better than I could!


#5

OK Jules. Thanks for taking the time to reply anyways.

If there is someone out there who 's done it with Jules' macros (or any other "simple" way) I am still VERY interested....


#6

Calling C++ from Java is fairly trivial TBH. The other-way is a little tougher but also not too bad. Jules has maid some great Macros for it.

 

Here is how I use my C++ function "galleryImageChose" being called from Java to C++

 

JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, galleryImageChosen, void, (JNIEnv* env, jobject activity,

                jstring url, jstring future))

{

    String imagePath = juceString (env, url);

    String orientationString = juceString (env, future);


   //internal call here

}

 

define it in Java

private native void galleryImageChosen (String url, String future);

 

Then call in Java

galleryImageChosen(mPicturePath, String.valueOf(orientation));

 

 

 

 

Going up to Java is a little tougher but here is an example.

In juce_android_JNIHelpers.h you must define your function. There are a whole lot of them to get examples from but I created a "shareImage". Find the #define JNI_CLASS_MEMBERS(METHOD, STATIMETHOD, FIELD, STATICFIELD) macro and at the bottom add a \ to add a line to the macro then put in this

METHOD (shareImage,            "shareImage",          "(Ljava/lang/String;)V")

This will define a function named "shareImage" that takes a Java::String. There are more complex options for objects passed and returned however I do not use most of them as String usually suffices for what I need done.

 

To call this function do this:

const LocalRef<jstring> t (javaString (fileName));

android.activity.callVoidMethod (JuceAppActivity.shareImage, t.get());

 

Then in your Java add:

public final void shareImage(String fileName)

{

//whatever you want here...

}

 

 

 


#7

I've tried the JUCE_JNI_CALLBACK a zillion times with various options but never got it to work. From what you are saying something is wrong in my eclipse project, I'm guessing... But now that you've confirmed it's the way to go I am going to insist...

thx a lot for your help. I will confirm when I get it to work.


#8

@vipersnake

Sorry but I don't get it... :(

1 - The makefile defines JUCE_ANDROID_ACTIVITY_CLASSNAME as the path com_etc_.JuceNative which is the name of the project path. I don't have a class named JuceNative. The class generated by Juce is called JuceNativeApplication.

2 -What if I want to define the method in another class ?

3 - Where do you put the 
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, galleryImageChosen, void, (JNIEnv* env, jobject activity, jstring url, jstring future)) {...} code ?

4- Don't you need to define a stub as well ?

Sorry again these probably are silly questions to you but I'm pretty new to this stuff so please bear with me...


#9

Ended up rewriting the equivalent of Jules macro as I don't know what to include to get JUCE_JNI_CALLBACK to be defined (I know in which file it is defined but that triggers a bunch of other includes...). The example below allowed me to display in a "Label" as defined in IntroJucer a string from the Java world in a child window (Component) called ActualWindow.

<EDIT> By using Jules' JUCE_JNI_CALLBACK macro instead of the full extern "C" declaration one can be more generic and make a better code (thank you vipersnake; see posts above using namespace juce).

My code in the  C++ world looks like this (in MainComponent.cpp):

ScopedPointer<ActualWindow> customWindow;

//==============================================================================

MainContentComponent::MainContentComponent()

{

    customWindow = new ActualWindow;

    addAndMakeVisible (customWindow);

    setSize (customWindow->getWidth(), customWindow->getHeight());

}

MainContentComponent::~MainContentComponent()

{

    customWindow = nullptr;

}

#ifdef JUCE_ANDROID
extern "C" __attribute__ ((visibility("default"))) void Java_com_yourcompany_jucenative_JuceNative_displayInLabel (JNIEnv* env, jobject activity,jstring testString)

{

    const char *inCStr = env->GetStringUTFChars(testString,nullptr);

    customWindow->label->setText (inCStr,dontSendNotification);

    env->ReleaseStringUTFChars(testString, inCStr);

}
#endif

 

and like that in the Java world JuceNative.java (JuceNative being my project name):

//==============================================================================

public final class JuceNative extends Activity

{

    //==============================================================================

    static

    {

        System.loadLibrary ("juce_jni");

    }


    private native void displayInLabel (String aString);

..............

       protected final void onLayout (boolean changed, int left, int top, int right, int bottom)

        {

            setScreenSize (getWidth(), getHeight(), getDPI());


            if (isFirstResize)

            {

                isFirstResize = false;

                callAppLauncher();

                displayInLabel("Display this!");

            }

        }

Not sure this is the right way to do it, but at least it seems to work...


#10

JUCE_ANDROID_ACTIVITY_CLASSNAME is defined in Android.mk to be your custom class name.

 

Mine is

-D "JUCE_ANDROID_ACTIVITY_CLASSNAME=com_jixipix_testapp_TestAPP"

-D JUCE_ANDROID_ACTIVITY_CLASSPATH=\"com/jixipix/testapp/TestAPP\"

 

I am surprised it works if those are not defined.

 

2) these are Global functions. In my case its in the same source as my main component and will call my MainComponent::SharedComponent()->androidImageChosen(). If you are talking in another class in Java it should not matter. I have the functions in my main activity and just call it from other classes.

 

3) Probably partially answered in 2).

4) Stub is defined in your .java class

 

Looking at your later post you seem to have it correctly. The only difference between yours and Jules cool macros is that your not getting it defined in the Android.mk class. With your code it will be specific to that project. Double check your Android.mk for your proper DEFINE using the -D and see what they are setup as, if they are setup. This way your code will be transportable to other projects.

 

 


#11

Thanks a lot for taking the time to explain.

This is now clear thanks to you.

The only remaining problem I have is what do i need to #include to be able to use JUCE_JNI_CALLBACK macro... I worked around the issue by using my own declaration but as you said I would be much better off using Jules macro. But every time I try I get an error because I did not include the right files... Basically this is the source of all my silly questions...

eg if I use 

#include "../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h"

it does compile but I get a bunch of linking errors, so I am wondering if there is a "right" include  that I need to use instead of just the include above ?

<EDIT> Here are the messages I get, apparently linked to the JNIClassBase

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function RectClass_Class::~RectClass_Class():jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:412: error: undefined reference to 'JNIClassBase::~JNIClassBase()'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function Matrix_Class::~Matrix_Class():jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:401: error: undefined reference to 'JNIClassBase::~JNIClassBase()'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function Paint_Class::~Paint_Class():jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:393: error: undefined reference to 'JNIClassBase::~JNIClassBase()'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function JuceAppActivity_Class::~JuceAppActivity_Class():jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:376: error: undefined reference to 'JNIClassBase::~JNIClassBase()'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function Matrix_Class::initialiseFields(_JNIEnv*):jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:401: error: undefined reference to 'JNIClassBase::resolveMethod(_JNIEnv*, char const*, char const*)'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function Matrix_Class::initialiseFields(_JNIEnv*):jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:401: error: undefined reference to 'JNIClassBase::resolveMethod(_JNIEnv*, char const*, char const*)'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function Paint_Class::initialiseFields(_JNIEnv*):jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:393: error: undefined reference to 'JNIClassBase::resolveMethod(_JNIEnv*, char const*, char const*)'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function Paint_Class::initialiseFields(_JNIEnv*):jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:393: error: undefined reference to 'JNIClassBase::resolveMethod(_JNIEnv*, char const*, char const*)'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function RectClass_Class::initialiseFields(_JNIEnv*):jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:412: error: undefined reference to 'JNIClassBase::resolveField(_JNIEnv*, char const*, char const*)'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function RectClass_Class::initialiseFields(_JNIEnv*):jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:412: error: undefined reference to 'JNIClassBase::resolveField(_JNIEnv*, char const*, char const*)'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function RectClass_Class::initialiseFields(_JNIEnv*):jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:412: error: undefined reference to 'JNIClassBase::resolveField(_JNIEnv*, char const*, char const*)'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function RectClass_Class::initialiseFields(_JNIEnv*):jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:412: error: undefined reference to 'JNIClassBase::resolveField(_JNIEnv*, char const*, char const*)'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function JuceAppActivity_Class::initialiseFields(_JNIEnv*):jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:376: error: undefined reference to 'JNIClassBase::resolveStaticMethod(_JNIEnv*, char const*, char const*)'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function JuceAppActivity_Class::initialiseFields(_JNIEnv*):jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:376: error: undefined reference to 'JNIClassBase::resolveStaticMethod(_JNIEnv*, char const*, char const*)'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function _GLOBAL__sub_I_MainComponent.cpp:jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:376: error: undefined reference to 'JNIClassBase::JNIClassBase(char const*)'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function _GLOBAL__sub_I_MainComponent.cpp:jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:393: error: undefined reference to 'JNIClassBase::JNIClassBase(char const*)'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function _GLOBAL__sub_I_MainComponent.cpp:jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:401: error: undefined reference to 'JNIClassBase::JNIClassBase(char const*)'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function _GLOBAL__sub_I_MainComponent.cpp:jni/../../../Source/../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h:412: error: undefined reference to 'JNIClassBase::JNIClassBase(char const*)'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function typeinfo for RectClass_Class:MainComponent.cpp(.data.rel.ro._ZTI15RectClass_Class+0x8): error: undefined reference to 'typeinfo for JNIClassBase'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function typeinfo for Matrix_Class:MainComponent.cpp(.data.rel.ro._ZTI12Matrix_Class+0x8): error: undefined reference to 'typeinfo for JNIClassBase'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function typeinfo for Paint_Class:MainComponent.cpp(.data.rel.ro._ZTI11Paint_Class+0x8): error: undefined reference to 'typeinfo for JNIClassBase'

/Applications/adt-bundle-mac-x86_64-20130729/android-ndk-r9d/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi/objs/juce_jni/__/__/__/Source/MainComponent.o: in function typeinfo for JuceAppActivity_Class:MainComponent.cpp(.data.rel.ro._ZTI21JuceAppActivity_Class+0x8): error: undefined reference to 'typeinfo for JNIClassBase'

#12

At the top right below the rest of your includes put this:

 

 

#if defined(JUCE_ANDROID)

namespace juce

{

#include "juce_android_JNIHelpers.h"

}

#endif

 

//depending on your project you may have to swap out the full path like this:

#if defined(JUCE_ANDROID)

namespace juce

{

#include "../JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h"

}

#endif

This is assuming your name space is not the same as Juce (and it most likely is not).

 

 

Without the name space items above you are running into issues since where you include the JNIHelpers.h will be without name space and all the rest of the Juce items will not be defined for that particular file.


#13

"namespace juce" did the trick. This is the info I was looking for from the beginning...

Thank you so much.

Phil


#14

Hello! I am trying to do something similar but have run into an implementation snag-- and I am completely new at this so please bear with me...

I have taken a Juce example project, 'HelloWorld', and have modified it to have the following functionality for the purposes of being able to retrieve how much memory is left for my particular app on an android device.  

- GrabMem Button: will allocate 20M of memory for an array

-FreeMem Button: will free 20M of memory

- Display: Displays how much memory the app has on that specific android device that it is running on via getNativeHeapSize, getNativeHeapFreeSize and getNativeHeapAllocatedSize.

I want to use Jules' macros to make my life easier... I have spent a lot of time researching how to do this from scratch and it's ridiculous, and now I am more confused than ever.

I don't understand where exactly everything goes.  I have tried a few options with no success... These are the files that I am using for the project.

C++ Code:     Main.cpp, mainComponent.cpp, MainComponent.h

Java:             HelloWorld.java

I'm hoping that there is an easy way to do this-- seems like there should be!!!


#15

Have a look at Vipersnake's post #6 above. It also addresses how to access Java from C++.