I am implementing an android app using JNI ,
I have some JNI functions ,
#include <stdio.h>
#include <jni.h>
// other header files
JNIEXPORT jint JNICALL Java_com_example_projectpmic_NativeLib_get_1clock_1status
(JNIEnv *env, jobject obj, jint v)
{ / will do something
pthread_t native_thread ;
pthread_create(&native_thread,NULL,native_thread_funtion,NULL) ;
}
JNIEXPORT jint JNICALL Java_com_example_projectpmic_NativeLib_get_name
(JNIEnv *env, jobject obj, jint v)
{ / will do something
}
void *native_thread_function(void* args)
{
/*I want to call the above get_name() function in this thread function ,
// How to do that ? */
}
Note : These two functions are in same .c file
To call a JNI method from an arbitrary native method you need a JNIEnv pointer, and the only place to get one is from AttachCurrentThread().
Related
Assume the code below demonstrating a binary semaphore example.
In this example, we have a pthread that reads the source.txt and attempts to copy the content to destination.txt while locking it using a binary semaphore.
What happens in the comment section below without the semaphore?
#include <cstdlib>
#include <memory>
#include <filesystem>
#define _TIMESPEC_DEFINED
#include <pthread.h>
#include <semaphore.h>
#include <thread>
#include <valarray>
pthread_t StartFileAccessThread();
void *CopyFile(void *parameters);
int main(int argc, char* argv[])
{
pthread_t thread = StartFileAccessThread();
void *returnValue;
pthread_join(thread, &returnValue);
getchar();
return EXIT_SUCCESS;
}
pthread_t StartFileAccessThread()
{
std::string sourcePath("source.txt");
std::string destinationPath("dest.txt");
sem_t semaphore;
sem_init(&semaphore, 0, 0);
pthread_t thread;
void *parameters[3];
parameters[0] = &semaphore;
parameters[1] = &sourcePath;
parameters[2] = &destinationPath;
pthread_create(&thread, nullptr, &CopyFile, parameters);
// What happens without the binary semaphore?
sem_wait(&semaphore);
sem_destroy(&semaphore);
printf("Freeing ressources.\n");
return thread;
}
void *CopyFile(void *rawParameter)
{
void **parameters = static_cast<void **>(rawParameter);
sem_t *semaphore = static_cast<sem_t *>(parameters[0]);
std::string sourcePath(*static_cast<std::string *>(parameters[1]));
std::string destinationPath(*static_cast<std::string *>(parameters[2]));
sem_post(semaphore);
std::this_thread::sleep_for(std::chrono::seconds(2));
copy_file(sourcePath, destinationPath, std::experimental::filesystem::copy_options::overwrite_existing);
printf("File copied \n");
return nullptr;
}
What happens in the comment section below without the semaphore?
Without the semaphore, function startFileAccessThread() may return before the new thread finishes (or starts) copying its parameters from the argument object. That object is local to startFileAccessThread(), so its lifetime ends when that function returns. Undefined behavior results if the copy thread's accesses to it do not happen before that.
I called env->SetStaticIntField(cls, jfid, 0); app crashed .
addr2line tool found this:
/usr/local/google/buildbot/src/android/ndk-release-r19/external/libcxx/../../external/libcxxabi/src/cxa_demangle.cpp:2125
My application is using cmake_minimum_required(VERSION 3.4.1) and cppFlags "-std=c++14"
c++ method like this :
JNIEXPORT jint JNICALL
Java_zhumu_xiaot_com_ndkdemo_MainActivity_operateStaticInt(JNIEnv *env,
jobject instance) {
jclass cls = env->GetObjectClass(instance);
jfieldID jfid = env->GetStaticFieldID(cls, "staticint", "I");
jint jsataticint = env->GetStaticIntField(cls, jfid);
env->SetStaticIntField(cls, jfid, 80);
}
For some reason, when I try to access a java object (which persists throughout the entire program, BTW) from a thread, the program crashes. Here's a boiled down example to demonstrate the problem:
#include <jni.h>
#include <pthread.h>
pthread_t thread;
jobject object;
JavaVM* jvm;
/*
Our thread function:
*/
void* run( void* );
extern "C" void Java_com_Program_Initialize( JNIEnv* jnv, jobject caller )
{
object = caller;
jnv->GetJavaVM( &jvm );
/*
Before launching our thread, this works just fine:
*/
jnv->CallVoidMethod( object, jnv->GetMethodID( jnv->GetObjectClass( object ), "foo", "()V" ) );
pthread_create( &thread, NULL, run, NULL );
}
void* run( void* )
{
JNIEnv* jnv;
jvm->AttachCurrentThread( &jnv, NULL );
/*
Within the context of our thread however, this crashes:
*/
jnv->CallVoidMethod( object, jnv->GetMethodID( jnv->GetObjectClass( object ), "foo", "()V" ) );
jvm->DetachCurrentThread( );
return NULL;
}
Any ideas as to what's going wrong?
Okay, the problem seemed to be a missing NewGlobalRef call. This version works:
#include <jni.h>
#include <pthread.h>
pthread_t thread;
jobject object;
JavaVM* jvm;
/*
Our thread function:
*/
void* run( void* );
extern "C" void Java_com_Program_Initialize( JNIEnv* jnv, jobject caller )
{
object = jnv->NewGlobalRef( caller );
jnv->GetJavaVM( &jvm );
pthread_create( &thread, NULL, run, NULL );
}
void* run( void* )
{
JNIEnv* jnv;
jvm->AttachCurrentThread( &jnv, NULL );
jnv->CallVoidMethod( object, jnv->GetMethodID( jnv->GetObjectClass( object ), "foo", "()V" ) );
jnv->DeleteGlobalRef( object );
jvm->DetachCurrentThread( );
return NULL;
}
I'd like to suggest you to try to find a way to post the invocation to main thread.
Since the entire process from "AttachCurrentThread" to "DetachCurrentThread" is not locked, the main thread may re"AttachCurrentThread", in some case, before the "run" finished. Thus there would still be issues.
I want to call Java class methods from a cpp file that receives call backs from another executable.
To achieve this, I have retrieved a JavaVM pointer using the android::AndroidRuntime::getJavaVM() method in the .cpp file that directly receives JNI method calls. I am sharing this JavaVM pointer via the constructor to the eventual .cpp file where I call required Java methods as follows:
/* All the required objects(JNIEnv*,jclass,jmethodID,etc) are appropriately declared. */
**JNIEnv* env;
jvm->AttachCurrentThread(&env, NULL);
clazz = env->FindClass("com/skype/ref/NativeCodeCaller");
readFromAudioRecord = env->GetStaticMethodID(clazz, "readFromAudioRecord", "([B)I");
writeToAudioTrack = env->GetStaticMethodID(clazz, "writeToAudioTrack", "([B)I");**
However, I get a SIGSEGV fault running this code.
According to the JNI documentation this seems to be the appropriate way to obtain JNIEnv in arbitary contexts: http://java.sun.com/docs/books/jni/html/other.html#26206
Any help in this regard will be appreciated.
Regards,
Neeraj
Global references will NOT prevent a segmentation fault in a new thread if you try to use a JNIEnv or JavaVM reference without attaching the thread to the VM. You were doing it properly the first time around, Mārtiņš Možeiko is mistaken in implying that there was something wrong with what you were doing.
Don't remove it, just learn how to use it. That guy doesn't know what he's talking about, if it's in jni.h you can be pretty sure it's not going anywhere. The reason it's not documented is probably because it's ridiculously self explanatory. You don't need to create GlobalReference objects or anything either, just do something like this:
#include <jni.h>
#include <string.h>
#include <stdio.h>
#include <android/log.h>
#include <linux/threads.h>
#include <pthread.h>
#define LOG_TAG "[NDK]"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
static pthread_mutex_t thread_mutex;
static pthread_t thread;
static JNIEnv* jniENV;
void *threadLoop()
{
int exiting;
JavaVM* jvm;
int gotVM = (*jniENV)->GetJavaVM(jniENV,&jvm);
LOGI("Got JVM: %s", (gotVM ? "false" : "true") );
jclass javaClass;
jmethodID javaMethodId;
int attached = (*jvm)->AttachCurrentThread(jvm, &jniENV,NULL);
if(attached>0)
{
LOGE("Failed to attach thread to JavaVM");
exiting = 1;
}
else{
javaClass= (*jniENV)->FindClass(jniENV, "com/justinbuser/nativecore/NativeThread");
javaMethodId= (*jniENV)->GetStaticMethodID(jniENV, javaClass, "javaMethodName", "()V");
}
while(!exiting)
{
pthread_mutex_lock(&thread_mutex);
(*jniENV)->CallStaticVoidMethod(jniENV, javaClass, javaMethodId);
pthread_mutex_unlock(&thread_mutex);
}
LOGE("Thread Loop Exiting");
void* retval;
pthread_exit(retval);
return retval;
}
void start_thread(){
if(thread < 1)
{
if(pthread_mutex_init(&thread_mutex, NULL) != 0)
{
LOGE( "Error initing mutex" );
}
if(pthread_create(&thread, NULL, threadLoop, NULL) == 0)
{
LOGI( "Started thread#: %d", thread);
if(pthread_detach(thread)!=0)
{
LOGE( "Error detaching thread" );
}
}
else
{
LOGE( "Error starting thread" );
}
}
}
JNIEXPORT void JNICALL
Java_com_justinbuser_nativecore_NativeMethods_startThread(JNIEnv * env, jobject this){
jniENV = env;
start_thread();
}
Solved the problem. The segmentation fault was because I could not retrieve a jclass object from the JNIEnv object retrieved from the shared jvm pointer.
I propogated a Global reference jclass object alongwith the jvm and the problem was solved.
Thanks for your help Mārtiņš Možeiko!..
Regards,
Neeraj
I'm trying to make my first native function with NDK and I'm in trouble with very basic stuff.
Please consider the following c code:
#include <jni.h>
#include <string.h>
JNIEXPORT jint JNICALL Java_eu_elevelcbt_sm_YCrCbUtils_toARGB(
JNIEnv* env, jbyteArray src, jintArray out, jint width, jint height){
jbyte *c_src = (*env)->GetByteArrayElements(env, src, NULL);
jint *c_out = (*env)->GetDirectBufferAddress(env, out);
if (c_out==NULL)
return -1;
int length = width * height;
int co;
unsigned int color;
for (co=0; co<length; co++) {
color = c_src[co] & 0xFF;
color = 0xFF000000 | (color<<16) | (color<<8) | color;
c_out[co] = color;
}
(*env)->ReleaseByteArrayElements(env, src, c_src, 0);
return 0;
}
JNIEXPORT jint JNICALL Java_eu_elevelcbt_sm_YCrCbUtils_sum(jint a, jint b){
return a+b;
}
and the following Java class:
public class YCrCbUtils {
public native int toARGB(byte[] src, int[] out, final int width, final int height);
public native int sum(int a, int b);
static {
System.loadLibrary("yuv");
}
}
Problem 1: If I call the second function
Log.v("DBG", "sum is: " + new YCrCbUtils().sum(10, 5));
This is what I get: "sum is 1079199776" !!!! WHY?!??!? :(
If I try calling first function like this:
int[] colors = new int[size.width * size.height]; // where width=800 and height=480
new YCrCbUtils().toARGB(data, colors, size.width, size.height); // data is a byte[]
I get a SIGSEGV error...
HELP please!!!
PS: my dev environment is MAC OSX Snow Leopard, NDK-r5b. My runtime env is Nexus One 2.3.3
...ok I'm stupid...
My methods signatures were WRONG... They always must have "JNIEnv* env, jobject obj" as first two members... well I spend an afternoon on this but the good thing is that now I'll never forget it!
Also, on my first method I had to change
jint *c_out = (*env)->GetDirectBufferAddress(env, out);
with
jint *c_out = (*env)->GetIntArrayElements(env, out, NULL);
as the previous one was returning a NULL pointer