Fill java class members with c structure members - android

I have a code something similar to this
struct time
{
long milliscnds;
int secs;
}
In my java file , I had something like this
class jtime
{
long millscnds;
int secs;
}
new jtime time = new jtime();
public int native getTimeFromC(object time);
in native class
getTimeFromc(JNIEnv* env, jobject thiz,jobject jtime)
{
struct time *mytime = getTime();
now to fill the jtime with mytime
}
Suggestions please?

You can simplify your Java class and the required JNI code.
Currently, your native method has some issues:
public int native getTimeFromC(object time);
Parameter is Object but should be jtime.
Return value doesn't seem to have a purpose.
Since the method completely initializes a jtime object, why not create and return a jtime object?
This class definition has a factory method to create the object and a constructor that moves some the initialization work over from the JNI side.
public class jtime {
long millscnds;
int secs;
public jtime(long millscnds, int secs) {
this.millscnds = millscnds;
this.secs = secs;
}
public native static jtime FromC();
}
The factory method can be implemented like this:
JNIEXPORT jobject JNICALL Java_jtime_FromC
(JNIEnv * env, jclass clazz)
{
struct time *mytime = getTime();
jmethodID ctor = (*env)->GetMethodID(env, clazz, "<init>", "(JI)V");
jobject obj = (*env)->NewObject(env, clazz, ctor, mytime->milliscnds, mytime->secs);
return obj;
}
Tip: The javap tool is like javah but shows the signatures of non-native methods. Using javap -s jtime, you can see the signature of the constructor.

Something like the following:
void getTimeFromc(JNIEnv* env, jobject thiz, jobject jtime)
{
struct time *mytime = getTime();
// now to fill the jtime with mytime
jclass jtimeClazz = (*env)->GetObjectClass(jtime); // Get the class for the jtime object
// get the field IDs for the two instance fields
jfieldID millscndsFieldId = (*env)->GetFieldID(jtimeClazz, "milliscnds", "J"); // 'J' is the JNI type signature for long
jfieldID secsFieldId = (*env)->GetFieldID(jtimeClazz, "secs", "I"); // 'I' is the JNI type signature for int
// set the fields
(*env)->SetLongField(jtime, millscndsFieldId, (jlong)mytime.milliscnds);
(*env)->SetIntField(jtime, secsFieldId, (jint)mytime.secs);
}
Ideally you should cache the values of millscndsFieldId and secsFieldId as they won't change during execution (and you could also cache jtimeClazz if you NewGlobalRef it).
All JNI functions are documented here: http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html

Related

Implementing Android Event Handlers Using C++

