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
}

0 0
原创粉丝点击