细说JNI与NDK专题目录:
细说JNI与NDK(一) 初体验
细说JNI与NDK(二)基本操作)
细说JNI与NDK(三)ndk 配置说明
细说JNI与NDK(四)动态和静态注册
细说JNI与NDK(五)JNI 线程
细说JNI与NDK(六)静态缓存,异常捕获,内置函数
细说JNI与NDK(七)Parcel底层JNI思想与OpenCV简单对比
简单概念关系
JNI :ava Native Interface,即 Java 本地接口,JNI 是 Java 调用 Native 语言的一种特性,属于 Java 的,与 Android 无直接关系
作用: 使得 Java 与 本地其他类型语言(如 C、C++)交互。
实际中的驱动都是 C/C++开发的,通过 JNI,Java 可以调用 C/c++实现的驱动,从 而扩展 Java 虚拟机的能力。另外,在高效率的数学运算、游戏的实时渲染、音 视频的编码和解码等方面,一般都是用 C 开发的
(Java 代码 里调用 C/C++等语言代码 或 C/C++代码调用 Java 代码)
JNI相当于桥梁
Kotlin/Java <---> JNI <---> C/C++
NDK Android 平台提供的 Native 开发工具集开发包 Native Development Kit。把 JNI,拿到 NDK 里面来并进行封装 (JNI,gcc,g++,...)
环境
jni.h 的引入,两个jni环境,一个是NDK对应的工具集中的jni.h。一个是JDK对应的环境中的jni.h
我们引入使用的是NDK中的jni.h
需要我们安装好本地Java环境。利用javah 手动生成说明原理。正常开发都是用Android Studio 自动快速生成,不可能在刀耕火种。
我们先创建一个Activity,的native方法演示
public class JavaJNIActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
public static final int A = 100;
//Java 本地方法 实现:native层--->this
public native String getStringPwd();
//native static---->class
public static native String getStringPwd2();
//我们用native 修改java字段
public String name = "David";
public static int age = 0;
// java交互
public native void changeName();
public static native void changeAge();
public native void callAddMethod();
// 函数,native调用java
public int add(int num1, int num2) {
return num1 + num2;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_java_jni);
changeName();
TextView tv = findViewById(R.id.name_text);
tv.setText(name);
changeAge();
TextView tva = findViewById(R.id.age_text);
tva.setText(age + "");
callAddMethod();
}
}
用命令 javah 包名.类,生成头文件 top_zcwfeng_jni_JavaJNIActivity.h
javah top.zcwfeng.jni.JavaJNIActivity
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class top_zcwfeng_jni_JavaJNIActivity */
#ifndef _Included_top_zcwfeng_jni_JavaJNIActivity
#define _Included_top_zcwfeng_jni_JavaJNIActivity
#ifdef __cplusplus
extern "C" {
#endif
#undef top_zcwfeng_jni_JavaJNIActivity_A
#define top_zcwfeng_jni_JavaJNIActivity_A 100L
/*
* Class: top_zcwfeng_jni_JavaJNIActivity
* Method: getStringPwd
* Signature: ()Ljava/lang/String;
*/
extern "C"
JNIEXPORT jstring JNICALL Java_top_zcwfeng_jni_JavaJNIActivity_getStringPwd
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
定义了final int A 就会生成一个宏。具体看上述注释
C++是可以重载的,所以用external C 声明用C的语法,不允许重载
接下来我们需要实现函数 其他方法就不定义在.h ,只是演示javah 的用法,实际开发不怎么用,直接在cpp实现
#include "top_zcwfeng_jni_JavaJNIActivity.h"
#include <android/log.h>
#define LOG_TAG "native_zcw"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
// 实现 native
extern "C" JNIEXPORT jstring
JNICALL Java_top_zcwfeng_jni_JavaJNIActivity_getStringPwd
(JNIEnv *env, jobject jobj){
}
// static native
extern "C"
JNIEXPORT jstring JNICALL
Java_top_zcwfeng_jni_JavaJNIActivity_getStringPwd2(JNIEnv *env, jclass clazz) {
}
//--------------------------
extern "C"
JNIEXPORT void JNICALL
Java_top_zcwfeng_jni_JavaJNIActivity_changeName(JNIEnv *env, jobject thiz) {
// 获取class
jclass jcs = env->GetObjectClass(thiz);
// jfieldID GetFieldID(jclass clazz, const char* name 方法名, const char* sig属性签名)
jfieldID jfid = env->GetFieldID(jcs,"name","Ljava/lang/String;");
// jobject GetObjectField(jobject obj, jfieldID fieldID),用到了指针转换
jstring j_str = static_cast<jstring>(env->GetObjectField(thiz, jfid));
// 打印字符串
char * c_str = const_cast<char *>(env->GetStringUTFChars(j_str, NULL));
LOGD("native: %s",c_str);
// 修改Tesla,必须写出jstring才能转换
jstring jName = env->NewStringUTF("Tesla");
env->SetObjectField(thiz,jfid,jName);
}
//--------------------------
extern "C"
JNIEXPORT void JNICALL
Java_top_zcwfeng_jni_JavaJNIActivity_changeAge(JNIEnv *env, jclass clazz) {
const char * sig = "I";
jfieldID jfid = env->GetStaticFieldID(clazz,"age",sig);
jint age = env->GetStaticIntField(clazz,jfid);
age += 10;
//jint----int
env->SetStaticIntField(clazz,jfid,age);
}
//--------------------------
extern "C"
JNIEXPORT void JNICALL
Java_top_zcwfeng_jni_JavaJNIActivity_callAddMethod(JNIEnv *env, jobject jobj) {
// 自己得到class
jclass javaJNIActivityClass = env->GetObjectClass(jobj);
// jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
jmethodID jmid = env->GetMethodID(javaJNIActivityClass,"add","(II)I");
// 调用Java 方法
jint sum = env->CallIntMethod(jobj,jmid,3,4);
LOGE("add sum result ->java:%d",sum);
}
至于为设么用external "C" 需要看Jni.h 源码
jni.h 中我们能看到这部分代码
#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif
else C的方式,JNINativeInterface 结构体指针
struct JNINativeInterface {
void* reserved0;
void* reserved1;
void* reserved2;
void* reserved3;
......
};
JNINativeInterface 这里面所有的函数就是我们要用到的
C++ 中的 _JNIEnv,最终调用的还是C中JNINativeInterface
struct _JNIEnv {
/* do not rename this; it does not seem to be entirely opaque */
const struct JNINativeInterface* functions;
......
无论是C/C++最终还是调用的C,所以必须用external "C"
- JNIEXPORT // 标记该方法可以被外部调用
- jstring // Java <---> native 转换用的
- JNICALL // 代表是 JNI标记
- Java_包名类名方法名 ,注意:我们的包名:" _" ---> native :" _1"
- JNIEnv * env JNI:的桥梁环境 300多个函数,所以的JNI操作,必须靠他
- jobject jobj 谁调用,就是谁的实例 MainActivity this
- jclass clazz 谁调用,就是谁的class MainActivity.class
- android 打印需要log---》#include <android/log.h>
- 定义宏进行打印
#define LOG_TAG "native_zcw"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
- jint----int 查看源码最终jint最终也是int
- 代码演示
① Java-JNI 声明 static 和 非 static方法的区别
② 定义final int 会在C中定义宏方式体现
③ JNI在C,android 打印Log的方式
④ 用native 修改java字段
⑤ java通过Jni与native交互,java调用native实现方法。native调用java实现并传参
JNI 常用签名规则
签名规则 大写
javap -s -p MainActivity 必须是.class
Java的boolean --- Z 注意点
Java的byte --- B
Java的char --- C
Java的short --- S
Java的int --- I
Java的long --- J 注意点
Java的float --- F
Java的double --- D
Java的void --- V
Java的引用类型 --- Lxxx/xxx/xx/类名;
Java的String --- Ljava/lang/String;
Java的array int[] --- [I double[][][] --- [[[D
int add(char c1, char c2) ---- (CC)I
void a() === ()V
javap -s -p xxx.class -s 输出xxxx.class的所有属性和方法的签名, -p 忽略私有公开的所有属性方法全部输出
现在Android Studio插件更加智能,我们知道规则就好,配置好环境签名是有提示的。
2021-04-14 00.48.56.png
网友评论