JNI学习笔记_Java调用C —— 非Android中使用的方法
一、学习笔记
1.java源码中的JNI函数本机方法声明必须使用native修饰。
2.相对反编译 Java 的 class 字节码文件来说,反汇编.so动态库来分析程序的逻辑要复杂得多,为了应用的安全性,会将一些复杂的逻辑和
算法通过本地代码(C或C++)来实现,然后打包成.so动态库文件
3.使用了 JNI 接口的 JAVA 程序,不再像以前那样自由的跨平台。如果要实现跨平台,就必须将本地代码在不同的操作系统平台下编译出相
应的动态库。
4.JNI 开发流程主要分为以下 6 步:
(1)编写声明了 native 方法的 Java 类。
(2)将 Java 源代码编译成 class 字节码文件。
(3)用 javah -jni 命令生成.h头文件(-jni 参数表示将 class 中用native 声明的函数生成 JNI 规则的函数)。
(4)用本地代码实现.h头文件中的函数。
(5)将本地代码编译成动态库。
(6)拷贝动态库至 java.library.path 本地库搜索目录下,并运行 Java 程序。
5.JVM 查找 native 方法的两种方式:
(1)按照 JNI 规范的命名规则
(2)调用 JNI 提供的 RegisterNatives 函数,将本地函数注册到 JVM 中。
6.Native方法签名中的 JNIEXPORT 和 JNICALL 在 Linux/Unix 系统中,这两个宏可以省略不加。
7.本地实现方法的第二个参数为:如果这个 native 方法是实例方法,则该参数是 jobject,如果是静态方法,则是 jclass。
8.JNI 把 Java 中的所有对象当作一个C指针传递到本地方法中,这个指针指向 JVM 中的内部数据结构,而内部的数据结构在内存中的存储方
式是不可见的。只能从 JNIEnv 指针指向的函数表中选择合适的 JNI 函数来操作 JVM 中的数据结构。
9.JNI 的异常和 Java 中的异常处理流程是不一样的,Java 遇到异常如果没有捕获,程序会立即停止运行。而 JNI 遇到未决的异常不会改变
程序的运行流程,也就是程序会继续往下走。
10.字符串操作
(1)GetStringChars/GetStringUTFChars和ReleaseStringChars/ReleaseStringUTFChars
用于获取和释放以 Unicode/UTF-8 格式编码的字符串。后者是用于释放。
(2)GetStringLength/GetStringUTFLength
由于 UTF-8 编码的字符串以'\0'结尾(也可以使用strlen获取长度),而 Unicode 字符串不是。上面函数获取字符串长度。
(3)GetStringCritical和ReleaseStringCritical
提高 JVM 返回源字符串直接指针的可能性,但是获取和释放之间是不能阻塞的也不能调用其它JNI函数。因为获取这个直接指针后会导致暂停
GC(垃圾回收) 线程,当 GC 被暂停后,如果其它线程触发 GC 继续运行的话,都会导致被阻塞。
(4)GetStringRegion和GetStringUTFRegion
分别表示获取 Unicode 和 UTF-8 编码字符串指定范围内的内容到一个预先分配好的缓冲区中,这个函数不会分配内存,因此也没有释放的函数。
11.JNI 中的数组分为基本类型数组和对象数组,它们的处理方式是不一样的,基本类型数组中的所有元素都是 JNI 的基本数据类型,可以直
接访问。而对象数组中的所有元素是一个类的实例或其它数组的引用,和字符串操作一样,不能直接访问 Java 传递给 JNI 层的数组,必须选
择合适的 JNI 函数来访问和设置 Java 层的数组对象。
12.GC 会实时扫描所有创建的对象是否还有引用,如果没有引用则会立即清理掉。当我们创建一个像 int 数组对象的时候,当我们在本地代码
想去访问时,发现这个对象正被 GC 线程占用了,这时本地代码会一直处于阻塞状态,直到等待 GC 释放这个对象的锁之后才能继续访问。为了
避免这种现象的发生,JNI 提供了 Get/ReleasePrimitiveArrayCritical 这对函数,本地代码在访问数组对象时会暂停 GC 线程。同样这两个函
数之间不能阻塞或调用其它JNI函数
13.在 JNI 中,只有 jobject 以及子类属于引用变量,会占用引用表的空间,jint,jfloat,jboolean 等都是基本类型变量,不会占用引用
表空间,即不需要释放。引用表最大空间为 512 个,如果超出这个范围,JVM 就会挂掉。
14.本地代码调用 Java 层某个对象的方法或属性,这就是来自 C/C++层本地函数的 callback(回调)。
15.引用类型统一调用CallStaticObjectMethod 函数.(不存在CallStaticStringMethod)。
16.GetMethodID/GetStaticMethodID 和 CallIntMethod/CallStaticIntMethod 和 SetIntField/SetStaticIntField,对应static和非static成员方法的调用函数不同。
17.JNI方法签名的格式为:(形参参数类型列表)返回值。形参参数列表中,引用类型以 L 开头,后面紧跟类的全路径名(需将.全部替换成/),
以分号结尾。
18.GetMethodID()返回的jmethodID应该不是引用,不需要对它调用DeleteLocalRef()。
19.调用 GetMethodID 获取方法 ID 和调用 FindClass 获取 Class 实例后,要做异常判断
20.在 Java 中任何一个类的.class字节码文件被加载到内存中之后,该class子节码文件统一使用 Class 类来表示该类的一个引用(相当于
Java 中所有类的基类是 Object一样)。然后就可以从该类的 Class 引用中动态的获取类中的任意方法和属性。
21.在本地代码中可以调用 JNI 函数可以访问 Java 对象中的非 public(eg private) 属性和方法。
22.由于 ID 对于特定类是相同的,因此只需要查找一次,然后便可重复使用。同样,查找类对象的开销也很大,因此也应该缓存它们。
23.class 和 member id 在一定范围内是稳定的,但在动态加载的 class loader 下,保存全局的 class 要么可能失效,要么可能造成无法卸载classloader。
24.(*env)->FindClass()返回的jclass类型的变量是local referenced的,不能被缓存,因为一次JNI调用返回后它引用的已经回收了,下次在
调用JNI函数时出问题。
25.方法ID和域ID应该是可以缓存的(它不是引用,而且是稳定存在的)。
26.如果在用使用时缓存的 ID,要注意只要本地代码依赖于这个 ID 的值,那么这个类就不会被 unload。另外一方面,如果缓存发生在静态初
始化时,当类被 unload 或 reload 时,ID 会被重新计算。因此,尽量在类静态初始化时就缓存字段 ID、方法 ID。
27.有两种域ID的缓存方法:
静态缓存:在静态代码块中调用JNI native函数缓存。
使用时缓存:就是在正常调用native函数时进行缓存。
28.三种引用介绍
(1)局部引用:
通过 NewLocalRef() 和各种 JNI 接口创建(FindClass、NewObject、GetObjectClass和NewCharArray等)。LocalRef 会阻止 GC 回收所引用
的对象。JNI函数返回后局部引用所引用的对象会被 JVM 自动释放,或在JNI函数中调用 DeleteLocalRef 释放。(*env)->DeleteLocalRef(env,local_ref)
局部引用只有在创建它的本地方法返回前有效,本地方法返回到 Java 层之后,如果 Java 层没有对return的局部引用使用的话,局部引用就会被
JVM 自动释放。在JNI函数中将局部引用存储在静态变量中缓存起来,供下次调用时使用是错误的。
JNI 会将创建的局部引用都存储在一个局部引用表中,如果这个表超过了最大容量限制,就会造成局部引用表溢出,使程序崩溃。经测试,Android 上的
JNI 局部引用表最大数量是 512 个。不用时注意及时调用DeleteLocalRef().
JNI 提供了一系列函数来管理局部引用的生命周期。这些函数包括:EnsureLocalCapacity、NewLocalRef、PushLocalFrame、PopLocalFrame、DeleteLocalRef。
局部引用不能跨线程使用,只在创建它的线程有效。不要试图在一个线程中创建局部引用并存储到全局引用中,然后在另外一个线程中使用。
(2)全局引用:
调用 NewGlobalRef() 基于局部引用创建,会阻止 GC 回收所引用的对象。可以跨方法、跨线程使用。JVM 不会自动释放,必须调用 DeleteGlobalRef() 手
动释放。(*env)->DeleteGlobalRef(env, g_cls_string)
只能通过 NewGlobalRef 函数创建全局引用。
(3)弱全局引用:
调用 NewWeakGlobalRef() 基于局部引用或全局引用创建,不会阻止 GC 回收所引用的对象,可以跨方法、跨线程使用。引用不会自动释放,在 JVM 认为应
该回收它的时候(比如内存紧张的时候)进行回收而被释放。或调用DeleteWeakGlobalRef() 手动释放。(*env)->DeleteWeakGlobalRef(env,g_cls_string)
29.在管理局部引用的生命周期中,Push/PopLocalFrame 是非常方便且安全的。我们可以在本地函数的入口处调用PushLocalFrame,然后在出口处调用
PopLocalFrame,这样的话,在函数内任何位置创建的局部引用都会被释放。而且,这两个函数是非常高效的,强烈建议使用它们。需要注意的是,
如果在函数的入口处调用了PushLocalFrame,记住要在函数所有出口(有 return 语句出现的地方)都要调用 PopLocalFrame。
30.JNI函数签名
JNIEXPORT void JNICALL Java_HelloWorld_print(JNIEnv *, jobject); //这两个宏可以忽略
JNIEnv:JNIEnv接口指针指向包含指向函数表的指针的位置。函数表中的每个条目都指向一个JNI函数。本机方法始终通过其中一个JNI函数访问Java虚拟机中的数据结构。
jobject:是对HelloWorld对象本身的引用(有点像C ++中的“this”指针),若是java中的静态native方法,参数二是jclass.
JNIEXPORT和JNICALL宏(在jni.h头文件中定义)确保从本机库导出此函数和C编译器生成具有此函数的正确调用约定的代码。
在/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/jni_md.h中(jni.h中包含它):
#define JNIEXPORT __attribute__((visibility("default")))
#define JNICALL //空宏
31.编译本地方法
$ gcc -fPIC -shared HelloWorld.c -o libHelloWorld.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/
5.运行
$ export LD_LIBRARY_PATH=./ 或 setenv LD_LIBRARY_PATH . 或 不设置环境变量运行$ java -Djava.library.path=. HelloWorld
$ java HelloWorld
32.JNI以不同方式处理原始类型(java中的8种基本类型)和引用类型; 实例方法和静态方法。
33.C和C++使用JNI的区别
从jni.h中关于__cplusplus的条件编译来看,native中的类型和JNIenv中提供的函数都是不同的!
#ifdef __cplusplus
typedef JNIEnv_ JNIEnv; //C++使用这个JNIEnv函数集合,它里面的functions域内嵌了JNINativeInterface_!
#else
typedef const struct JNINativeInterface_ *JNIEnv; //C使用这个JNIEnv函数集合
#endif
二、试验Demo
1.互传基本类型测试
/* hello_world.c */
#include <jni.h>
#include <stdio.h>
#include "com_study_jnilearn_HelloWorld.h" JNIEXPORT jint JNICALL Java_com_study_jnilearn_HelloWorld_test
(JNIEnv *env, jclass cls, jshort s, jint i, jlong l, jfloat f, jdouble d, jchar c, jboolean z, jbyte b)
{
jint ret = ; printf("C: s=%hd, i=%d, l=%ld, f=%f, d=%lf, c=%c, z=%d, b=%d\n", s, i, l, f, d, c, z, b); return ret;
}
/* HelloWorld.java */
package com.study.jnilearn; public class HelloWorld { public static native int test(short s, int i, long l, float f, double d, char c, boolean z, byte b); public static void main(String[] args) {
short s = 1;
long l = 20;
byte b = 127;
int ret;
ret = test(s, 1, l, 1.0f, 10.5, 'A', true, b);
System.out.println("java: ret=" + ret);
} static {
System.loadLibrary("hello_world");
}
}
编译:
$ javac HelloWorld.java -d ./
$ javah -jni com.study.jnilearn.HelloWorld
$ gcc -shared -fPIC hello_world.c -I /usr/lib/jvm/java-1.7.-openjdk-amd64/include/ -o libhello_world.so
运行:
$ java com.study.jnilearn.HelloWorld
C: s=, i=, l=, f=1.000000, d=10.500000, c=A, z=, b=
java: ret=
2.互传String类型测试
/* StringTest.java */
package com.study.jnilearn; public class StringTest { public native static String sayHello(String text); public static void main(String[] args) {
System.out.println("Java send: Hello, I am Java");
String text = sayHello("Hello, I am Java");
System.out.println("Java get: " + text);
} static {
System.loadLibrary("string_test");
}
}
/* string_test.c */
#include <stdio.h>
#include <jni.h> JNIEXPORT jstring JNICALL Java_com_study_jnilearn_StringTest_sayHello
(JNIEnv *env, jclass cls, jstring j_str)
{
const char *str_get = NULL;
const char *str_send = "Hello I am C";
jboolean isCopy; //返回JNI_TRUE表示原字符串的拷贝,返回JNI_FALSE表示返回原字符串的指针 str_get = (*env)->GetStringUTFChars(env, j_str, &isCopy);
if(str_get == NULL) {
return NULL;
}
printf("C get: %s, isCopy=%d\n", str_get, isCopy);
(*env)->ReleaseStringUTFChars(env, j_str, str_get); printf("C send: %s\n", str_send);
return (*env)->NewStringUTF(env, str_send);
}
编译:
$ javac StringTest.java -d ./
$ javah -jni com.study.jnilearn.StringTest
$ gcc -shared -fPIC string_test.c -I /usr/lib/jvm/java-1.7.-openjdk-amd64/include/ -o libstring_test.so
运行:
$ java com.study.jnilearn.StringTest
Java send: Hello, I am Java
C get: Hello, I am Java, isCopy=
C send: Hello I am C
Java get: Hello I am C
3.基本类型数组类型双向传参
/*IntArray.java*/
class IntArray { private native int[] sortArray(int[] arr); public static void main(String[] args) {
IntArray obj = new IntArray();
int arr1[] = new int[10]; for (int i = 0; i < 10; i++) {
arr1[i] = i;
} int arr2[] = obj.sortArray(arr1); for (int i = 0; i < 10; i++) {
System.out.print(arr2[i] + " ");
}
System.out.println("");
} static {
System.loadLibrary("IntArray");
}
}
/* IntArray.c */
#include <jni.h>
#include <stdio.h>
#include <string.h>
#include "IntArray.h" void arr_short(jint *arr, jint num) {
jint i, j, tmp; for (i = ; i < num; i++) {
for (j = i; j < num; j++) {
if (arr[i] < arr[j]) {
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
}
} JNIEXPORT jintArray JNICALL Java_IntArray_sortArray
(JNIEnv *env, jobject obj, jintArray jarr)
{
jint *array, length;
jint buff[] = {};
array = (*env)->GetIntArrayElements(env, jarr, NULL);
if (array == NULL) {
return ; /* exception occurred */
}
length = (*env)->GetArrayLength(env, jarr);
memcpy(buff, array, length * sizeof(jint));
(*env)->ReleaseIntArrayElements(env, jarr, array, ); /*不能直接传jarr然后返回jarr,因为其是一个local reference, native调用后就被回收了*/
arr_short(buff, length); jintArray new_arr = (*env)->NewIntArray(env, length);
(*env)->SetIntArrayRegion(env, new_arr, , length, buff); return new_arr;
}
编译:
$ javac IntArray.java
$ javah -jni IntArray
$ gcc -shared -fPIC IntArray.c -I /usr/lib/jvm/java-1.7.-openjdk-amd64/include/ -o libIntArray.so
运行:
$ java IntArray
4.JNI函数中回调类的静态方法和实例方法
/* AccessMethod.java */
package com.study.jnilearn; class ClassMethod { private static int javaStaticCallback(String str, int i) {
int ret = 1;
System.out.format("Java: javaStaticCallback: str=%s " + " i=%d\n", str, i);
return ret;
} private int javaInstanceCallback(String str, int i) {
int ret = 2;
System.out.format("Java: javaInstanceCallback: str=%s " + "i=%d\n", str, i);
return ret;
}
} public class AccessMethod { public static native int javaStaticMethod();
public static native int javaInstaceMethod(); public static void main(String[] args) {
int ret1, ret2;
ret1 = javaStaticMethod();
ret2 = javaInstaceMethod();
System.out.println("ret1=" + ret1 + " ret2=" + ret2);
} static {
System.loadLibrary("AccessMethod");
}
}
/* AccessMethod.c */
#include <stdio.h>
#include "com_study_jnilearn_AccessMethod.h" JNIEXPORT jint JNICALL Java_com_study_jnilearn_AccessMethod_javaStaticMethod(JNIEnv *env, jclass cls)
{
jclass clazz = NULL;
jstring str_arg = NULL;
jmethodID method_id;
jint ret = ; // 1、从classpath路径下搜索ClassMethod这个类,并返回该类的Class对象
clazz =(*env)->FindClass(env, "com/study/jnilearn/ClassMethod");
if (clazz == NULL) {
printf("Couldn't find class: com/study/jnilearn/ClassMethod");
return;
} // 2、从clazz类中查找javaStaticCallback方法
method_id = (*env)->GetStaticMethodID(env, clazz, "javaStaticCallback", "(Ljava/lang/String;I)I");
if (method_id == NULL) {
printf("Couldn't find method: javaStaticCallback");
return;
} // 3、调用clazz类的callStaticMethod静态方法
str_arg = (*env)->NewStringUTF(env, "C: str pass to java static method");
ret = (*env)->CallStaticIntMethod(env, clazz, method_id, str_arg, ); // 4.删除局部引用
(*env)->DeleteLocalRef(env, clazz);
(*env)->DeleteLocalRef(env, str_arg); // id is not reference,needn't to delete return ret;
} JNIEXPORT jint JNICALL Java_com_study_jnilearn_AccessMethod_javaInstaceMethod(JNIEnv *env, jclass cls)
{
jclass clazz = NULL;
jobject jobj = NULL;
jmethodID mid_construct = NULL;
jmethodID mid_instance = NULL;
jstring str_arg = NULL;
jint ret = ; // 1、从classpath路径下搜索ClassMethod这个类,并返回该类的Class对象
clazz = (*env)->FindClass(env, "com/study/jnilearn/ClassMethod");
if (clazz == NULL) {
printf("Couldn't find class: com/study/jnilearn/ClassMethod");
return;
} // 2、获取类的默认构造方法ID
mid_construct = (*env)->GetMethodID(env,clazz, "<init>", "()V");
if (mid_construct == NULL) {
printf("Couldn't find construct method");
return;
} // 3、查找实例方法的ID
mid_instance = (*env)->GetMethodID(env, clazz, "javaInstanceCallback", "(Ljava/lang/String;I)I");
if (mid_instance == NULL) {
return;
} // 4、创建该类的实例
jobj = (*env)->NewObject(env, clazz, mid_construct);
if (jobj == NULL) {
printf("Couldn't create new object");
return;
} // 5、调用对象的实例方法
str_arg = (*env)->NewStringUTF(env, "C: str pass to java instance method");
ret = (*env)->CallIntMethod(env, jobj, mid_instance, str_arg, ); // 删除局部引用
(*env)->DeleteLocalRef(env, clazz);
(*env)->DeleteLocalRef(env, jobj);
(*env)->DeleteLocalRef(env, str_arg); return ret;
}
编译:
$ javac AccessMethod.java -d ./
$ javah -jni com.study.jnilearn.AccessMethod
$ gcc -shared -fPIC AccessMethod.c -I /usr/lib/jvm/java-1.7.-openjdk-amd64/include/ -o libAccessMethod.so
运行:
$ java com.study.jnilearn.AccessMethod
Java: javaStaticCallback: str=C: str pass to java static method i=
Java: javaInstanceCallback: str=C: str pass to java instance method i=
ret1= ret2=
5.JNI函数中使用静态和非静态成员属性
/* AccessField.java */
package com.study.jnilearn; class ClassField { private static int num;
private String str; public int getNum() {
return num;
} public void setNum(int num) {
this.num = num;
} public String getStr() {
return str;
} public void setStr(String str) {
this.str = str;
}
} public class AccessField { private native static void accessInstanceField(ClassField obj); private native static void accessStaticField(); public static void main(String[] args) {
ClassField obj = new ClassField();
obj.setNum(10);
obj.setStr("Hello"); // 本地代码访问和修改ClassField为中的静态属性num
accessStaticField();
accessInstanceField(obj); System.out.println("Java: ClassField.num = " + obj.getNum());
System.out.println("Java: ClassField.str = " + obj.getStr());
} static {
System.loadLibrary("AccessField");
}
}
#include "com_study_jnilearn_AccessField.h" JNIEXPORT void JNICALL Java_com_study_jnilearn_AccessField_accessInstanceField
(JNIEnv *env, jclass cls, jobject obj)
{
jclass clazz;
jfieldID fid;
jstring j_str;
jstring j_newStr;
const char *c_str = NULL; // 1.获取AccessField类的Class引用
clazz = (*env)->GetObjectClass(env, obj);
if (clazz == NULL) {
return;
} // 2. 获取AccessField类实例变量str的属性ID
fid = (*env)->GetFieldID(env, clazz, "str", "Ljava/lang/String;");
if (clazz == NULL) {
return;
} // 3. 获取实例变量str的值
j_str = (jstring)(*env)->GetObjectField(env, obj, fid); // 4. 将unicode编码的java字符串转换成C风格字符串
c_str = (*env)->GetStringUTFChars(env, j_str, NULL);
if (c_str == NULL) {
return;
}
printf("C: ClassField.str = %s\n", c_str);
(*env)->ReleaseStringUTFChars(env, j_str, c_str); // 5. 修改实例变量str的值
j_newStr = (*env)->NewStringUTF(env, "World");
if (j_newStr == NULL) {
return;
} (*env)->SetObjectField(env, obj, fid, j_newStr); // 6.删除局部引用
(*env)->DeleteLocalRef(env, clazz);
(*env)->DeleteLocalRef(env, j_str);
(*env)->DeleteLocalRef(env, j_newStr);
} JNIEXPORT void JNICALL Java_com_study_jnilearn_AccessField_accessStaticField
(JNIEnv *env, jclass cls)
{
jclass clazz;
jfieldID fid;
jint num; // 1.获取ClassField类的Class引用
clazz = (*env)->FindClass(env, "com/study/jnilearn/ClassField");
if (clazz == NULL) {
return;
} // 2.获取ClassField类静态变量num的属性ID
fid = (*env)->GetStaticFieldID(env, clazz, "num", "I");
if (fid == NULL) {
return;
} // 3.获取静态变量num的值
num = (*env)->GetStaticIntField(env, clazz, fid);
printf("C: ClassField.num = %d\n", num); // 4.修改静态变量num的值
(*env)->SetStaticIntField(env, clazz, fid, ); // 删除属部引用
(*env)->DeleteLocalRef(env, clazz);
}
编译:
$ javac AccessField.java -d ./
$ javah -jni com.study.jnilearn.AccessField
$ gcc -shared -fPIC AccessField.c -I /usr/lib/jvm/java-1.7.-openjdk-amd64/include/ -o libAccessField.so
运行:
$ java com.study.jnilearn.AccessField
C: ClassField.num =
C: ClassField.str = Hello
Java: ClassField.num =
Java: ClassField.str = World
6.优化:类静态初始化缓存
/* AccessCache.java */
public class AccessCache { public static native void initIDs(); public native void nativeMethod();
public void callback() {
System.out.println("Java: AccessCache.callback invoked!");
} public static void main(String[] args) {
AccessCache accessCache = new AccessCache();
accessCache.nativeMethod();
} static {
System.loadLibrary("AccessCache");
initIDs();
}
}
#include <stdio.h>
#include "AccessCache.h" jmethodID MID_AccessCache_callback; JNIEXPORT void JNICALL Java_AccessCache_initIDs(JNIEnv *env, jclass cls)
{
printf("initIDs called!\n");
MID_AccessCache_callback = (*env)->GetMethodID(env, cls, "callback", "()V");
} JNIEXPORT void JNICALL Java_AccessCache_nativeMethod(JNIEnv *env, jobject obj)
{
printf("C: call java's callback()\n");
(*env)->CallVoidMethod(env, obj, MID_AccessCache_callback);
}
编译:
javac AccessCache.java
javah -jni AccessCache
$ gcc -shared -fPIC AccessCache.c -I /usr/lib/jvm/java-1.7.-openjdk-amd64/include/ -o libAccessCache.so
运行:
$ java AccessCache
initIDs called!
C: call java's callback()
Java: AccessCache.callback invoked!
jni.pdf翻译总结版:http://wiki.jikexueyuan.com/project/jni-ndk-developer-guide/recommend.html
JNI学习笔记_Java调用C —— 非Android中使用的方法的更多相关文章
- JNI学习笔记_Java调用C —— Android中使用的方法
一.笔记 1.JNI(Java Native Interface),就是如何使用java去访问C/C++编写的那些库.若想深入了解JNI可以看官方文档jni.pdf.优秀博文:Android JNI知 ...
- JNI学习笔记_C调用Java
一.笔记 1.C调用Java中的方法,参考jni.pdf pg97可以参考博文:http://blog.csdn.net/lhzjj/article/details/26470999步骤: a. 创建 ...
- Android:日常学习笔记(2)——分析第一个Android应用程序
Android:日常学习笔记(2)——分析第一个Android应用程序 Android项目结构 整体目录结构分析 说明: 除了APP目录外,其他目录都是自动生成的.APP目录的下的内容才是我们的工作重 ...
- WebSocket学习笔记IE,IOS,Android等设备的兼容性问
WebSocket学习笔记IE,IOS,Android等设备的兼容性问 一.背景 公司最近准备将一套产品放到Andriod和IOS上面去,为了统一应用的开发方式,决定用各平台APP嵌套一个HTML5浏 ...
- Git学习笔记(二) · 非典型性程序猿
远程库的使用 前面说到的都是git在本地的操作,那么实际协作开发过程中我们肯定是要有一个远程版本库作为项目的核心版本库,也就是投入生产使用的版本.这里我们以 Github为例.Github是一个开放的 ...
- C# 动态生成word文档 [C#学习笔记3]关于Main(string[ ] args)中args命令行参数 实现DataTables搜索框查询结果高亮显示 二维码神器QRCoder Asp.net MVC 中 CodeFirst 开发模式实例
C# 动态生成word文档 本文以一个简单的小例子,简述利用C#语言开发word表格相关的知识,仅供学习分享使用,如有不足之处,还请指正. 在工程中引用word的动态库 在项目中,点击项目名称右键-- ...
- Vue.js学习笔记:在元素 和 template 中使用 v-if 指令
f 指令 语法比较简单,直接上代码: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" " ...
- 用juery的ajax方法调用aspx.cs页面中的webmethod方法示例
juery的ajax调用aspx.cs页面中的webmethod方法:首先在 aspx.cs文件里建一个公开的静态方法,然后加上WebMethod属性,具体实现如下,感兴趣的朋友可以参考下哈,希望对大 ...
- Javscript调用iframe框架页面中函数的方法
Javscript调用iframe框架页面中函数的方法,可以实现iframe之间传值或修改值了, 访问iframe里面的函数: window.frames['CallCenter_iframe'].h ...
随机推荐
- Java——继承的运行顺序
首先看一个代码 父类代码: public class Parent { { System.out.println("Parent非静态代码块"); } static { Syste ...
- Thread线程notify方法的自我理解
感谢博主:http://zy19982004.iteye.com/blog/1626916 这篇博文给予我线程知识很大的帮助 知识背景:(1)wait().notify()均是Object的方法,故每 ...
- 预热ASP.NET MVC 的View
ASP.NET MVC 的View 预设是Load on Demand(按需加载),也就是说View 第一次要Render 的时候才会去载入跟编译,这个就会造成一个现象,即使Web 应用程式已经完成启 ...
- Codeforces Round #162 (Div. 1) B. Good Sequences (dp+分解素数)
题目:http://codeforces.com/problemset/problem/264/B 题意:给你一个递增序列,然后找出满足两点要求的最长子序列 第一点是a[i]>a[i-1] 第二 ...
- js中级
闭包:函数在调用的时候,会形成一个私有作用域,内部的变量不会被访问, 这种保护机制叫闭包.这就意味着函数调用完毕,这个函数形成的栈内存会被销毁. 重点 函数归属谁跟他在哪调用没有关系,跟在哪定义有关. ...
- Python机器学习(基础篇---监督学习(k近邻))
K近邻 假设我们有一些携带分类标记的训练样本,分布于特征空间中,对于一个待分类的测试样本点,未知其类别,按照‘近朱者赤近墨者黑’,我们需要寻找与这个待分类的样本在特征空间中距离最近的k个已标记样本作为 ...
- 下载MNIST数据集脚本input_data源码
# Copyright 2015 Google Inc. All Rights Reserved.## Licensed under the Apache License, Version 2.0 ( ...
- [Hive安装问题]
启动Hive时出现: Exception in thread "main" java.lang.RuntimeException: java.lang.IllegalArgumen ...
- 2017-10-5模拟赛T2 小Z爱排序(sorting.*)
Description Solution 比赛时找到了规律,但是没有证出来……(当然最后还是AC了……) 显然没有被操作的数在排好序的序列中一定是连续的一段. 所以,没有被操作的数一定从左到右连续地递 ...
- 树莓派安装tensorflow1.11
树莓派3B+ 环境:2018-11-13-raspbian-stretch 初始状态 首先将本地更新一下和安装 sudo apt-get update sudo apt-get upgrade 然后更 ...