I have a layout design in Java that I am currently porting over to C++ via JNI. I am practically done at this point, but I am currently puzzled on how I am supposed to set up event handlers like setOnClickListener for example. I have gone through the JNI specification and have not gotten much luck.
If anyone can port the following snippet to C++ or lead me in the right direction (more reasonable due to how much code the result would be), that would be greatly appreciated.
public void setOnClickListener(boolean modification, int index, int commandIndex, final TextView textView){
final int key = index;
final int command = commandIndex;
if(modification) {
textView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
changingMenu(key, command, textView);
Runnable r = new Runnable() {
#Override
public void run() {
resetMenu(key, command, textView);
}
};
Handler h = new Handler();
h.postDelayed(r, 250);
}
});
return;
}
menuTitle.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
toggleMenu();
}
});
}
EDIT: Passing bad argument to setOnClickListener
Java
Object getProxy (MyInvocationHandler mih) {
ClassLoader classLoader = new ClassLoader() {
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return super.loadClass(name);
}
};
return java.lang.reflect.Proxy.newProxyInstance(classLoader, new Class[] { }, mih);
}
C++
jobject createProxyInstance(JNIEnv *env, jclass cls, CFunc cfunc) {
jclass cls_IH = env->FindClass("com/app/core/MyInvocationHandler");
jmethodID cst_IH = env->GetMethodID(cls_IH, "<init>", "(J)V");
jobject myIH = env->NewObject(cls_IH, cst_IH, (jlong)cfunc);
jclass klass = env->FindClass("com/app/core/Activity");
jmethodID method = env->GetMethodID(klass, "getProxy", "(Lcom/app/core/MyInvocationHandler;)Ljava/lang/Object;");
return env->CallObjectMethod(context, method, myIH); //Returning wrong object?
}
jobject aa (JNIEnv *env, jobject obj, jobject proxy, jobject method, jobjectArray args) {
__android_log_print(ANDROID_LOG_ERROR, "TEST", "SUCCESS");
}
void setListeners() {
jclass klass = env->FindClass("android/view/View");
jmethodID method = env->GetMethodID(klass, "setOnClickListener", "(Landroid/view/View$OnClickListener;)V");
klass = env->FindClass("android/view/View$OnClickListener");
env->CallVoidMethod(imageView, method, createProxyInstance(env, klass, &aa));
}
The syntax you show boils down to creating anonymous inner classes at compile time and inserting calls to create objects of these classes (with the correct variables in scope) in place of the new View.OnClickListener() { ... } expression.
I see the following two options:
For each different interface, you create a small Java class that implements the interface, with a native implementation of the interface's method(s). This is the most direct approach, but it does require you to keep the tens or hundreds of interface implementations straight.
You use java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) to dynamically create objects that implement the necessary interfaces. This will re-route each method invocation to your InvocationHandler implementation, which should be a Java class that has a native Object invoke(Object proxy, Method method, Object[] args) implementation.
To make all this reusable, you can implement this InvocationHandler to wrap a std::function object, so the final call to eg menuTitle.setOnClickListener might look like the following:
env->CallVoidMethod(menuTitle, menuTitle_setOnClickListener,
createProxyInstance(env, cls_View_OnClickListener, [](JNIEnv *env, jobject proxy, jobject method, jobjectArray args) {
...
});
For the latter solution, define the following Java class:
class MyInvocationHandler implements InvocationHandler {
private long cfunc;
MyInvocationHandler(long cfunc) { this.cfunc = cfunc; }
public native static Object invoke(Object proxy, Method method, Object[] args);
}
Which you implement on the C++ side as:
typedef jobject (*CFunc)(JNIEnv *env, jobject obj, jobject proxy, jobject method, jobjectArray args)
extern "C" jobject Java_MyInvocationHandler_invoke(JNIEnv *env, jobject obj, jobject proxy, jobject method, jobjectArray args) {
jclass cls_myIH = env->GetObjectClass(obj);
jfieldID fld_myIH_cfunc = env->GetFieldID(cls_myIH, "cfunc", "J");
CFunc cfunc = (CFunc)env->GetLongField(obj, fld_myIH_cfunc);
cfunc(env, proxy, method, args);
return nullptr;
}
Finally, we can implement createProxyInstance as follows:
jobject createProxyInstance(JNIEnv *env, jclass cls, CFunc cfunc) {
jclass cls_IH = env->GetClass("MyInvocationHandler");
jmethodID cst_IH = env->GetMethodID(cls_ID, "<init>", "(J)V");
jobject myIH = env->NewObject(cls_ID, cst_IH, (jlong)cfunc);
// now call Proxy.createProxyInstance with this object as InvocationHandler
}

How to call JNI Function from a method in C library

I'm currently developping an android app with android studio, for this project i need to use a custom library written in c/c++ .
So i in order to do that i needed to use NDK.
The library contain methods that i need to implements in order to have access to specifics fonctions in android
My question is : how can I call my jni method inside the method existing in c
which will call a java method to store session key in the android system
So for a pratical exemple since it's maybe not clear:
In a file called lib_exemple.c i have three methods
the first one is the initialisation call to the library (jni to library c) -->working
JNIEXPORT void JNICALL
Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1init
(JNIEnv *env, jobject instance) {
lib_Example_init('0'); // c func of the library
}
then the second one is the jni who call a java method (jni to java) -> working
JNIEXPORT jint
JNICALL Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1store_1session_1key
(JNIEnv * env, jobject jobject1, jbyte jbyte1, jchar jchar1, jbyte jbyte2){
jclass clazz = (*env)->FindClass(env, "com/example/libExemple/Libs/ExempleLib");
jmethodID mCurrentActivityId = (*env)->GetMethodID(env, clazz, "KeyStoreSessionKey", "(BCB)I");
jint result = (*env)->CallIntMethod(env, jobject1, mCurrentActivityId, jbyte1, jchar1, jbyte2);
return result;
}
and the third method is the one the library c have in it (library C to jni)
int lib_Exemple_store_session_key(uint8_t Session, P_KEY_ST_T pKey, uint8_t keyType) {
//i want to call jni func here , so the librairy can access the native android function
return 0;
}
then to configure the ndk in a file called ExempleLib.java i have defined
static {
System.loadLibrary("LibExemple");
}
then the prototype of the initialisation of the library
public native void libExemple_init();
the prototype of the first method in lib_exemple.c
public native int lib_Exemple_store_session_key(byte pKey, char key_length, byte keyIndex);
and the java fonction called by it
protected int KeyStoreSessionKey(byte pKey, char key_length, byte keyIndex) {
...
}
my mainActivity contain
ExempleLib LibFunc = new ExempleLib();
Log.d(TAG, "init lib" );
LibFunc.lib_Exemple_init();
My goal is that after initialising my library and call an init function , it can call the first method in the second method of lib_exemple.c (the one used in the library )
thanks
EDIT :
The solution of my problem is when i initialize the lib , i need to save jvm
so i can access the JNIenv in the library method directly
jobject Savedinstance = NULL;
static JavaVM *cachedJVM;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
cachedJVM = jvm;
return JNI_VERSION_1_6;
}
JNIEnv *AttachJava() {
JavaVMAttachArgs args = {JNI_VERSION_1_6, 0, 0};
JNIEnv *java;
(*cachedJVM)->AttachCurrentThread(cachedJVM, &java, &args);
return java;
}
JNIEXPORT jint JNICALL
Java_com_example_vgosselin_libExemple_Libs_ExempleLib_lib_1init(JNIEnv *env,
jobject instance) {
Savedinstance = instance;
void *element = 0;
jint result;
result = lib_Exemple_init_();
return result;
}
so i can call the java method in
int lib_Exemple_store_session_key(uint8_t Session, P_KEY_ST_T pKey, uint8_t keyType) {
JNIEnv *NewEnv = AttachJava();
jclass clazz = (*NewEnv)->FindClass(NewEnv,"com/example/vgosselin/libExemple/Libs/ExempleLib");
jmethodID mCurrentActivityId = (*NewEnv)->GetMethodID(NewEnv,clazz,"KeystoreStoreKey","([BCB)I");
jint result;
jbyteArray Data = 0;
jchar Key = 0;
jbyte Type = 0;
result = (*NewEnv)->CallIntMethod(NewEnv, Savedinstance, mCurrentActivityId, Data, Key, Type);
return result;
}
and now i don't need the method in jni syntax anymore
JNIEXPORT jint JNICALL Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1store_1session_1key

