2014-02-20 3 views
1

일부 C 라이브러리에는 signature가있는 함수가 있습니다. int someFunction (int a);JNI에서 Java 메소드 호출

인터페이스를 손상시키지 않도록이 함수 (원시 코드에서)에서 Java 메소드를 호출해야합니다. JNI 필요한 someFunctionJNIEnv 같은 여분의 인수를 넣지 수

I

int someFunction(int a) { 
    ... 
    jbyteArray result = (jbyteArray)(*jEnv)->CallStaticObjectMethod(jEnv, clazz, methodId); 
    ... 
} 
그래서 난 초기화에 전역 변수와 함수를 선언 : 예를 들어

jmethodID globalMethodId; 
jclass globalClass; 
JavaVM* globalJVM; 

void initJNI(JNIEnv * env, jclass clazz) { 
    globalClass = (*env)->NewGlobalRef(env, clazz); 
    (*env)->GetJavaVM(env, &globalJVM); 

    globalMethodId = (*env)->GetStaticMethodID(env, clazz, "javaMethodName", "()[B"); 
} 

JNIEnv * GetJniEnv() { 
    JNIEnv * env; 
    if((*globalJVM)->AttachCurrentThread(globalJVM, &env, NULL) != JNI_OK) { 
     env = NULL; 
    } 
    return env; 
} 

내 된 SomeFuncion 기능입니다 다음과 같이

int someFunction(int a) { 
    JNIEnv * jEnv = GetJniEnv(); 
    jbyteArray result = (jbyteArray)(*jEnv)->CallStaticObjectMethod(jEnv, globalClass, globalMethodId); 
    ... 
} 

그리고이 새 라이브러리의 사용자는 initJNI 함수를 호출 한 후 someFunction을 호출해야합니다.

오류가 발생하지 않는 방법입니까? 멀티 쓰레딩은 어떨까요? 더 바람직한 해결책이 있습니까?

+1

주어진 함수의 반환 후에 유효한 전역 변수에 복사 한 값을 믿을 수는 없을 것 같습니다. 내 자신의 비슷한 상황에서, 또한 안드로이드에서 포인터를 전역 변수로 저장하는 것을 멈추고 JVM이 내 모듈에 호출 할 때 문제를 해결했습니다. – mah

+1

VM 포인터를 캐시하는 것은 괜찮지 만 변경하지는 말아야합니다. 그러나 위에서와 같이 다중 스레드 호환성을 보장하려면'JNIEnv'를 얻어야합니다. – Samhain

+0

한 스레드 응용 프로그램을 다루는 경우 JNIEnv 전역 초기화를 한 번 수행 한 다음 jni 메서드를 여러 번 호출 (한 스레드에서) 할 수 있습니다. – Lighter

답변

0

나는 이것이 오래되었다는 것을 알고 있지만, 사람들은 아직도 그것을 검색에서 찾는다.

을 프로젝트 JNI 디렉토리

LOCAL_PATH := $(call my-dir) 
include $(CLEAR_VARS) 
LOCAL_ALLOW_UNDEFINED_SYMBOLS=false 
LOCAL_MODULE := testjni 
LOCAL_SRC_FILES := testjni.c 
LOCAL_LDLIBS := -llog -ljnigraphics 
include $(BUILD_SHARED_LIBRARY) 

응용 프로그램 만들기에 Android.mk를 작성 : 여기

자바에서 C 코드를 호출하고, JNI C 코드에서 자바 코드를 다시 호출하는 방법, 전체 솔루션입니다. MK 프로젝트의 JNI의 디렉토리에

APP_ABI := armeabi-v7a 
APP_PLATFORM := android-8 

프로젝트 JNI 디렉토리에 testjni.c 만들기

JNI 폴더 실행 NDK 빌드에서

#include <jni.h> 
#include <string.h> 
#include <sys/stat.h> 
#include <sys/types.h> 
#include <pthread.h> 

