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.0
sdk:
该版本下创建的工程,项目中会自动帮我们生成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中的int
4:JNICALL ----> 可删除
5:Java_ ----> JNI协议,必须添加
6:com_wedfrend_jni_ ---> 所在类的包名,java中的 . 变化为 _
7: INI_ --->类名
8:add ---> 方法名
9: (JNIEnv *env, jobject instance) 两个固定参数,*env JNI中与c之间调用的主要二级指针。 instance 上下文参数
*/
JNIEXPORT jint JNICALL
Java_com_wedfrend_jni_JNI_add(JNIEnv *env, jobject instance) {
// TODO
}
2.2:自己知道上面的书写方式,自己写。
2.3:使用如下方法。
2.3.1
cmd --》进入到项目中的java文件夹下,调用javah com.wedfrend.jni.JNI
F:\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 __cplusplus
extern "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文件: