JNI开发基础篇:Java类调用C库或者方法

来源:互联网 发布:access2007数据库教程 编辑:程序博客网 时间:2024/05/18 06:43

Maybe i walk slowly,but i will never give up

创建新的工程

在JNI开发中与C语言的结合,所以我们在创建新的项目的时候,建议添加C库的支持。(因为会有代码自动提示)如下:
这里写图片描述

勾选 Include C++ Support

然后一路next。在于平常创建项目中不同的是最后会有一个这样的选择:
这里写图片描述

下面的两个框可选可不选。
Exceptions Support(异常报错)
Runtime Type Information Support(运行支持)

创建完的工程如下:

这里写图片描述

附:在这里说一下不同的开发环境所遇到不同的问题。AS:2.2.2 或者更高ndk:version 16.0sdk:该版本下创建的工程,项目中会自动帮我们生成cpp文件夹,并且在项目中的`build.gradle`中默认配置Cmark。
like this:android {   ···    defaultConfig {       ···        externalNativeBuild {            cmake {                cppFlags ""            }        }    }    externalNativeBuild {        cmake {            path "CMakeLists.txt"        }    }}
并且生成一个默认的文件`CMakeLists.txt`
like this:文件中前面有 `#`的均是说明信息,我直接在这里删掉了cmake_minimum_required(VERSION 3.4.1)add_library(              native-lib                #指定生成os文件的名称,但是注意,在androidstudio中生             #成的os文件会在指定名前面添加 lib 关键词,所以我们平常             #看到的os文件都是类似`libxxxxxx.os`             src/main/cpp/native-lib.cpp             #项目中C/C++的源代码位置,如果有多个,可以接着写下去             #eg:src/main/jni/test.c             #eg: src/main/jni/JNI.c )find_library(               log-lib              log )target_link_libraries( # Specifies the target library.                       # 指定目标library                       # 解释说明我们可以自定多个library。                       native-lib                       # Links the target library to the log library                       # included in the NDK.                       ${log-lib} )
这就是我目前相应版本下创建项目中的内容。之前的1.xx版本是没有这些自动生成文件,是需要自己在`build.gradle`中自己配置脚本like this:
//配置ndk        ndk{            moduleName "native-lib"//os文件名称            abiFilters "arm64-v8a","x86","x86_64"            //支持CPU,默认会生成目前android手机所有可能的CPU            ldLibs "log" //支持log输出        }

正题:
Java 类 调用 C 方法、函数

1:创建一个java类:

package com.wedfrend.jni;public class JNI {    /**     * 调用C语言中的加法运算     * @return     */    public native int add();    /**     * 检查密码是否正确     * @param pwd     * @return  正确,返回200 错误返回500     */    public native int CheckPassWord(String pwd);}

这里所有的java方法,必须使用nativie关键字。

2:创建c中的方法。

2.1:直接在java的方法中,选中方法,按住alt+enter按钮。根据提示生成

这里写图片描述

这样生成的方法在另外一个文件中会生成如下的方法:

/**1:这个方法便是JIN调用的时候,在C语言中应该声明的方法2:JNIEXPORT  ---> 自动生成,可删除3:jint    ---->  经过JNI协议规定,返回值。等值于 java中的int4:JNICALL ----> 可删除5:Java_   ----> JNI协议,必须添加6:com_wedfrend_jni_    ---> 所在类的包名,java中的 . 变化为 _7: INI_  --->类名8:add  ---> 方法名9: (JNIEnv *env, jobject instance) 两个固定参数,*env JNI中与c之间调用的主要二级指针。  instance 上下文参数*/JNIEXPORT jint JNICALLJava_com_wedfrend_jni_JNI_add(JNIEnv *env, jobject instance) {    // TODO}
2.2:自己知道上面的书写方式,自己写。2.3:使用如下方法。
2.3.1cmd --》进入到项目中的java文件夹下,调用javah com.wedfrend.jni.JNIF:\ASTestApplication\JNIApplications\app\src\main\java>javah com.wedfrend.jni.JNI错错误误: 编编码码GBK的的不不可可映映射射字字符符错错误误: 编编码码GBK的的不不可可映映射射字字符符错错误误: 编编码码GBK的的不不可可映映射射字字符符编码错误是因为我们使用了中文,不过没什么影响。如果使用英文写注释,则不会出现这种错误了。这样会生成一个.h文件,如图:

这里写图片描述

打开如下:

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_wedfrend_jni_JNI */#ifndef _Included_com_wedfrend_jni_JNI#define _Included_com_wedfrend_jni_JNI#ifdef __cplusplusextern "C" {#endif/* * Class:     com_wedfrend_jni_JNI * Method:    add * Signature: ()I */JNIEXPORT jint JNICALL Java_com_wedfrend_jni_JNI_add  (JNIEnv *, jobject);/* * Class:     com_wedfrend_jni_JNI * Method:    CheckPassWord * Signature: (Ljava/lang/String;)I */JNIEXPORT jint JNICALL Java_com_wedfrend_jni_JNI_CheckPassWord  (JNIEnv *, jobject, jstring);#ifdef __cplusplus}#endif#endif
说明:JNIEXPORT jint JNICALL Java_com_wedfrend_jni_JNI_CheckPassWord (JNIEnv *, jobject, jstring);  和上面的解释一样,之所以多了个参数,那是因为我们java方法中带参

不过这样的生成是没有参数值,只有参数类型,需要自己添加

2.3.2

这里写图片描述

debug处,使用cmd进入到该文件目录下,执行

javah com.wedfrend.jni.JNI

同样会生成一个头文件。

以上3中方法便是如何生成JNI下的.h方法

step3:在项目中创建一个jni文件夹,与java同等级的jni文件夹,并将.h文件拖入jni问价夹中

这里写图片描述

step4: 在jni中创建一个c源文件,然后引入头文件

这里写图片描述

此时你在打开 java类的时候,不会报错,反而会出现
这里写图片描述

按钮,点击之后便可以直接跳转到头文件中。

step5:在C中实现方法功能。

#include <string>#include "com_wedfrend_jni_JNI.h"/**下面的是引入打印模式,在c语言中可以执行输出内容*/#include "android/log.h"#define LOG_TAG "com.wedfrend.cNative"#define LOGD(...)__android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)#define LOGI(...)__android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)#define LOGE(...)__android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)/** * 两个数相加 */JNIEXPORT jint JNICALL Java_com_wedfrend_jni_JNI_add        (JNIEnv * env, jobject jobj){    int a = 10;    int b = 5l;    return a+b;};/** * 验证密码是否正确 */JNIEXPORT jint JNICALL Java_com_wedfrend_jni_JNI_CheckPassWord        (JNIEnv * env, jobject instance, jstring jstr){    /*讲一个字符串转化为char指针*/    //const char *userPsw = env->GetStringUTFChars(jstr, 0);    const char *userPsw = (*env)->GetStringUTFChars(env,jstr,0);    char* basePsw = "123456";    LOGD("userPsw---------%d\n",*userPsw);    /**    *strcmp string库中提供比较两个字符串相等的方法。    *返回一个int类型值,这边很有意思的。    */    if(strcmp(userPsw,basePsw)==0){        return 200;    }else{        return 500;    }};

6:activity或者class中直接调用C语言中的方法:

6.1
TextView tv = (TextView) findViewById(R.id.sample_text);/*实例化你的native类,并且需要在该类中添加动态加载库的方法     {        System.loadLibrary("native");    }*/ tv.setText(new JNI().CheckPassWord("123456")+"");
6.2在JNI类中动态加载库,如下:
package com.wedfrend.jni;public class JNI {    {        System.loadLibrary("native-lib");    }    /**     * 调用C语言中的加法运算     * @return     */    public native int add();    /**     * 检查密码是否正确     * @param pwd     * @return  正确,返回200 错误返回500     */    public native int CheckPassWord(String pwd);    public native String getName(String name);}

7:运行结果展示:

/*
打印输出功能查看:

12-18 15:34:42.229 7964-7964/com.wedfrend D/com.wedfrend.cNative: userPsw———123456
12-18 15:34:42.239 7964-7964/com.wedfrend D/SecWifiDisplayUtil: Metadata value : SecSettings2
*/

生成的os文件:
这里写图片描述

JNI开发基础篇:C语言调用Java中的方法