JNI基础01
来源:互联网 发布:黑门市场 知乎 编辑:程序博客网 时间:2024/06/05 20:48
1.搭建开发环境
下载NDK, 最新版本android-ndk-r9.Windows 32-bit 版本下载地址:http://dl.google.com/android/ndk/android-ndk-r9-windows-x86.zipWindows 64-bit 版本下载地址:http://dl.google.com/android/ndk/android-ndk-r9-windows-x86_64.zip解压压缩包.配置环境变量.
2.JNI第一个HelloWorld程序
MainActivity.java:
public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 加载动态库,把文件名前面的lib和后面的.so去掉 System.loadLibrary("hello-jni");}public void javaCallC(View v) { // 调用本地方法,就是调用c中的方法 String result = sayHello(); Toast.makeText(getApplicationContext(), result, 0).show();}// 定义本地方法,对应了C程序中的一个方法,在java中调用C的方法时就是调用通过这个方法来调用的public native String sayHello();}
hello.c:
#include <stdio.h>#include <stdlib.h>#include <jni.h>//env 二级指针,它对应的结构体中定义很多类型和函数指针,在开发jni程序时需要用到//obj java程序中调用本地方法的类对象jstring Java_com_itheima_jnihello_MainActivity_sayHello(JNIEnv* env,jobject obj){ // 返回一个字符串 char* text = "i'm from C!!!!"; // 把c的字符串转换成jstring类型:jstring (*NewStringUTF)(JNIEnv*, const char*); jstring jstr = (*env)->NewStringUTF(env,text); return jstr;}
3.开发jni程序流程
1.在java代码中写一个本地方法:必须以public native开头2.在工程的根目录下创建jni文件夹3.在jni文件夹下创建c文件,在里面导入了3个头文件: #include <stdio.h> #include <stdlib.h> #include <jni.h>4.在c文件中写一个c方法: //env 二级指针,它对应的结构体中定义很多类型和函数指针,在开发jni程序时需要用到 //obj java程序中调用本地方法的类对象 jstring Java_com_itheima_jnihello_MainActivity_sayHello(JNIEnv* env,jobject obj){ // 返回一个字符串 char* text = "i'm from C!!!!"; // 把c的字符串转换成jstring类型:jstring (*NewStringUTF)(JNIEnv*, const char*); jstring jstr = (*env)->NewStringUTF(env,text); return jstr; }5.在jni文件夹下创建Android.mk文件: LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello-jni LOCAL_SRC_FILES := hello.c include $(BUILD_SHARED_LIBRARY)6.在jni文件夹下创建Application.mk文件:为生成能够在所有cpu下可以运行的so文件 APP_ABI := all7.打开命令行窗口,切换到工程的根目录下,执行ndk-build命令,生成动态库8.在java代码中加载动态库: // 加载动态库,把文件名前面的lib和后面的.so去掉 System.loadLibrary("hello-jni");9.在java代码中调用本地方法: public void javaCallC(View v) { // 调用本地方法,就是调用c中的方法 String result = sayHello(); Toast.makeText(getApplicationContext(), result, 0).show(); }
4.jni开发常见错误
1.缺少Android.mk文件, 在jni目录下创建一个Android.mk文件.Android NDK: Your APP_BUILD_SCRIPT points to an unknown file: ./jni/Android.mk2.c文件没有include导入jni.h的头文件, 导致某些类型找不到."Compile thumb : itheima31 <= Hello.cjni/Hello.c:4:1: error: unknown type name 'JNIEXPORT'jni/Hello.c:4:19: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'JNICALL'jni/Hello.c:4:19: error: unknown type name 'JNICALL'3.方法的形参没有指定名称. 加上名字就可以了.jni/Hello.c:6:3: error: parameter name omitted4.没有加载动态库.so文件.No implementation found for native Lcom/itheima31/commonerrordemo/MainActivity;.helloFromC ()Ljava/lang/String;5.加载动态链接库错误, 名字写错. 加载时一定要写: Android.mk文件中的LOCAL_MODULE对应的名字.Caused by: java.lang.UnsatisfiedLinkError: Couldn't load libitheima31.so: findLibrary returned null
6、 确定加载.so库文件的名字没有错误, 还是报一下错误. 是模拟器问题. 把生成的arm下的.so文件防盗x86模拟器上运行就报此错误.
Caused by: java.lang.UnsatisfiedLinkError: Couldn't load itheima31: findLibrary returned null 解决方法: 在jni目录下创建一个Application.mk文件, 声明以下内容: APP_ABI := all // 当前只能在x86的模拟器上运行, 因为生成的so文件是x86机器的机器码 APP_ABI := x86
5.Android.mk文件说明
// 设置本地路径,指向工程根目录下的jni目录LOCAL_PATH := $(call my-dir)// 把上次编译时产生的变量值清除掉,然后再重新赋值,重新编译include $(CLEAR_VARS)// 指定动态库的名称LOCAL_MODULE := hello-jni// 指定c文件,就是要把这个c文件打包成soLOCAL_SRC_FILES := hello.c//调用本地方法执行构建动态库的命令,生成的动态库格式.so,// BUILD_STATIC_LIBRARY 构建静态库的命令,生成的静态库文件的格式.ainclude $(BUILD_SHARED_LIBRARY)
6.简便的开发流程
1.在java代码中创建本地方法;2.第一次开发JNI程序,先检查eclipse中是否配置的NDK的根目录: window->preference->Android-NDK-NDK Location:NDK的根目录3.右键工程-Android Tools- add native support,在弹出的对话框中设置动态库的名称,点击finish,会自动创建了jni目录,里面已经创建 Android.mk和cpp文件。4.把cpp文件改成c文件,把mk文件中的cpp文件也改成c文件。5.打开命令行窗口,切换到工程的src目录下,执行javah 全类名,生成头文件;6.把头文件拖到jni目录下;7.右键工程-properties-C/C++ General-Path and Sympols-includes-add-File system-D:\develops\android-ndk-r9d\platforms\android-16\arch-mips\usr\include;8.在c文件导入三个头文件: #include <stdio.h> #include <stdlib.h> #include "com_itheima_simplejni_MainActivity.h"9.从头文件中拷贝方法到c文件中,然后实现方法体;10.在jni文件夹下添加Application.mk文件;11.在c/c++视图中,选择工程,点eclipse上面的小锤子图标,生成动态库;12.在java代码中加载动态库,调用本地方法。
7.java传递数据给c
JNI.java:
public class JNI { static { // 加载动态库 System.loadLibrary("PassDataDemo"); } public native int add(int x, int y); public native String sayHelloInC(String s); public native int[] arrElementsIncrease(int[] intArray); }
PassDataDemo.c:
#include <stdio.h>#include <stdlib.h>#include "com_itheima_passdatademo_JNI.h"/** * 把jstring类型的字符串转换成char* 类型的字符串 */char* _JString2CStr(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = (*env)->FindClass(env, "java/lang/String"); jstring strencode = (*env)->NewStringUTF(env, "GB2312"); jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B"); jbyteArray barr = (jbyteArray) (*env)->CallObjectMethod(env, jstr, mid, strencode); // String .getByte("GB2312"); jsize alen = (*env)->GetArrayLength(env, barr); jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE); if (alen > 0) { rtn = (char*) malloc(alen + 1); //"\0" memcpy(rtn, ba, alen); rtn[alen] = 0; } (*env)->ReleaseByteArrayElements(env, barr, ba, 0); return rtn;}JNIEXPORT jint JNICALL Java_com_itheima_passdatademo_JNI_add (JNIEnv * env, jobject obj, jint x , jint y){ jint result = x + y; return result;}JNIEXPORT jstring JNICALL Java_com_itheima_passdatademo_JNI_sayHelloInC (JNIEnv * env, jobject obj, jstring jstr){ // 在jstr后面拼接一个字符串,然后返回 // 1.创建字符串,用于拼接到jstr后面 char* text ="bingbing"; // 2.把text拼接到jstr后面 char* cStr = _JString2CStr(env,jstr); // 拼接字符串:把text拼接到cStr后面 strcat(cStr,text); //3.把cStr转换成jstring类型:jstring (*NewStringUTF)(JNIEnv*, const char*); jstring result = (*env)->NewStringUTF(env,cStr); // 4.返回拼接后的字符串 return result;}
8.使用c语言校验密码
JNIEXPORT jint JNICALL Java_com_itheima_passdatademo_JNI_checkPasswordInC(JNIEnv * env, jobject obj, jstring jstr){ //使用jstr与正确密码比较,返回结果值 // 1.创建一个字符串作为正确的密码 char* password = "abc"; // 2.把jstr转换成c中的字符串,然后比较字符串 char* cStr = _JString2CStr(env,jstr); // 比较字符串 int result = strcmp(cStr,password); LOGE("result==%d\n",result); return result;}
9.回调java中的方法
JNIEXPORT void JNICALL Java_com_itheima_calljavaincdemo_JNI_callHelloFromJavaInC( JNIEnv * env, jobject obj) { // 使用反射的机制调用JNI.java中的public void helloFromJava()方法 // 1.得到class对象:jclass (*FindClass)(JNIEnv*, const char*); char* path = "com/itheima/calljavaincdemo/JNI"; jclass class = (*env)->FindClass(env, path); // 2.得到method对象:jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID methodId = (*env)->GetMethodID(env, class, "helloFromJava", "()V"); // 3.得到Java类的对象:jobject (*AllocObject)(JNIEnv*, jclass); jobject jobj = (*env)->AllocObject(env, class); // 4.调用对象的方法:void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); (*env)->CallVoidMethod(env, jobj, methodId, NULL);}JNIEXPORT void JNICALL Java_com_itheima_calljavaincdemo_JNI_callAddInC( JNIEnv * env, jobject obj) { // 使用反射的机制调用JNI.java中的public int add(int x,int y)方法 // 1.得到class对象:jclass (*FindClass)(JNIEnv*, const char*); char* path = "com/itheima/calljavaincdemo/JNI"; jclass class = (*env)->FindClass(env, path); // 2.得到method对象:jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID methodId = (*env)->GetMethodID(env, class, "add", "(II)I"); // 3.得到Java类的对象:jobject (*AllocObject)(JNIEnv*, jclass); jobject jobj = (*env)->AllocObject(env, class); // 4.调用对象的方法:jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...); jint result = (*env)->CallIntMethod(env, jobj, methodId, 10, 20); LOGE("result==============%d\n", result);}JNIEXPORT void JNICALL Java_com_itheima_calljavaincdemo_JNI_callPrintStringInC( JNIEnv * env, jobject obj) { // 使用反射的机制调用JNI.java中的public void printString(String s)方法 // 1.得到class对象:jclass (*FindClass)(JNIEnv*, const char*); char* path = "com/itheima/calljavaincdemo/JNI"; jclass class = (*env)->FindClass(env, path); // 2.得到method对象:jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID methodId = (*env)->GetMethodID(env, class, "printString", "(Ljava/lang/String;)V"); // 3.得到Java类的对象:jobject (*AllocObject)(JNIEnv*, jclass); jobject jobj = (*env)->AllocObject(env, class); // 4.调用对象的方法:void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); char* text = "i'm from c!!!!!"; // 把c中的字符串转换成jstring:jstring (*NewStringUTF)(JNIEnv*, const char*); jstring jstr = (*env)->NewStringUTF(env, text); (*env)->CallVoidMethod(env, jobj, methodId, jstr);}
10.回调java中的静态方法
JNIEXPORT void JNICALL Java_com_itheima_calljavaincdemo_JNI_callSayHelloInC( JNIEnv * env, jobject obj) {//使用反射的机制调用JNI.java中的public static void sayHello(String text)方法// 1.得到class对象:jclass (*FindClass)(JNIEnv*, const char*);char* path = "com/itheima/calljavaincdemo/JNI";jclass class = (*env)->FindClass(env, path);// 2.得到method对象:jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);jmethodID methodId = (*env)->GetStaticMethodID(env, class, "sayHello", "(Ljava/lang/String;)V");// 3.得到Java类的对象:jobject (*AllocObject)(JNIEnv*, jclass);//jobject jobj = (*env)->AllocObject(env,class);// 4.调用对象的方法:void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);char* text = "i'm from c!!!!!";// 把c中的字符串转换成jstring:jstring (*NewStringUTF)(JNIEnv*, const char*);jstring jstr = (*env)->NewStringUTF(env, text);(*env)->CallStaticVoidMethod(env, class, methodId, jstr);
}
11.回调MainActivity中的showToast方法
#include <stdio.h>#include <stdlib.h>#include "com_itheima_showtoastdemo_MainActivity.h"JNIEXPORT void JNICALL Java_com_itheima_showtoastdemo_MainActivity_callShowToastInC (JNIEnv * env, jobject obj){ // 使用反射机制调用MainActivity.java中的 public void showToast(String str)方法 // 1.得到class对象:jclass (*FindClass)(JNIEnv*, const char*); char* path = "com/itheima/showtoastdemo/MainActivity"; jclass class = (*env)->FindClass(env,path); // 2.得到方法对象:jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID methodId = (*env)->GetMethodID(env,class,"showToast","(Ljava/lang/String;)V"); // 3.得到类对象 // 方法中的obj参数就是MainActivity对象,所以不用调用AllocObject函数指针得到了 // 4.调用方法:void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); char* text = "hello from c!!!"; jstring jstr = (*env)->NewStringUTF(env,text); (*env)->CallVoidMethod(env,obj,methodId,jstr);}
dos中签名
F:\Android-ADT\day04\returnPass\bin\classes>javap -s com.heima.returnpass.JNI
Compiled from “JNI.java”
public class com.heima.returnpass.JNI {
static {};
Signature: ()V
public com.heima.returnpass.JNI();
Signature: ()V
public void helloFromJava();
Signature: ()V
public int add(int, int);
Signature: (II)I
public void printString(java.lang.String);
Signature: (Ljava/lang/String;)V
public static void sayHello(java.lang.String);
Signature: (Ljava/lang/String;)V
public native void callHelloFromJavaInC();
Signature: ()V
public native void callAddInC();
Signature: ()V
public native void callPrintStringInC();
Signature: ()V
public native void callSayHelloInC();
Signature: ()V
}
- 01-JNI-基础
- JNI基础01
- JNI基础
- JNI基础
- JNI基础
- JNI基础
- JNI基础
- JNI基础
- jni基础
- Android JNI基础:什么是JNI
- Android JNI基础篇
- Android Jni基础学习
- JNI基础ABC
- JNI 入门基础
- java基础---JNI原理
- Android JNI开发基础
- Android基础--jni开发
- 安卓JNI基础
- JDO快速入门
- java方法的重载
- C语言入门
- 如何获得当前类的字段,方法参数泛型的类型
- 最简单的 NSIS 函数返回值的返回与接收实例
- JNI基础01
- 如何反射获得父类的泛型类型
- HTTP协议详解
- BZOJ 3522 & 4543: [POI2014]Hotel
- Python内置函数_反射类
- Method.getParameterAnnotations()研究
- Linux下pppoe设置
- teamviewer 5分钟断线 隔几分钟断线 最新解决办法 专治5分钟限制 完美破解
- 2016工作总结MX