How to invoke C++ methods with function pointers as arguments from Java (Android)

I need to integrate C++ files into my Android application project.
I can build the files and .so file is generated.
This is the header file which has the function process().
I need to invoke this method from my .java file.
class PayrollGenerator {
public:
typedef void (* PAYROLL_READY_CALLBACK) (std::vector<int> list, int id);
typedef void (* PROGRESS_CALLBACK) (int progress);
private:
PAYROLL_READY_CALLBACK _dataReadyCallback;
PROGRESS_CALLBACK _progressCallback;
public:
DataProcessor(PAYROLL_READY_CALLBACK dataReadyCallback, PROGRESS_CALLBACK progressCallback);
void process(int data);
};
Two callbacks are there which will give me result and the progress data of the data being processed.
I am not able to design the JNI methods for it.
BTW, I am run simple C++ programs from .java files.
But this one is quite complex to me.
Please help !!
Progress -
I created a C++ file and wrote the wrapper
JNIEnv *global;
jobject impl;
struct DataProcessorStruct {
jobject callback_ptr;
DataProcessor *dataProcessor;
};
void dataReadyCallback(std::vector<jint> processedSamples, jint heartRate){
jintArray arr = global->NewIntArray( processedSamples.size() );
global->SetIntArrayRegion( arr, 0, processedSamples.size(), ( jint * ) &processedSamples[0] );
jclass clazz = global->FindClass("com/app/AudioActivity");
jmethodID method = global->GetMethodID(clazz, "dataReady","[Ljava/util/List;I)V");
global->CallVoidMethod(impl,method,arr,heartRate);
__android_log_print(ANDROID_LOG_INFO, "test", "C-dataReadyCallback");
}
void progressCallback(jint progress){
jclass clazz = global->FindClass("com/app/AudioActivity");
jmethodID method = global->GetMethodID(clazz, "dataProcessProgress","(I)V");
global->CallVoidMethod(impl, method,progress);
__android_log_print(ANDROID_LOG_INFO, "test", "C-progressCallback");
}
JNIEXPORT jlong JNICALL Java_com_app_AudioActivity_createDataProcessorObject (JNIEnv * env, jobject obj){
global = env;
impl = obj;
DataProcessorStruct *cpp_obj = new DataProcessorStruct();
if (cpp_obj == NULL) {
return 0;
}
DataProcessor *csObj = new DataProcessor(dataReadyCallback,progressCallback);
if (csObj == NULL) {
return 0;
}
cpp_obj->dataProcessor = csObj;
cpp_obj->callback_ptr = env->NewGlobalRef(obj);
return (jlong)cpp_obj;
}
JNIEXPORT void JNICALL Java_com_app_processData (JNIEnv * env, jobject obj,jlong objId,jint sample,jint dataLeft){
impl = obj;
DataProcessorStruct *cpp_obj = (DataProcessorStruct *)objId;
DataProcessor *dataProcessor=cpp_obj->dataProcessor;
if (dataProcessor != NULL){
dataProcessor->process(sample,dataLeft);
}
}
The native methods in Java are -
public native long createDataProcessorObject();
public native void processData(long dataProcessor,int sample, int dataLeft);
Is this the right way of doing so ?
And is there any way I don't have to call class Java methods directly from dataReadyCallback() and progressCallback() C++ methods but somehow I can call interface methods which are in Java to get invoked from these C++ methods so that any class listening to these callbacks should get notified and not a particular class?
You can easily define native functions in Java wich are callbacks in your C++ programme. Usually you start declaring a class and some functions in java:
class MyJNative {
static { // static bloc, executed when class is loaded => load your cpp library
System.out.print ("Load library in ");
System.out.println(System.getProperty("java.library.path"));
System.loadLibrary("MyNativeCPP");
}
private static int next_id=1; // every object gets a unique id
private int id; // unique object id
public MyJNative () { // constructor (sets unique id)
id = next_id++;
// ...
}
public native static void doSetup(); // initialisation for your factory function
public native void doSomething(); // example method
...
}
Then you'll let javah generate the header for the native funtions in C++. You shouldn't hand-write the header yourself ! Here a step by step tutorial, and the example for the code above:
...
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: MyJNative
* Method: doSetup
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_MyJNative_doSetup
(JNIEnv *, jclass);
/*
* Class: MyJNative
* Method: doSomething
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_MyJNative_doSomething
(JNIEnv *, jobject);
...
#ifdef __cplusplus
}
#endif
#endif
As you see, JNI is designed to call C++/C functions and not member functions of C++ class. So you need to define some global functions (or static member functions of your class), that will get as a (first) parameter some ID which can identify the C++ object, and will then forward the call to this object.
The example above is simplified, but the idea would be that, the Java class maintains a unique ID. You could imagine that at creation, after setting the ID, it calls a factory function in C++, that would maintain a map<jint, unique_ptr<MyCPPclass>> objects so that all other functions like doSomething() can be invoked with ID as parameter and then call objects[ID].get()->doSomething().
The problem with this approach is the life of your C++ object: you manage it's destruction: C++ objects created on the C++ side won't be garbage collected, so it's difficult to guess when they are no longer needed.
A variant of this approach would be to host ther you "host it" somewhere in a byte array of a java object. But in this case the problem is the other way round: when the Java object is no longer needed, the C++ side won't be informed that the object is destructed and so the destructor won't be called.
As you can see, the JNI design makes it rather difficult to manage objects on both sides. So the safest approach is to manage objects on one side only and on the other side expose only functions that will use/manipulate the object.

JNI: Segfaults when calling Java callbacks from C

I a Library in C that I'm leveraging for an Android application. This library has an audio stream that it occasionally flushes. When this happens it calls a write callback function of my design.
My intent is to have that C callback call a method on a specific Java Object which will handle stuff with the strem.
Currently I have code like so:
methodID compressionHandler=0;
jobject compressionHandlerClass;
int audioBufferChunkSize;
static JavaVM *gJavaVM;
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
gJavaVM = vm;
return JNI_VERSION_1_6;
}
JNIEXPORT void JNICALL
Java_com_my_code_init(JNIEnv* env, jobject obj, /*classpath of the class we want to call against*/jstring compressedAudioHandlerPath, /*class instance we want to call against*/jobject callbackClass) {
......
// this is a global ref as per:
//http://stackoverflow.com/questions/14765776/jni-error-app-bug-accessed-stale-local-reference-0xbc00021-index-8-in-a-tabl
compressionHandlerClass = (*env)->NewGlobalRef(env,callbackClass);
// name of the class
const char *classLocation;
// convert jString to c String
classLocation = (*env)->GetStringUTFChars( env, compressedAudioHandlerPath , NULL ) ;
// tmp variable for holding the class location, relates to the above issue with garbage collection
jclass clazz = (*env)->FindClass(env, classLocation);
// the actual method that we want to call, this gets used in the writeCallback
compressionHandler = (*env)->GetMethodID(env, clazz, "handleCompressedAudio", "([B)V");
......
}
The callback method looks like so:
void writeCallback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data) {
JNIEnv *env;
int isAttached = 0;
if ((status = (*gJavaVM)->GetEnv(gJavaVM, (void**)&env, JNI_VERSION_1_6)) < 0) {
if ((status = (*gJavaVM)->AttachCurrentThread(gJavaVM, &env, NULL)) < 0) {
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
isAttached = 1;
}
if(*env!=0 && compressionHandler!=0){
jbyteArray arr = (*env)->NewByteArray(env,bytes);
(*env)->SetByteArrayRegion(env,arr, 0, bytes, (jbyte*)buffer);
(*env)->CallVoidMethod(env,compressionHandlerClass, compressionHandler,arr);
free(arr);
free(env);
free(isAttached);
}
}
I'm getting crashes at the CallVoidMethod, that signature of which is an interface implemented by whatever object I pass in:
public interface CompressedAudioHandler {
void handleCompressedAudio(byte[] buff);
}
I suspect that I am improperly attaining/keep references to these objects, but I haven't found a great way to handle that. Any advice on how I can more correctly handle this?

Issue with instance and class variable states when using JNI

I'm currently writing some Android code that uses JNI and I'm having difficulty how class and instance variables work. If I execute the following code I would expect the code to print a value of "18" but I always receive a value of "0". Can someone explain what I'm doing wrong?
// Java code
SampleClass sc = new SampleClass(18);
sc.printId() // returns 18, as expected
sc.nativePrintId() // returns 0, why?!
// Java Class
public class SampleClass
{
private int mId = -1;
public FFmpegMediaPlayer(int id) {
mId = id;
}
public void printId() {
System.out.println("id: " + mId);
}
public native void nativePrintId();
}
// JNI C++ code
static void nativePrintId(JNIEnv* env, jobject thiz)
{
jclass clazz = env->FindClass("wseemann/media/SampleClass");
jmethodID printId = env->GetMethodID(clazz, "printId", "()V");
env->CallVoidMethod(clazz, printId); // always prints zero?
}
You must pass the object, not the class, to CallVoidMethod.
Use:
env->CallVoidMethod(thiz, printId);
Also, you should get the class from the object, not from FindClass.
Use:
jclass clazz = env->GetObjectClass(thiz);

Categories

Resources