参考网址: Java Native Interface (JNI)
可以参考Android NDK开发详解及Android 5.0 开发环境搭建进行如下操作:
public class HelloJNI {
static {
System.loadLibrary("hello"); // Load native library at runtime
// hello.dll (Windows) or libhello.so (Unixes)
}
// Declare a native method sayHello() that receives nothing and returns void
private native void sayHello();
// Test Driver
public static void main(String[] args) {
new HelloJNI().sayHello(); // invoke the native method
}
}
Compile the "HelloJNI.java" into "HelloJNI.class".
> javac HelloJNI.java
Run javah utility on the class file to create a header file for C/C++ programs:
> javah HelloJNI
The output is HelloJNI.h as follows:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class HelloJNI */ #ifndef _Included_HelloJNI #define _Included_HelloJNI #ifdef __cplusplus extern "C" { #endif /* * Class: HelloJNI * Method: sayHello * Signature: ()V */ JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
The arguments:
- JNIEnv*: reference to JNI environment, which lets you access all the JNI fucntions.
- jobject: reference to "this" Java object.
Save the C program as "HelloJNI.c".
#include <jni.h>
#include <stdio.h>
#include "HelloJNI.h"
// Implementation of native method sayHello() of HelloJNI class
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject thisObj) {
printf("Hello World!\n");
return;
}
Compile the C program - this depends on the C compiler you used.
For MinGW GCC in Windows
> set JAVA_HOME=C:\Program Files\Java\jdk1.7.0_{xx}
// Define and Set environment variable JAVA_HOME to JDK installed directory
// I recommend that you set JAVA_HOME permanently, via "Control Panel" ⇒ "System" ⇒ "Environment Variables"
> echo %JAVA_HOME%
// In Windows, you can refer a environment variable by adding % prefix and suffix
> gcc -Wl,--add-stdcall-alias -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -shared -o hello.dll HelloJNI.c
// Compile HellJNI.c into shared library hello.dll
You can also compile and link in two steps:
// Compile-only with -c flag. Output is HElloJNI.o
> gcc -c -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" HelloJNI.c
// Link into shared library "hello.dll"
> gcc -Wl,--add-stdcall-alias -shared -o hello.dll HelloJNI.o
For Cygwin GCC in Windows
For gcc-3, include option -mno-cygwin to build DLL files which are not dependent upon the Cygwin DLL.
> gcc-3 -D __int64="long long" -mno-cygwin -Wl,--add-stdcall-alias
-I"<JAVA_HOME>\include" -I"<JAVA_HOME>\include\win32" -shared -o hello.dll HelloJNI.c
For gcc-4: I still cannot find the correct compiler option (-mno-cygwin is not supported). The Java program hangs!
> java HelloJNI
or
> java -Djava.library.path=. HelloJNI
The native functions receives argument in the above JNI types and returns a value in the JNI type (such as jstring, jintArray). However, native functions operate on their own native types (such as C-string, C's int[]). Hence, there is a need to convert (or transform) between JNI types and the native types.
4.1 Passing Primitives
Passing Java primitives is straight forward. A jxxx type is defined in the native system, i.e,. jint, jbyte, jshort, jlong, jfloat, jdouble, jchar and jboolean for each of the Java's primitives int, byte, short, long, float, double, char and boolean, respectively.
public class TestJNIPrimitive {
static {
System.loadLibrary("myjni"); // myjni.dll (Windows) or libmyjni.so (Unixes)
}
// Declare a native method average() that receives two ints and return a double containing the average
private native double average(int n1, int n2);
// Test Driver
public static void main(String args[]) {
System.out.println("In Java, the average is " + new TestJNIPrimitive().average(3, 2));
}
}
#include <jni.h>
#include <stdio.h>
#include "TestJNIPrimitive.h"
JNIEXPORT jdouble JNICALL Java_TestJNIPrimitive_average
(JNIEnv *env, jobject thisObj, jint n1, jint n2) {
jdouble result;
printf("In C, the numbers are %d and %d\n", n1, n2);
result = ((jdouble)n1 + n2) / 2.0;
// jint is mapped to int, jdouble is mapped to double
return result;
}
4.2 Passing Strings
public class TestJNIString {
static {
System.loadLibrary("myjni"); // myjni.dll (Windows) or libmyjni.so (Unixes)
}
// Native method that receives a Java String and return a Java String
private native String sayHello(String msg);
public static void main(String args[]) {
String result = new TestJNIString().sayHello("Hello from Java");
System.out.println("In Java, the returned string is: " + result);
}
}
#include <jni.h>
#include <stdio.h>
#include "TestJNIString.h"
JNIEXPORT jstring JNICALL Java_TestJNIString_sayHello(JNIEnv *env, jobject thisObj, jstring inJNIStr) {
// Step 1: Convert the JNI String (jstring) into C-String (char*)
const char *inCStr = (*env)->GetStringUTFChars(env, inJNIStr, NULL);
if (NULL == inCSt) return NULL;
// Step 2: Perform its intended operations
printf("In C, the received string is: %s\n", inCStr);
(*env)->ReleaseStringUTFChars(env, inJNIStr, inCStr); // release resources
// Prompt user for a C-string
char outCStr[128];
printf("Enter a String: ");
scanf("%s", outCStr); // not more than 127 characters
// Step 3: Convert the C-string (char*) into JNI String (jstring) and return
return (*env)->NewStringUTF(env, outCStr);
}
#include <jni.h>
#include <iostream>
#include <string>
#include "TestJNIString.h"
using namespace std;
JNIEXPORT jstring JNICALL Java_TestJNIString_sayHello(JNIEnv *env, jobject thisObj, jstring inJNIStr) {
// Step 1: Convert the JNI String (jstring) into C-String (char*)
const char *inCStr = env->GetStringUTFChars(inJNIStr, NULL);
if (NULL == inCStr) return NULL;
// Step 2: Perform its intended operations
cout << "In C++, the received string is: " << inCStr << endl;
env->ReleaseStringUTFChars(inJNIStr, inCStr); // release resources
// Prompt user for a C++ string
string outCppStr;
cout << "Enter a String: ";
cin >> outCppStr;
// Step 3: Convert the C++ string to C-string, then to JNI String (jstring) and return
return env->NewStringUTF(outCppStr.c_str());
}
4.3 Passing Array of Primitives
public class TestJNIPrimitiveArray {
static {
System.loadLibrary("myjni"); // myjni.dll (Windows) or libmyjni.so (Unixes)
}
// Declare a native method sumAndAverage() that receives an int[] and
// return a double[2] array with [0] as sum and [1] as average
private native double[] sumAndAverage(int[] numbers);
// Test Driver
public static void main(String args[]) {
int[] numbers = {22, 33, 33};
double[] results = new TestJNIPrimitiveArray().sumAndAverage(numbers);
System.out.println("In Java, the sum is " + results[0]);
System.out.println("In Java, the average is " + results[1]);
}
}
#include <jni.h>
#include <stdio.h>
#include "TestJNIPrimitiveArray.h"
JNIEXPORT jdoubleArray JNICALL Java_TestJNIPrimitiveArray_sumAndAverage
(JNIEnv *env, jobject thisObj, jintArray inJNIArray) {
// Step 1: Convert the incoming JNI jintarray to C's jint[]
jint *inCArray = (*env)->GetIntArrayElements(env, inJNIArray, NULL);
if (NULL == inCArray) return NULL;
jsize length = (*env)->GetArrayLength(env, inJNIArray);
// Step 2: Perform its intended operations
jint sum = 0;
int i;
for (i = 0; i < length; i++) {
sum += inCArray[i];
}
jdouble average = (jdouble)sum / length;
(*env)->ReleaseIntArrayElements(env, inJNIArray, inCArray, 0); // release resources
jdouble outCArray[] = {sum, average};
// Step 3: Convert the C's Native jdouble[] to JNI jdoublearray, and return
jdoubleArray outJNIArray = (*env)->NewDoubleArray(env, 2); // allocate
if (NULL == outJNIArray) return NULL;
(*env)->SetDoubleArrayRegion(env, outJNIArray, 0 , 2, outCArray); // copy
return outJNIArray;
}
5.1 Accessing Object's Instance Variables
public class TestJNIInstanceVariable {
static {
System.loadLibrary("myjni"); // myjni.dll (Windows) or libmyjni.so (Unixes)
}
// Instance variables
private int number = 88;
private String message = "Hello from Java";
// Declare a native method that modifies the instance variables
private native void modifyInstanceVariable();
// Test Driver
public static void main(String args[]) {
TestJNIInstanceVariable test = new TestJNIInstanceVariable();
test.modifyInstanceVariable();
System.out.println("In Java, int is " + test.number);
System.out.println("In Java, String is " + test.message);
}
}
#include <jni.h>
#include <stdio.h>
#include "TestJNIInstanceVariable.h"
JNIEXPORT void JNICALL Java_TestJNIInstanceVariable_modifyInstanceVariable
(JNIEnv *env, jobject thisObj) {
// Get a reference to this object's class
jclass thisClass = (*env)->GetObjectClass(env, thisObj);
// int
// Get the Field ID of the instance variables "number"
jfieldID fidNumber = (*env)->GetFieldID(env, thisClass, "number", "I");
if (NULL == fidNumber) return;
// Get the int given the Field ID
jint number = (*env)->GetIntField(env, thisObj, fidNumber);
printf("In C, the int is %d\n", number);
// Change the variable
number = 99;
(*env)->SetIntField(env, thisObj, fidNumber, number);
// Get the Field ID of the instance variables "message"
jfieldID fidMessage = (*env)->GetFieldID(env, thisClass, "message", "Ljava/lang/String;");
if (NULL == fidMessage) return;
// String
// Get the object given the Field ID
jstring message = (*env)->GetObjectField(env, thisObj, fidMessage);
// Create a C-string with the JNI String
const char *cStr = (*env)->GetStringUTFChars(env, message, NULL);
if (NULL == cStr) return;
printf("In C, the string is %s\n", cStr);
(*env)->ReleaseStringUTFChars(env, message, cStr);
// Create a new C-string and assign to the JNI string
message = (*env)->NewStringUTF(env, "Hello from C");
if (NULL == message) return;
// modify the instance variables
(*env)->SetObjectField(env, thisObj, fidMessage, message);
}
Get the Field ID of the instance variable to be accessed via GetFieldID() from the class reference. You need to provide the variable name and its field descriptor (or signature). For a Java class, the field descriptor is in the form of "L<fully-qualified-name>;", with dot replaced by forward slash (/), e.g.,, the class descriptor for String is "Ljava/lang/String;". For primitives, use "I" for int, "B" for byte, "S" for short, "J" for long, "F" for float, "D" for double, "C" for char, and "Z" for boolean. For arrays, include a prefix "[", e.g., "[Ljava/lang/Object;" for an array of Object; "[I" for an array of int.
5.2 Accessing Class' Static Variables
public class TestJNIStaticVariable {
static {
System.loadLibrary("myjni"); // nyjni.dll (Windows) or libmyjni.so (Unixes)
}
// Static variables
private static double number = 55.66;
// Declare a native method that modifies the static variable
private native void modifyStaticVariable();
// Test Driver
public static void main(String args[]) {
TestJNIStaticVariable test = new TestJNIStaticVariable();
test.modifyStaticVariable();
System.out.println("In Java, the double is " + number);
}
}
#include <jni.h>
#include <stdio.h>
#include "TestJNIStaticVariable.h"
JNIEXPORT void JNICALL Java_TestJNIStaticVariable_modifyStaticVariable
(JNIEnv *env, jobject thisObj) {
// Get a reference to this object's class
jclass cls = (*env)->GetObjectClass(env, thisObj);
// Read the int static variable and modify its value
jfieldID fidNumber = (*env)->GetStaticFieldID(env, cls, "number", "D");
if (NULL == fidNumber) return;
jdouble number = (*env)->GetStaticDoubleField(env, cls, fidNumber);
printf("In C, the double is %f\n", number);
number = 77.88;
(*env)->SetStaticDoubleField(env, cls, fidNumber, number);
}
5.3 Callback Instance Methods and Static Methods
public class TestJNICallBackMethod {
static {
System.loadLibrary("myjni"); // myjni.dll (Windows) or libmyjni.so (Unixes)
}
// Declare a native method that calls back the Java methods below
private native void nativeMethod();
// To be called back by the native code
private void callback() {
System.out.println("In Java");
}
private void callback(String message) {
System.out.println("In Java with " + message);
}
private double callbackAverage(int n1, int n2) {
return ((double)n1 + n2) / 2.0;
}
// Static method to be called back
private static String callbackStatic() {
return "From static Java method";
}
// Test Driver
public static void main(String args[]) {
new TestJNICallBackMethod().nativeMethod();
}
}
#include <jni.h>
#include <stdio.h>
#include "TestJNICallBackMethod.h"
JNIEXPORT void JNICALL Java_TestJNICallBackMethod_nativeMethod
(JNIEnv *env, jobject thisObj) {
// Get a class reference for this object
jclass thisClass = (*env)->GetObjectClass(env, thisObj);
// Get the Method ID for method "callback", which takes no arg and return void
jmethodID midCallBack = (*env)->GetMethodID(env, thisClass, "callback", "()V");
if (NULL == midCallBack) return;
printf("In C, call back Java's callback()\n");
// Call back the method (which returns void), baed on the Method ID
(*env)->CallVoidMethod(env, thisObj, midCallBack);
jmethodID midCallBackStr = (*env)->GetMethodID(env, thisClass,
"callback", "(Ljava/lang/String;)V");
if (NULL == midCallBackStr) return;
printf("In C, call back Java's called(String)\n");
jstring message = (*env)->NewStringUTF(env, "Hello from C");
(*env)->CallVoidMethod(env, thisObj, midCallBackStr, message);
jmethodID midCallBackAverage = (*env)->GetMethodID(env, thisClass,
"callbackAverage", "(II)D");
if (NULL == midCallBackAverage) return;
jdouble average = (*env)->CallDoubleMethod(env, thisObj, midCallBackAverage, 2, 3);
printf("In C, the average is %f\n", average);
jmethodID midCallBackStatic = (*env)->GetStaticMethodID(env, thisClass,
"callbackStatic", "()Ljava/lang/String;");
if (NULL == midCallBackStatic) return;
jstring resultJNIStr = (*env)->CallStaticObjectMethod(env, thisClass, midCallBackStatic);
const char *resultCStr = (*env)->GetStringUTFChars(env, resultJNIStr, NULL);
if (NULL == resultCStr) return;
printf("In C, the returned string is %s\n", resultCStr);
(*env)->ReleaseStringUTFChars(env, resultJNIStr, resultCStr);
}
You can list the method signature for a Java program via javap utility (Class File Disassembler) with -s (print signature) and -p (show private members):
> javap --help
> javap -s -p TestJNICallBackMethod
.......
private void callback();
Signature: ()V
private void callback(java.lang.String);
Signature: (Ljava/lang/String;)V
private double callbackAverage(int, int);
Signature: (II)D
private static java.lang.String callbackStatic();
Signature: ()Ljava/lang/String;
.......
5.4 Callback Overridden Superclass' Instance Method
The JNI function for calling the overridden superclass' instance method are:
NativeType CallNonvirtual<type>Method(JNIEnv *env, jobject obj, jclass cls, jmethodID methodID, ...);
NativeType CallNonvirtual<type>MethodA(JNIEnv *env, jobject obj, jclass cls, jmethodID methodID, const jvalue *args);
NativeType CallNonvirtual<type>MethodV(JNIEnv *env, jobject obj, jclass cls, jmethodID methodID, va_list args);
see the website "Java Native Interface (JNI)" for more details.
util.h
#include <jni.h>
extern JavaVM* android_jvm;
#ifdef __cplusplus
// For cpp.
#define ATTACH_JVM(env) \
JNIEnv *e;\
int __env = android_jvm->GetEnv((void **)&e, JNI_VERSION_1_6); \
android_jvm->AttachCurrentThread(&env,NULL);
#define DETACH_JVM(env) \
if( __env == JNI_EDETACHED) {\
android_jvm->DetachCurrentThread();\
}
#else
// For c
#define ATTACH_JVM(env) \
JNIEnv *e;\
int __env = (*android_jvm)->GetEnv(android_jvm, (void **)&e, JNI_VERSION_1_6);\
(*android_jvm)->AttachCurrentThread(android_jvm, &env, NULL);
#define DETACH_JVM() \
if (__env == JNI_EDETACHED) {\
(*android_jvm)->DetachCurrentThread(android_jvm);\
}
#endif
util.cpp
JavaVM* android_jvm;
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
android_jvm = vm;
return JNI_VERSION_1_6;
}
.cpp
JNIEnv* env;
ATTACH_JVM(env);
env->CallVoidMethod(obj, jmethodID); // see note
DETACH_JVM();
Note:
obj should be global references.
参考网址:how-to-access-arrays-within-an-object-with-jni
// Get the class
jclass mvclass = env->GetObjectClass( *cls );
// Get method ID for method getSomeDoubleArray that returns a double array
jmethodID mid = env->GetMethodID(mvclass, "getSomeDoubleArray", "()[D");
// Call the method, returns JObject (because Array is instance of Object)
jobject mvdata = env->CallObjectMethod(*base, mid);
// Cast it to a jdoublearray
jdoubleArray * arr = reinterpret_cast<jdoubleArray*>(&mvdata)
// Get the elements (you probably have to fetch the length of the array as well
double * data = env->GetDoubleArrayElements(*arr, NULL);
// Don't forget to release it
env->ReleaseDoubleArrayElements(*arr, data, 0);
参考网址: Android NDK实现 C 代码中操作 Java 代码的类对象
java code:
package com.example.utility;
public class Information {
public int data;
}
jni code:
jclass infoClass = env->FindClass("com/example/utility/Information");
jmethodID infoConsMethod = env->GetMethodID(infoClass, "<init>", "()V"); // Information construction
jobject infoObj = env->NewObject(infoClass, infoConsMethod);
jfieldID field = env->GetFieldID(infoClass, "data", "I");
env->SetIntField(infoObj, field, 10);
参考网址:
java code:
public class Information {
public List<xxx> xxxlist = new ArrayList<xxx>();
}
jni code:
jclass class = env->GetObjectClass(obj); //obj type: Information
jfieldID field = env->GetFieldID(class, "xxxlist", "Ljava/util/List;");
jobject listObj = env->GetObjectField(obj, field);
jclass listClass = env->FindClass("java/util/List");
jmethodID listAdd = env->GetMethodID(listClass, "add", "(Ljava/lang/Object;)Z"); // boolean add(java.lang.Object);
jclass elemClass = env->FindClass("xxx/xxx/xxx");
jmethodID elemConsMethod = env->GetMethodID(elemClass, "<init>", "()V"); // xxx.xxx.xxx construction
for (int i = 0; i < 10; i++) {
jobject elemObj = env->NewObject(elemClass, elemConsMethod);
env->CallBooleanMethod(listObj, listAdd, elemObj);
}
===================================================================
相关网址