我知道這是舊的,但人們仍然從搜索中找到它。
下面是完整的解決方案,如何從Java調用C代碼,並從JNI的C代碼回調Java代碼:
項目中的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。 MK在您的項目JNI目錄
APP_ABI := armeabi-v7a
APP_PLATFORM := android-8
項目中的JNI DIR生成testjni.c
#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);
}
在JNI文件夾運行NDK建造
編譯庫libtest.so
創建:MyInterface.java在Java的Android項目的src
package com.example.test;
public interface MyInterface
{
void setData(int width, int height);
}
創建LibTest.java //用於發送數據從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();
}
創建MainActivity.java
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);
}
}
我不認爲你可以指望你複製到全局值的那些值在返回給它們的函數後保持有效。在我自己的類似的情況下,也是在Android上,我遇到了問題,當我停止將指針存儲爲全局變量並且更新它們並將JVM調用到模塊中時,我解決了這個問題。 – mah
您應該可以緩存vm指針,該指針不會更改,但您需要獲得'JNIEnv'來確保多線程兼容性,就像您上面所述。 – Samhain
如果我正在處理單線程應用程序,可以對JNIEnv全局進行一次初始化,然後在多個jni方法調用(在一個線程中)中使用它。 – Lighter