I got the following code inside my templateApp.java:
package com.android.templateApp;
import android.app.Activity;
import android.os.Bundle;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
public class templateApp extends Activity implements SensorEventListener
{
GL2View mView;
SensorManager mSensorManager;
#Override protected void onCreate(Bundle icicle)
{
super.onCreate( icicle );
mSensorManager = ( SensorManager ) getSystemService( SENSOR_SERVICE );
mView = new GL2View( getApplication() );
mSensorManager.registerListener( this,
mSensorManager.getDefaultSensor( SensorManager.SENSOR_ACCELEROMETER ),
SensorManager.SENSOR_DELAY_GAME );
setContentView( mView );
}
#Override protected void onResume()
{
super.onResume();
mView.onResume();
}
public static native void Accelerometer( float x, float y, float z );
public void onSensorChanged( SensorEvent event )
{
float x = event.values[ SensorManager.DATA_X ],
y = event.values[ SensorManager.DATA_Y ],
z = event.values[ SensorManager.DATA_Z ];
// ALWAYS CRASH HERE!!!!
Accelerometer( x, y, z );
}
public void onAccuracyChanged( Sensor sensor, int arg1 ) {}
}
And inside my templateApp.cpp I got the following to bridge the Accelerometer function:
extern "C"
{
JNIEXPORT void JNICALL Java_com_android_templateApp_GL2View_Init( JNIEnv *env, jobject obj, jint width, jint height );
JNIEXPORT void JNICALL Java_com_android_templateApp_templateApp_Accelerometer( JNIEnv *env, jfloat x, jfloat y, jfloat z );
};
JNIEXPORT void JNICALL Java_com_android_templateApp_GL2View_Init( JNIEnv *env, jobject obj, jint width, jint height )
{ Init( width, height); }
JNIEXPORT void JNICALL Java_com_android_templateApp_templateApp_Accelerometer( JNIEnv *env, jfloat x, jfloat y, jfloat z )
{ Accelerometer( x, y,z ); }
When I pass my mouse over in eclipse it show that the call belong to
com.android.templateApp.templateApp.Accelerometer, which is exactly what I declare, however it always crash reporting an UnsatisfiedLinkError.
I tried every combination (assuming that my native definition is wrong) but it keep crashing at the Accelerometer call inside the java code... Weird thing is that my init function that is inside my GL2View.java work flawless. Is there anything that prevent me to call a native API from the main file that extends Activity? Cuz the sensor work, I print the value its the native call that crash and I don't know why.
Tks in advance!
Have you initiated your native code? This is mine, it located in the class with native methods exposed.
static {
Log.v("NativeCodeWrapper.java", "Loading native libraries");
System.loadLibrary("NameOfNativeModuleAsDefinedInAndroidDotMk");
}
You forgot to add implicit jclass/jobject argument to your JNI function:
JNIEXPORT void JNICALL Java_com_android_templateApp_templateApp_Accelerometer( JNIEnv *env, jclass clazz, jfloat x, jfloat y, jfloat z );
'clazz' argument will be the Java class of the calling class (templateApp in your case).
For non-static functions implicit argument will be jobject of the calling object.
Related
I want return a pointer to a double array in JNI, and then, use this values in Java's code. So i did this:
JNIEXPORT jlong JNICALL Java_com_sistoleaudiocapture_Processing_prueba_1nativa(
JNIEnv * env, jclass, jlong retorno, jbyteArray data, jint lenbytes) {
//PROCESS
long dirt;
dirt=(long)d_est;
return(dirt);
}
In my java funcition:
public void prueba(byte[] data, int lenbytes) {
prueba=prueba_nativa(retorno, data, lenbytes);
}
So now, How can I acess to my values?
Thanks
You can either create additional functions to access the array on the native side, exposing them to Java. Or you can create a jDoubleArray within your native code, and return that to Java.
const double * arrayPtr = (const double *)&yourDoubleArray;
jint lengthOfArray = 0; //Fill dynamically with the length of your native double array.
jdoubleArray doubleArray = (*env)->NewDoubleArray(env, lengthOfArray);
(*env)->SetDoubleArrayRegion( env, doubleArray, 0, 16, arrayPtr);
return doubleArray;
I'm trying to learn the basics of Android NDK but I'm stucked when I have to use it with a c++ class.
I understand how to use it with a simple function but what should I do to be able to manipulate the fields and the methods of a c++ class ?
I'm trying to do it with this simple c++ class :
#include <cstdlib>
#include <jni.h>
using namespace std;
class Point {
int x, y; // coordonnées du point
public:
Point() {
this->x = 0;
this->y = 0;
}
Point(int x, int y) {
this->x = x;
this->y = y;
}
int getX() const {
return x;
}
int getY() const {
return y;
}
Point symetrique() const {
return Point(-x, -y);
}
bool operator ==(const Point &p) const {
return this->x == p.getX() && this->y == p.getY();
}
};
extern "C" {
JNIEXPORT jlong JNICALL Java_com_example_jnipoint_JPoint_createPoint__
(JNIEnv *, jobject);
JNIEXPORT jlong JNICALL Java_com_example_jnipoint_JPoint_createPoint__II
(JNIEnv *, jobject, jint, jint);
JNIEXPORT jint JNICALL Java_com_example_jnipoint_JPoint_nativeGetX
(JNIEnv *, jobject, jlong);
JNIEXPORT jint JNICALL Java_com_example_jnipoint_JPoint_nativeGetY
(JNIEnv *, jobject, jlong);
JNIEXPORT jlong JNICALL Java_com_example_jnipoint_JPoint_nativeSymetrique
(JNIEnv *, jobject, jlong);
};
JNIEXPORT jlong JNICALL Java_com_example_jnipoint_JPoint_createPoint__(JNIEnv* env, jobject thiz) {
return (jlong)(new Point());
}
JNIEXPORT jlong JNICALL Java_com_example_jnipoint_JPoint_createPoint__II(JNIEnv* env, jobject thiz, jint x, jint y) {
return (jlong)(new Point(x, y));
}
JNIEXPORT jint JNICALL Java_com_example_jnipoint_JPoint_nativeGetX(JNIEnv* env, jobject thiz, jlong nativePointer) {
return ((Point*)nativePointer)->getX();
}
JNIEXPORT jint JNICALL Java_com_example_jnipoint_JPoint_nativeGetY(JNIEnv* env, jobject thiz, jlong nativePointer) {
return ((Point*)nativePointer)->getY();
}
jlong Java_com_example_jnipoint_JPoint_nativeSymetrique(JNIEnv* env, jobject thiz, jlong nativePointer) {
return ((Point*)nativePointer)->symetrique();
}
I tried to find samples but nothing so far... Maybe I'm not using the right keywords
* UPDATE *
I created a Java wrapper for the c++ Point class and added to the c++ file JNI methods. The code is the following :
public class JPoint {
private long nativePointer;
public JPoint() {
nativePointer = createPoint();
}
public JPoint(int x, int y) {
nativePointer = createPoint(x, y);
}
public int getX() {
return nativeGetX(nativePointer);
}
public int getY() {
return nativeGetY(nativePointer);
}
public JPoint symetrique() {
JPoint tmp = new JPoint();
tmp.nativePointer = nativeSymetrique(nativePointer);
return tmp;
}
// TODO
/*public boolean equals(Object o) {
return nativeEquals(o);
}*/
private native long createPoint(); // Void constructor
private native long createPoint(int x, int y);
private native int nativeGetX(long nativePointer);
private native int nativeGetY(long nativePointer);
private native long nativeSymetrique(long nativePointer);
//private native boolean nativeEquals(Object p); TODO
}
Right now I'm stucked with the nativeSymetrique function, it says that I cannot convert 'Point' to 'jlong'. Can anyone help me on this ? Thanks
* UPDATE 2 *
SWIG solved my issues, you don't have to handwrite the wrappers and it seems to be a good choice for big libraries.
Have a look at JNA.
JNI is meant to access Java classes/objects from C. Which means that JNI gives you C functions for accessing JVM. But there is no way vice versa: to access C structures (C++ classes) from JVM. Java has no such methods. So if you want to have a "class reflection" between C++ and Java, the only you can do is to have the class on Java side and a set of JNI C calls to access, modify and call methods on the Java object. JNI native methods on Java side are of no use for you, because the only parameters it can take (in or out) can be again only Java objects (or primitives or arrays). There is simply no way to pass C(++) structures/objects to Java side.
You can manipulate with your C code as you wish and pass\return values via JNI, you can find JNI samples in androidndk/samples - helloJni.
For example:
JNIEXPORT jfloat JNICALL Java_com_opengl_glworld_GLWorldRenderer_changeCurrentArea(JNIEnv *env, jobject obj, jfloat curArea)
{
area = curArea;
return area;
// here you can execude you C code, you can access to methods of class,
// or method that use your classes.
}
As I said in my second update, SWIG was the perfect match for my needs.
I am creating a demo of math operation like addition, subtraction, multiplication and division using NDK.
I am able to make the library and getting the response from the native code but result is not proper I mean it is random static value.
Calculator.c class
#include <stdio.h>
#include <jni.h>
jint
Java_com_example_jni_calculator_Calculator_add(JNIEnv* env, jint a, jint b) {
return (jint)(a + b);
}
jint
Java_com_example_jni_calculator_Calculator_substract(JNIEnv* env, jint a, jint b) {
return (jint)(a - b);
}
jint
Java_com_example_jni_calculator_Calculator_multiply(JNIEnv* env, jint a, jint b) {
return (jint)(a * b);
}
jint
Java_com_example_jni_calculator_Calculator_devide(JNIEnv* env, jint a, jint b) {
return (jint)(a / b);
}
Calculator.java class for load library and initiating native methods.
public class Calculator {
static {
System.loadLibrary("Calculator");
}
public native int add(int a, int b);
public native int substract(int a, int b);
public native int multiply(int a, int b);
public native int devide(int a, int b);
}
I am using below code to display result:
int num1 = Integer.parseInt(txtNumber1.getText().toString().trim());
int num2 = Integer.parseInt(txtNumber2.getText().toString().trim());
tvResult.setText(String.format("%1$d + %2$d is equals to %3$d", num1, num2, mCalculator.add(num1, num2)));
Output
You are declaring non-static methods and don't pass a reference to "jobject" - that is why you are getting garbage in the return value.
To fix the bug you have to add an extra argument for "jobject" in the native code, just after the "env"argument.
Here is some supplementary sample code to Sergey's answer:
C/C++ side:
JNIEXPORT jint JNICALL Java_com_marakana_NativeLib_add
(JNIEnv *, jobject, jint, jint);
Java side:
public native int add( int v1, int v2 );
Source: https://thenewcircle.com/s/post/49/using_ndk_to_call_c_code_from_android_apps
Thanks again to Sergey K., RobinHood and Dharmendra!
I have been messing with my own little project to teach myself the android ndk using c++ and jni but I can't figure out how to pass the data from a java float array to the c++ array. I have used the jni set up. Most the tutorials I find are either too simple and don't explain enough or are too complicated and go over my understanding at the moment. So, can some one just point me to a simple example of an array being passed from java to c++, then have some method/function performed on the data and sent back to java.
Heres my attempt so far but I have two errors left in the way. Im not sure if the rest of the syntax is up to par either but I don't see anything at compile time.
#include <iostream>
#include <Eigen/Dense>
#include <math.h>
#include <jni.h>
using namespace Eigen;
Vector3f vec;
Vector3f vec2;
Vector3f vecRtrn;
void vecLoad(float x, float y, float z, float x2, float y2, float z2){
vec(0) = x;
vec(1) = y;
vec(2) = z;
vec2(0) = x2;
vec2(1) = y2;
vec2(2) = z2;
}
void vecAdd(Vector3f vecA, Vector3f vecB){
vecRtrn = vecA+vecB;
}
extern "C"
{
JNIEXPORT jfloatArray JNICALL Java_jnimath_act_JnimathActivity_test
(JNIEnv *env, jobject obj, jfloatArray fltarray1, jfloatArray fltarray2){
float array1[3];
jfloatArray flt1 = fltarray1;
jfloatArray flt2 = fltarray2;
//flt1 = env->GetFloatArrayElements( fltarray1,0);
//flt2 = env->GetFloatArrayElements( fltarray2,0);
vecLoad(flt1[0], flt1[1], flt1[2], flt2[0], flt2[1], flt2[2]);
vecAdd(vec, vec2);
array1[0] = vecRtrn[0];
array1[1] = vecRtrn[1];
array1[2] = vecRtrn[2];
return array1;
};
}
And these are the errors at compile time
$ /cygdrive/c/android-ndk-r7/ndk-build
Compile++ thumb : test <= test.cpp
jni/test.cpp: In function '_jfloatArray* Java_jnimath_act_JnimathActivity_test(JNIEnv*, _jobject*, _jfloatArray*, _jfloatArray*)':
jni/test.cpp:42: error: cannot convert '_jfloatArray' to 'float' for argument '1' to 'void vecLoad(float, float, float, float, float, float)'
jni/test.cpp:49: error: cannot convert 'float*' to '_jfloatArray*' in return
make: *** [obj/local/armeabi/objs/test/test.o] Error 1
First you can't use jfloatArray directly. Instead, you should do this
JNIEXPORT jfloatArray JNICALL Java_jnimath_act_JnimathActivity_test
(JNIEnv *env, jobject obj, jfloatArray fltarray1, jfloatArray fltarray2)
{
jfloatArray result;
result = env->NewFloatArray(3);
if (result == NULL) {
return NULL; /* out of memory error thrown */
}
jfloat array1[3];
jfloat* flt1 = env->GetFloatArrayElements( fltarray1,0);
jfloat* flt2 = env->GetFloatArrayElements( fltarray2,0);
vecLoad(flt1[0], flt1[1], flt1[2], flt2[0], flt2[1], flt2[2]);
vecAdd(vec, vec2);
array1[0] = vecRtrn[0];
array1[1] = vecRtrn[1];
array1[2] = vecRtrn[2];
env->ReleaseFloatArrayElements(fltarray1, flt1, 0);
env->ReleaseFloatArrayElements(fltarray2, flt2, 0);
env->SetFloatArrayRegion(result, 0, 3, array1);
return result;
}
Please use this as a tutorial and study more. As I said before, studying will help you more than practicing at this time.
This is basically enough for create an empty array with new ndks. Assuming env is your jni environment.
jfloatArray jArray = env -> NewFloatArray(8);
validateAudio(JNIEnv* env, jobject obj, jstring resourceFolderPath, ,jfloatArray thresholdArray){
const char *resource_folder_path = (*env)->GetStringUTFChars(env,resourceFolderPath,0); // string parameter
const jfloat* threshold_array = (*env)->GetFloatArrayElements(env, thresholdArray,0); //float array
}
Instead the older style (above Tae-Sung Shin's code, still works), we should do this nowadays:
jfloatArray result;
result = (*env)->NewFloatArray( env, numbers_here );
Here's my function definition in Java:
public static native void ToucheBegan( float x, float y, int tapcount );
And here's my definition in CPP
JNIEXPORT void JNICALL Java_com_android_templateApp_GL2View_ToucheBegan( JNIEnv *env, jfloat x, jfloat y, jint tap_count );
When I log:
From Java I send:
125.278595 496.842102 1
And In C++ I receive:
3.274879 125.278595 1140353994
Is there some sort of conversion that have to be done between a jfloat to float or jint to int?
TIA!
You forgot implicit jobject/jclass argument that every JNI function has:
void JNICALL Java_com_android_templateApp_GL2View_ToucheBegan( JNIEnv *env, jobject thiz, jfloat x, jfloat y, jint tap_count );
So you were interpreting 'thiz' as 'x', 'x' as 'y' and 'y' as 'tap_count'.
There isn't any special conversion required.
Check that your C++ logging is acting as anticipated. One way to do this is to assign known values to x,y, and tapcount within the C++ function and make sure they are being logged as expected.