I need get byte array from jni to Java.
Ex: I have a byte array byte[] a = {1,2,3,4,5,6}
JNIEXPORT jbyteArray JNICALL Java_com_vn_getArray (JNIEnv *env, jobject obj) {
jbyte[] a = {1,2,3,4,5,6};
return a;
}
I do not know how to return a byte array from jni.
Can someone help me? Please!
In Java, an array is an object. So to hand a byte array from C or C++ over to java you will need to instantiate a jbyteArray, and return that. Instead of a C array. To solve that, see the following code:
JNIEXPORT jbyteArray JNICALL Java_Test_returnArray
(JNIEnv *env, jobject This)
{
jbyte a[] = {1,2,3,4,5,6};
jbyteArray ret = env->NewByteArray(6);
env->SetByteArrayRegion (ret, 0, 6, a);
return ret;
}
Based on this link
I do like that and it's working
JNIEXPORT jbyteArray JNICALL Java_com_vn_getArray(JNIEnv *env, jobject obj) {
jbyte byteUrl[] = {1,2,3,3,4};
int sizeByteUrl = 5;
jbyteArray data = (*env)->NewByteArray(env, sizeByteUrl);
if (data == NULL) {
return NULL; // out of memory error thrown
}
// creat bytes from byteUrl
jbyte *bytes = (*env)->GetByteArrayElements(env, data, 0);
int i;
for (i = 0; i < sizeByteUrl; i++) {
bytes[i] = byteUrl[i];
}
// move from the temp structure to the java structure
(*env)->SetByteArrayRegion(env, data, 0, sizeByteUrl, bytes);
return data;
}
Related
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 get pdf meta-data using MuPdf in Android ? I'm using MuPdf V1.7.
I can get Author & PDF name but I cannot get creation date, creator and etc. I used below function to get information:
fz_lookup_metadata(ctx, glo->doc, FZ_META_INFO_TITLE, info, sizeof(info));
fz_lookup_metadata(ctx, glo->doc, FZ_META_INFO_AUTHOR, info, sizeof(info));
Can anybody help?
hi all i can get creation date from pdf by the below code.
add this into document.h
#define FZ_META_INFO_CREATIONDATE "info:CreationDate"
paste the below code into mupdf.c
JNIEXPORT jstring
JNICALL JNI_FN(MuPDFCore_metaPublishDate)(JNIEnv * env, jobject thiz)
{
char info[64];
globals *glo = get_globals(env, thiz);
fz_context *ctx = glo->ctx;
pdf_document *idoc = pdf_specifics(ctx, glo->doc);
fz_lookup_metadata(ctx, glo->doc, FZ_META_INFO_CREATIONDATE, info, sizeof(info));
return (*env)->NewStringUTF(env, info);
}
then we can able to get this by core.metaPublishDate().
you can easily do this with mupdf library.This function returns a string array which contains metadata information, respectively to keys in keys array. If there is no such info for a key, it returns an empty string
JNIEXPORT jobjectArray JNICALL
JNI_FN(MuPDFCore_metadataInternal)(JNIEnv * env, jobject thiz)
{
char info[64];
globals *glo = get_globals(env, thiz);
jobjectArray arr;
jclass stringClass;
const int nkeys = 4;
const char *keys[nkeys];
int i;
keys[0] = "Title";
keys[1] = "Author";
keys[2] = "Subject";
keys[3] = "Keywords";
stringClass = (*env)->FindClass(env, "java/lang/String");
arr = (*env)->NewObjectArray(env, nkeys, stringClass, NULL);
LOGI("Getting metadata");
for(i=0; idoc, FZ_META_INFO, info, sizeof(info));
LOGI("%s : %s", keys[i], info);
jstring s = (*env)->NewStringUTF(env, info);
if (s != NULL) {
(*env)->SetObjectArrayElement(env, arr, i, s);
}
(*env)->DeleteLocalRef(env, s);
}
return arr;
}
I tried implementing direct convolution of two audio files using JNI on Android.. So far, I ve done this:
JNIEXPORT jint JNICALL Java_com_example_directconv_MainActivity_convolve (JNIEnv * env, jobject obj, jdoubleArray signal1, jdoubleArray signal2, jdoubleArray output)
{
jdouble *sig1, *sig2, *out;
// jboolean isCopy1, isCopy2, isCopy3;
int i,j;
jsize n,m;
sig1=(*env)->NewDoubleArray(env,n);
sig2=(*env)->NewDoubleArray(env,m);
out=(*env)->NewDoubleArray(env,m);
n=(*env)->GetArrayLength(env, sig1);
m=(*env)->GetArrayLength(env,sig2);
sig1=(*env)->GetDoubleArrayElements(env, signal1,NULL);
sig2=(*env)->GetDoubleArrayElements(env, signal2,NULL);
out=(*env)->GetDoubleArrayElements(env, output, NULL);
if (sig1 != NULL || sig2!=NULL) {
memcpy(signal1,sig1,n);
memcpy(signal2,sig2,m);
(*env)->ReleaseDoubleArrayElements(env,signal1,sig1,JNI_ABORT);
(*env)->ReleaseDoubleArrayElements(env,signal2,sig2,JNI_ABORT);
}
for(i=0; i<n;i++)
{
out[i]=0;
for(j=0;j<m;j++)
{
out[i]+=sig1[i-j]*sig2[j];
}
}
(*env)->ReleaseDoubleArrayElements(env,output,out,0);
return 1;}
And on the java side:
public class MainActivity extends Activity {
static
{
System.loadLibrary("DirectConv");
}
File externalDir1=Environment.getExternalStorageDirectory();
File externalDir2=Environment.getExternalStorageDirectory();
File f1=new File(externalDir1.getAbsolutePath()+"/Test"+File.separator+"wav2.wav");
File f2=new File(externalDir2.getAbsolutePath()+"/Test"+File.separator+"wav2.wav");
int res;
public native int convolve(double[]signal1, double[]signal2, double[]output);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
//---------------------------------------------read files
double[]raw1=read(f1);
double[]raw2=read(f2);
double[]out=new double[raw1.length];
res=convolve(raw1,raw2,out);
for(int i=0;i<out.length;i++)
Log.i("out", "out "+ out[i]);
}
The resulting array (out) is all zeros. I don't see what I did wrong. Any help?
Your handling of JNI arrays is all wrong. First off, there's no reason for you to be creating any new arrays here. YOur inputs and outputs are passed down, what you want to do is convert the inputs to C arrays, work on them in pure C, then convert your output array to a Java array.
Secondly, you don't need the memcpys. Calling getDoubleArrayElements does it for you.
Third, you need to put the values in out back into output via setDoubleArrayRegion
Fourth, you need to clean up your memory usage. If you don't, you're going to memory leak and eventually die as there's a limit of 256 (or is it 512? Been too long I forget) Java objects pinned to C.
Your code should look more like:
JNIEXPORT jint JNICALL Java_com_example_directconv_MainActivity_convolve (JNIEnv * env, jobject obj, jdoubleArray signal1, jdoubleArray signal2, jdoubleArray output)
{
double *sig1, *sig2, *out;
int i,j;
jsize n,m;
n=(*env)->GetArrayLength(env, sig1);
m=(*env)->GetArrayLength(env,sig2);
sig1=(*env)->GetDoubleArrayElements(env, signal1,NULL);
sig2=(*env)->GetDoubleArrayElements(env, signal2,NULL);
for(i=0; i<n;i++)
{
out[i]=0;
for(j=0;j<m;j++)
{
out[i]+=sig1[i-j]*sig2[j];
}
}
(*env)->SetDoubleArrayRegion(env, output, 0, n, out);
(*env)->ReleaseDoubleArrayElements(env,signal1,sig1,JNI_ABORT);
(*env)->ReleaseDoubleArrayElements(env,signal2,sig2,JNI_ABORT);
return 1;}
I'm going to assume your convolution math is right, its way too late for me to start remembering that stuff :)
I figured out the problem. I added files sizes as parameters like this:
JNIEXPORT jint JNICALL Java_com_example_directconv_MainActivity_convolve (JNIEnv * env, jobject obj, jdoubleArray signal1, jdoubleArray signal2, jdoubleArray output, jint size1, jint size2)
{
jdouble *sig1, *sig2, *out;
int i,j,k;
jint n,m;
n=size1;
m=size2;
sig1=(*env)->GetDoubleArrayElements(env, signal1,NULL);
sig2=(*env)->GetDoubleArrayElements(env, signal2,NULL);
out=(*env)->GetDoubleArrayElements(env,output,NULL);
for(i=0; i<n;i++)
{
out[i]=0;
for(j=0;j<m;j++)
{
if(i-j>=0)
out[i]+=sig1[i-j]*sig2[j];
}
}
(*env)->SetDoubleArrayRegion(env, output, 0, n, out);
(*env)->ReleaseDoubleArrayElements(env,signal1,sig1,JNI_ABORT);
(*env)->ReleaseDoubleArrayElements(env,signal2,sig2,JNI_ABORT);
return 1;
}
The program should take an Image from the SD card and adjust its brightness. And the image is taken from the SD card via the NDK C-code. It is to be noted that the string depicting the path to the image is passed to the NDK via JNI.
Java code:
private void adjustBrightness() {
imagePath = (Environment.getExternalStorageDirectory().getPath()+"earthglobe.jpeg").toCharArray();
brightness(imagePath, brightness);
}
public native void brightness(char[] imagePath, float brightness);
NDK code:
JNIEXPORT void JNICALL Java_com_example_ImageActivity_brightness(JNIEnv * env,char[] bitmappath, jfloat brightnessValue)
{
string bmpath = bitmappath+'\0';
jobject obj = fopen( bitmappath , "rb" );
}
You cannot pass char[] this way.
In Java use:
public static native void brightness(String imagePath, float brightness);
In native use:
std::string ConvertJString(JNIEnv* env, jstring str)
{
if ( !str ) std::string();
const jsize len = env->GetStringUTFLength(str);
const char* strChars = env->GetStringUTFChars(str, (jboolean *)0);
std::string Result(strChars, len);
env->ReleaseStringUTFChars(str, strChars);
return Result;
}
JNIEXPORT void JNICALL Java_com_example_ImageActivity_brightness(JNIEnv * env, jobject obj, jstring bitmappath, jfloat brightnessValue)
{
std::string bmpath = ConvertJString( env, bitmappath );
FILE* f = fopen( bmpath.c_str(), "rb" );
// do something useful here
fclose( f );
}
Background
I'm working with byte arrays in JNI. And I can't get length of jbyteArray. I'm writing code in eclipse in Windows 7.
Java code:
private native int Enroll( byte[] pSeed );
JNI code:
In JNI I have a struct that have two members unsigned long length and unsigned char data[1]
typedef struct blobData_s {
unsigned long length;
unsigned char data[1];
} blobData_t;
Now as my JNI function get as argument jbyteArray jpSeed i want to get the length of jpSeed and set it as length member of struct.
JNIEXPORT jint JNICALL Java_com_Test_Enroll( JNIEnv* env, jobject thiz, jbyteArray jpSeed ){
blobData_t* bd = malloc( sizeof(blobData_t) );
bd->length = **Question 1**
bd->data[1] = jbyteArray;
}
Question 1: How I can get the length of jpSeed in JNI ?
Question 2: Will this code works correct bd.data[1] = jbyteArray; ?
You can use GetArrayLength(JNIEnv* env, jbyteArray array) Read here.
Not sure what you want to do, I assume you want the content of jpSeed in bd.data[1].
Anyways, accessing the contents of a byte array, should be done with GetByteArrayElements(...).
Solution
Answer on Question 1. As jpSeed is jbyteArray it mean that you can get it's length by calling GetByteArrayElements( ... ) functions that declared in JNI ( you can read documentation here ) here the right code will be:
JNIEXPORT jint JNICALL Java_com_Test_Enroll( JNIEnv* env, jobject thiz, jbyteArray jpSeed ){
blobData_t* bd = malloc( sizeof(blobData_t) );
bd->length = (*env)->GetArrayLength( env, jpSeed );
.......
}
Answer on Question 2. This bd->data[1] = jbyteArray; code is not correct as it will not be compiled the right solution for this part is:
JNIEXPORT jint JNICALL Java_com_Test_Enroll( JNIEnv* env, jobject thiz, jbyteArray jpSeed ){
blobData_t* bd = malloc( sizeof(blobData_t) );
.......
jbyte* bytes = (*env)->GetByteArrayElements( env, jpSeed, 0 );
bd->data[1] = bytes[1];
}
And don't forgot to release.