In the jni layer of the android code that I have written, am returning an array from the jni layer to the java layer. I am using DeleteLocalRef() to free the local reference before passing the result. i just wanted to make sure that the code i have written is proper. Please find the code below.Any help is appreciated.
extern "C"
{
JNIEXPORT jbyteArray JNICALL Java_com_jni_btRead(JNIEnv* env, jobject)
{
unsigned char* reply = btRead();
jbyteArray jba;
if(reply)
{
jba = env->NewByteArray(2048);
env->SetByteArrayRegion(jba, 0, 2048, reinterpret_cast<jbyte*>(reply));
}
else
{
jba = env->NewByteArray(0);
}
env->DeleteLocalRef(jba);
return jba;
}
}
Local variables are always created in stack segment and hence destroyed after returning from function. This diagram may help.
A quote from here:
A local reference is valid only within the dynamic context of the
native method that creates it, and only within that one invocation of
the native method. All local references created during the execution
of a native method will be freed once the native method returns.
So you may not free your local reference as it gets freed automatically.
Related
I am trying to use Androids NDKMediaExtractor in C++ code like this:
const char *storageFile = "/storage/emulated/0/mytestfile.mp3";
AMediaExtractor *extractor = AMediaExtractor_new();
media_status_t amresult = AMediaExtractor_setDataSource(extractor, storageFile);
This fails with the error message E/NdkMediaExtractor: setDataSource(path) must be called from Java thread.
Searching online did not bring me any hints how I can solve this, what I did find though was the source code, the responsible part looks like this:
EXPORT
media_status_t AMediaExtractor_setDataSource(AMediaExtractor *mData, const char *location) {
ALOGV("setDataSource(%s)", location);
// TODO: add header support
JNIEnv *env = AndroidRuntime::getJNIEnv();
jobject service = NULL;
if (env == NULL) {
ALOGE("setDataSource(path) must be called from Java thread");
env->ExceptionClear();
return AMEDIA_ERROR_UNSUPPORTED;
}
// rest of method omitted for brevity
}
I hand in a JNIEnv when I call my native methods from Java/Kotlin, but NDKMediaExtractordoes not seem to know about it. How can I solve this?
you cant just pass JNIenv* between threads you should get it(every time in a native thread) by calling AttachCurrentThread(before calling any jni methods)in the current native thread so that it can be managed as java Thread.Once you do this it should be working.
i.e
JNIEnv* env;
virtualMachine->AttachCurrentThread(&env,NULL);
you should obtain env by calling AttachCurrentThread and now your native thread is managed as Java thread after attaching.So should resolve ur error.
One of the ways to register native functions in JNI is by using the function RegisterNatives(). An example (although seemingly with some errors) can be found in the Android documentation here. Here's a code example demonstrating this technique:
#include <jni.h>
void JNICALL test(JNIEnv* const environment, jobject const objectOrClass) {
}
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* const vm, void* const reserved) {
JNIEnv* environment;
vm->GetEnv(reinterpret_cast<void**>(&environment), JNI_VERSION_1_6);
auto const classRef = environment->FindClass("com/example/test/MainActivity");
JNINativeMethod method;
method.name = "test";
method.signature = "()V";
method.fnPtr = reinterpret_cast<void*>(test);
environment->RegisterNatives(classRef, &method, 1);
return JNI_VERSION_1_6;
}
Using RegisterNatives() requires casting (non-member) function pointers to type void*, as shown above. According to this documentation, this isn't necessarily portable in C++. It may be guaranteed to work in some environments (such as POSIX), but the behavior is otherwise implementation-dependent.
I wouldn't expect this functionality to be exposed this way if it wasn't reliable, but does either JNI or Android offer any guarantees that this can be counted on to work? Is it just assumed that JNI and/or Android will always be running in environments where casting between function pointers and void* is supported?
EDIT:
After further consideration, it seems JNI would almost have to do something non-portable with respect to native functions, irrespective of the cast issue. The only information about the function JNI has (other than the address) is a string describing the argument and return types, so it seems unlikely that the void* will ever be cast back to a valid function pointer and used in the conventional way. Presumably the calls are instead made via low-level platform-specific code.
It still seems like a requirement that function-pointer-to-void* casts be well-behaved, so if anyone knows if or how JNI guarantees that, I'd still be interested to know.
I am trying to load a libsvm model on an android phone. The model is about 3MB, but it takes about 20s to load this model file which is not acceptable for a small App. I am using the libsvm official Java API to load it from my internal storage.
PS: I am trying to use libsvm c++ API to load this model via JNI too. but I meet the following problem:
JNIEXPORT void JNICALL Java_ca_uwo_csd_Threads_FoodRecgNativeLib_loadsvm
(JNIEnv *env, jclass cls, jstring path)
{
const char* path_char = env->GetStringUTFChars(path,0);//path is correct!
LOGI(TAG,"Loading start");
svm_model* model = svm_load_model(path_char); // fatal signal 11 here!!!
LOGI(TAG,"Loading OK");
}
If I comment the load model line, everything goes well. Can anyone help?
I solved it myself. Calling C++ loading function svm_load_model() via JNI is much faster than Java version(444ms in my case). The fatal signal 11 error happens at
char *old_locale = strdup(setlocale(LC_ALL, NULL));
setlocale(LC_ALL, "C");
and any other places that call the parameter and function in svm.cpp file (like free the points to it). So comment these and loading file will work.
Can anyone explain why this happens?
I need to get Android unique device ID in my native library. As far as i know, it can be done with Java API and i need to use JNI. I read this, there is similar problem, but different ID is accessed. But this solution needs reference to JNIEnv for getting Java objects/methods. When JNI method called from Java, this is not problem, JNIEnv will be passed from Java. But how i can get JNIEnv for "total" native code?
The entrypoint of your native activity receives a struct android_app* as its argument. android_app contains an ANativeActivity* named activity, which in turn contains a JNIEnv* named env.
To be able to call Android Java API methods from your native code you'll have to attach the current thread to the VM first, i.e.:
JNIEnv *env = state->activity->env;
JavaVM *vm = state->activity->vm;
(*vm)->AttachCurrentThread(vm, &env, NULL);
(where state is the struct android_app*)
I found that JNIEnv can be accessed with JavaVM, which can be created with JNI invocation API. By default in Android NDK this functions not exported to user(from platforms/android-19/arch-arm/usr/include/jni.h):
/*
* VM initialization functions.
*
* Note these are the only symbols exported for JNI by the VM.
*/
#if 0 /* In practice, these are not exported by the NDK so don't declare them */
jint JNI_GetDefaultJavaVMInitArgs(void*);
jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);
#endif
But this functions presented in /system/lib/libdvm.so on my device. After including function definition for JNI_CreateJavaVM in my code and linking to libdvm, copied from device, i got access to creation of JavaVM in Android. This is not recommended, of course, but, if you want it and your device libdvm supports this, you can do it.
I have compiled FFmpeg library using NDK and use it to trim videos and get thumbnails from a video in my app, so basically I have ffmpeg.so and video-trimmer.so libraries up and running.
The problem however is strange, the trim or getThumbnail operations are successful but just one time i.e. the first time and the operations fail the second time. However it is successful the third time, I googled it and got two similar posts on SO related to my problem
POST 1:
POST 2:
Interestingly they suggest the same solution and I am unable to solve the issue being a naive in C programming language.
Here is what I have done
void Java_com_example_demo_natives_LibraryLoader_loadTrimmerLibrary(JNIEnv* env, jclass class, jstring libffmpeg_path, jstring inputFile, jstring outFile,
jstring startTime, jstring length)
{
const char* path;
void* handle;
int *(*Java_com_example_demo_natives_VideoTrimmer_trim)(JNIEnv *, jclass, jstring, jstring, jstring, jstring);
path = (*env)->GetStringUTFChars(env, libffmpeg_path, 0);
handle = dlopen(path, RTLD_LAZY);
Java_com_example_demo_natives_VideoTrimmer_trim = dlsym(handle, "Java_com_example_demo_natives_VideoTrimmer_trim");
(*Java_com_example_demo_natives_VideoTrimmer_trim)(env, class, inputFile, outFile, startTime, length);
(*env)->ReleaseStringUTFChars(env, libffmpeg_path, path);
dlclose(handle);
}
Despite of calling dlclose the library instance still exists in memory, what I am doing wrong here?
I come to know that library instance still exists because when I load the libraries again in some other activity the error message says library already exists in CL.
I want to get rid of the instance of that library from memory, please help...
try moving the position of the 'ReleaseString...'
it should be after the 'dlopen'
it should be before the call into the other shared lib...
(*env)->GetStringUTFChars
dlopen
(*env)->ReleaseStringUTFChars
make the main call
dlclose