I am quite new to Java programming and was looking for any good examples
of calling Java functions from within C++ and how to set them up. I can
find plenty of examples for Java to call C++, just not for C++ to call
any of my Java functions. Thanks.
Kind Regards,
Andy 'FishGuy876' Kellett < http://www.andykellett.com >
--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To post to this group, send email to andro...@googlegroups.com.
To unsubscribe from this group, send email to android-ndk...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/android-ndk?hl=en.
Calling a Java function from within C++ involves a number of steps, some
of which are rather slow, so you should try to avoid it if at all possible.
Having said that, given a Java class such as:
package org.kutulu.helloworld;
public class HelloWorld {
public boolean Test ( int a, int b ) {
return a > b;
}
}
and a native jobject instance of that object called myObject, the basic
process for calling one of that object's methods is this:
jvalue args[2];
args[0].i = 1;
args[1].i = 2;
jclass cls = env->FindClass("org/kutulu/helloworld/HelloWorld");
jclass javaClass = env->NewGlobalRef(cls);
jmethodID javaMethod = env->GetMethodID(javaClass, "Test", "(II)Z");
jbool result = env->CallBooleanMethod(myObject, javaMethod, args);
See also this tutorial:
http://journals.ecs.soton.ac.uk/java/tutorial/native1.1/implementing/method.html
Pay particular attention to how references work. You'll want to cache
the results of FindClass and GetMethodID, since those calls are
time-consuming. But FindClass() returns a reference object that is only
valid until the VM decides to garbage collect it, which can happen any
time after your method returns. This is the reason for the
NewGlobalRef() call -- this creates a global copy of the local jclass
reference that will never by GC'd until you release it with FreeGlobalRef().
Thanks for the information. I spent a few hours putting together some
code last night, the only thing I can get the code to do is reset when I
call it :) I will play with it some more today and if I still can't get
it wo work, i'll post some code so someone can see any obvious mistakes
I am making. :)
Kind Regards,
Andy 'FishGuy876' Kellett < http://www.andykellett.com >
FishGuy876 of Brainstorm/BRS < http://brainstorm.untergrund.net >
I have been playing with this code and have made progress, but am still
running into a problem. It looks like the code is calling correctly, no
error messages are appearing in the logfiles, though the actual Java
function appears as though its being skipped during the call. Attached
is some code:
C++ Code (some error checking stripped):
static JavaVM *gJavaVM;
static jobject gInterfaceObject, gDataObject;
jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
dbgmsg("JNI_Onload: Calling OnLoad Function");
JNIEnv *env;
gJavaVM = vm;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK)
{
dbgmsg("** ERROR ** JNI_Onload: Error With JNI_VERSION_1_6");
return -1;
}
return JNI_VERSION_1_6;
}
void androidPlaySound(uWord SoundID)
{
int status;
JNIEnv *env;
bool isAttached = false;
dbgmsg("androidPlaySound[%d]: Calling PlaySound Code", SoundID);
status = gJavaVM->GetEnv((void **) &env, JNI_VERSION_1_6);
dbgmsg("androidPlaySound: Status Of GetEnv: %d", status);
if(status < 0)
{
dbgmsg("** ERROR ** androidPlaySound: Failed To Get JNI Environment");
status = gJavaVM->AttachCurrentThread(&env, NULL);
if(status < 0)
{
dbgmsg("** ERROR ** androidPlaysound: Failed To Attach To
Current Thread");
return;
}
isAttached = true;
}
status = gJavaVM->AttachCurrentThread(&env, NULL);
dbgmsg("androidPlaySound: Thread Attach Status: %d", status);
jclass interfaceClass =
env->FindClass("com/shoecake/MandarkTest/MandarkSound");
dbgmsg("androidPlaySound: Gotten interfaceSound Value");
if(!interfaceClass) {
dbgmsg("androidPlaySound: Failed To Get Class Reference");
if(isAttached) gJavaVM->DetachCurrentThread();
return;
}
jmethodID method = env->GetMethodID(interfaceClass, "playSound",
"(I)V");
if(!method)
{
dbgmsg("androidPlaySound: Failed To Get Object Method");
return;
}
// Break it!
env->CallVoidMethod(interfaceClass, method, SoundID);
if(isAttached) gJavaVM->DetachCurrentThread();
}
The main Sound handling Java file:
package com.shoecake.MandarkTest;
import java.util.HashMap;
import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;
import android.util.Log; // So we can log some stuff
public class MandarkSound {
private SoundPool mSoundPool;
private HashMap<Integer, Integer> mSoundPoolMap;
private AudioManager mAudioManager;
private Context mContext;
public MandarkSound()
{
// Space For Rent ;)
}
// Initialize the object with the default data needed for sound replay.
public void initSounds(Context theContext) {
mContext = theContext;
// Initialize the SoundPool stream.
//
// MaxStreams - Maximum number of streams that can be played back.
Setting this too high can affect cpu/visual performance
// StreamType - This will always be STREAM_MUSIC for games
// SourceQuality - currently unimplemented, should be left at 0
per API docs
mSoundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);
mSoundPoolMap = new HashMap<Integer, Integer>(); // Somewhere to
store the loaded objects :)
mAudioManager =
(AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE); // Audio
Device context
}
// Add a new sound to the system. First parameter is the SoundID
associated with it, which is the same
// As the Mandark SoundID. The second parameter is the Android SoundID.
public void addSound(int Index,int SoundID)
{
// This is presently hard coded for 1 sound for testing
mSoundPoolMap.put(1, mSoundPool.load(mContext, SoundID, 1));
}
// Play the specified Mandark SoundID
public void playSound(int index) {
Log.i("playSound", "playSound: Java playSound Called: "
int streamVolume =
mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
mSoundPool.play(mSoundPoolMap.get(index), streamVolume,
streamVolume, 1, 0, 1f);
}
// Play the specified Mandark SoundID with looping. This can be
combined with previous function in the
// Final version, and a Mandark tag can detect if it should loop or not.
public void playLoopedSound(int index) {
int streamVolume =
mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
mSoundPool.play(mSoundPoolMap.get(index), streamVolume,
streamVolume, 1, -1, 1f);
}
}
The class which creates the sound object:
public class MandarkTest extends Activity {
private GLSurfaceView mGLView;
private MandarkSound mMandarkSound; // Mandark Sound Manager
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initiate sound system from MandarkSound
mMandarkSound = new MandarkSound();
mMandarkSound.initSounds(getBaseContext());
// Load our sample sound /res//raw/sound.ogg
mMandarkSound.addSound(1, R.raw.sound);
// Plays tghe sound on startup to indicate it works
mMandarkSound.playSound(1);
}
... Other code
static {
System.loadLibrary("mandarkapp");
}
}
At anywhere in the c++ code, androidPlaySound(1); can be called to play
the sound file. The playSound() function writes to log when the init
sound is played, not when any subsequent sound is played, its almost
like its calling a awrong thread or I might have missed something in the
initialization. When androidPlaySound() is called, the log reports:
I/Mandark ( 208): androidPlaySound[1]: Calling PlaySound Code
I/Mandark ( 208): androidPlaySound: Status Of GetEnv: 0
I/Mandark ( 208): androidPlaySound: Thread Attach Status: 0
I/Mandark ( 208): androidPlaySound: Gotten interfaceSound Value
Any info/help on this is really appreciated, as I am completely out of
ideas to try. Why can't it correctly see the Java function? Thanks.
Kind Regards,
Andy 'FishGuy876' Kellett < http://www.andykellett.com >
FishGuy876 of Brainstorm/BRS < http://brainstorm.untergrund.net >
--
Email: an...@f1-software.com
AOL: FishGuy876 Yahoo: FishGuy876 Google: FishGuy8765
http://www.cvgm.net
Maybe I'm missing something, but it looks like you try to call a
non-static method through a *class* reference, not through the object
one.
Where do you create/obtain an *object*?
Looking through the examples given to me in this thread and what I have
found on the web, i'm not sure I need to build an object to pass as I am
simply providing a numeric value to the function without a need for any
return data. The previous samples here also make no reference to jobject
and as I understand them, they point to it's location in the java and
pass the value I specify in the args. The object examples I found on the
web look like they are for strings only.
I am still fairly new to Java, so I apologize if im missing something.
If you have an example link/info about the object, could you provide it
so I may read further as I really want to get this working. Thanks.
Kind Regards,
Andy 'FishGuy876' Kellett < http://www.andykellett.com >
I don't know what examples you mean, but lets apply the common sense:
you need an object to invoke its method, don't you?
Eg., take a look at the very 1st example on this page:
http://journals.ecs.soton.ac.uk/java/tutorial/native1.1/implementing/method.html
If this is the case, then you need to make sure your Java function is
static. Static functions are called through a reference to the class,
but for any other object you do need a reference to an *instance* of the
class.
To obtain such an instance, you can either pass one in to the JNI (as a
jobject paramter type), or create one in C++ directly. The class's
constructor is a static method called "<init>" that returns an object
type, which you can then call like any other static class method.
--Mike
I have managed to solve this problem finally :)
By changing the code in my function to the following:
jmethodID altmethod = env->GetMethodID(interfaceClass, "<init>","()V");
jobject obj = env->NewGlobalRef(env->NewObject(interfaceClass, altmethod));
jmethodID method = env->GetMethodID(interfaceClass, "playSound", "(I)V");
env->CallVoidMethod(obj, method, SoundID);
It is now working completely as expected and is playing sound :) Thanks
Mike for pointing me in that direction, and to everyone else who offered
help on this subject.
Kind Regards,
Andy 'FishGuy876' Kellett < http://www.andykellett.com >
--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk...@googlegroups.com.
To post to this group, send email to andro...@googlegroups.com.
Visit this group at http://groups.google.com/group/android-ndk.
For more options, visit https://groups.google.com/d/optout.