#include <android/log.h> 

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,"TAG",__VA_ARGS__) 

JavaVM *myVm; 

static void *android_gui = NULL; 

jint JNI_OnLoad(JavaVM *vm, void *reserved) 
{ 
    // Keep a reference on the Java VM. 
    myVm = vm; 

    LOGD("JNI interface loaded."); 

    return JNI_VERSION_1_2; 
} 


void jni_setDataEnv(JNIEnv *p_env, int width, int height) 
{ 
    if (android_gui == NULL) 
     return; 

    jclass cls = (*p_env)->GetObjectClass (p_env, android_gui); 

    jmethodID methodId = (*p_env)->GetMethodID (p_env, cls, "setData", "(II)V"); 

    //add or delete II's depinding your number or variable passsed 

    (*p_env)->CallVoidMethod (p_env, android_gui, methodId, width, height); 

    (*p_env)->DeleteLocalRef(p_env, cls); 
} 

void jni_setData(int width, int height) 
{ 

    JNIEnv *p_env; 

    (*myVm)->AttachCurrentThread (myVm, &p_env, NULL); 

    jni_setDataEnv(p_env, width, height); 

    int check = (*myVm)->GetEnv(myVm, (void *) &p_env, JNI_VERSION_1_2); 

    LOGD(" Detach destructor = %d", check); 

    if (check != JNI_EDETACHED) 
    { 

     int detach = (*myVm)->DetachCurrentThread (myVm); 

     LOGD("Detach result ==: %d", detach); 

    } 


} 

//change this to your package 

JNIEXPORT void JNICALL Java_com_example_test_LibTest_initM(JNIEnv *env, jobject thiz, jobject gui) 
{ 

    android_gui = (*env)->NewGlobalRef(env, gui); 

} 


//when you need , in your c code call jni_setData(100.200) and 
//MainActivity method , setData() will receive this values 

void *call_from_thread() 
{ 
    jni_setData(1,2); 
    return NULL; 
} 

JNIEXPORT void JNICALL Java_com_example_test_LibTest_test(JNIEnv *env, jobject thiz) 
{ 


    pthread_t t; 
    //do hard work in a thread 
    pthread_create(&t, NULL, call_from_thread, NULL); 


} 
만들기

libtest.so 라이브러리를 컴파일 : MyInterface.java 당신의 자바 안드로이드 프로젝트 SRC에

package com.example.test; 

public interface MyInterface 
{ 

    void setData(int width, int height); 

} 

만들기 LibTest.java // 데이터를 전송하는 데 사용 자바에서 C 코드에

package com.example.test; 

public class LibTest 
{ 

    static 
    { 
    System.loadLibrary("testjni"); 
    } 

    private static LibTest sInstance; 

    public static LibTest getInstance() 
    { 
     synchronized (LibTest.class) 
      { 
      if (sInstance == null) 
       { 
       /* First call */ 
       sInstance = new LibTest(); 
       } 
      } 

     return sInstance; 
    } 

    public native void initM(MyInterface myinterface); 

    public native void test(); 

} 

package com.example.test; 

import android.app.Activity; 
import android.os.Bundle; 
import android.util.Log; 

public class MainActivity extends Activity implements MyInterface 
{ 
    int mHeight; 
    int mWidth; 

    LibTest libtest; 

    @Override 
    public void onCreate(Bundle savedInstanceState) 
    { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     libtest = LibTest.getInstance(); 

     libtest.initM(MainActivity.this); 


     //tell jni to send us some data 
     //we receive data in 'setData(int width, int height)' method 

     libtest.test(); 

    } 

@Override 
public void setData(int width, int height) 
{ 
    /// Here you receive data from C code 
    //We can store the values C send to Java so we use latter 

    mHeight = height; 
    mWidth = width; 

    Log.d("TAG" ,"GOT SOME DATA FROM C CODE" + mHeight + " " + mWidth); 


} 

} 
MainActivity.java 만들기