前言
本章主要记录学习中的JNI简单调用和传值
简介
JNI是Java Native Interface的简称,学会JNI我们可以在开发过程中调用一些使用C/C++编写的库从而实现一些音视频相关、图片渲染、I/O操作等一些功能
开发工具环境准备
Android Studio
CMake :一款跨平台的构建工具,与gradle可以构建原生库
NDK:Android提供的一款支持c/c++的工具集
环境准备好之后我们通过AS可以创建一个最简单模版JNI项目:
根据提示点击下一步进行创建完成:
public class MainActivity extends AppCompatActivity {
// 1.加载native库
static {
System.loadLibrary("nativestudy");
}
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
TextView tv = binding.sampleText;
tv.setText(stringFromJNI());
}
//2.定义native 函数
public native String stringFromJNI();
}
native-lib.cpp文件内容:
#include <jni.h>
#include <string>
extern "C" //添加c语言支持
//JNIEXPORT 与 JNICALL 是 JNI 中定义的两个宏 ,作用是告知虚拟机这是一个返回值是jstring类型jni函数
JNIEXPORT jstring JNICALL
//方法名格式为,native方法所在类的包名+方法名使用下划线连接,注意必须和包名+方法名一致!!!
Java_com_phuket_tour_nativestudy_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
运行成功之后,显示"Hello from C++".
现在有这样一个需求:需要Android把值传递到native层。
怎么办?
不要慌先看看传递什么类型的数据。
数据映射:
基本数据类型:byte、 short 、int 、long 、char、 float 、double 、boolean
先说基本数据类型,java的基本数据类型可以与c/c++基本数据类型直接映射,如下图所示:
java类型 | jni类型 | c/c++类型 |
byte | jbyte | char(有符号8位整型) |
short | jshort | short(有符号16位整型) |
int | jint | int(有符号32位整型) |
long | jlong | long(有符号64位整型) |
char | jchar | unsigned short(无符号16位整型) |
float | jfloat | float(有符合32位浮点型) |
double | jdouble | double(有符合64位双精度型) |
boolean | jboolean | unsigned char(无符号8位整型) |
直接来操作基本数据类型,定义native函数:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("nativestudy");
}
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
putValuesToJNI(
(byte) 1,
(short) 2,
3,
4,
'a',
5.0f,
6.0d,
true
);
}
//声明native函数
public native void putValuesToJNI(
byte b,
short s,
int i,
long l,
char c,
float f,
double d,
boolean bo
);
}
然后根据映射表,在JNI中接收这些数据:
#include <jni.h>
#include <string>
#include <android/log.h>
#define TAG "native-lib"
#define LOG_TAG "native-lib"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
extern "C"
JNIEXPORT void JNICALL
Java_com_phuket_tour_nativestudy_MainActivity_putValuesToJNI(
JNIEnv *env,
jobject,
jbyte jbyte1,
jshort jshort1,
jint jint1,
jlong jlong1,
jchar jchar1,
jfloat jfloat1,
jdouble jdouble1,
jboolean jboolean1
) {
char c_byte = jbyte1;
LOGD("jbyte-------->%d", c_byte);
short s_short = jshort1;
LOGD("jshort-------->%d", s_short);
int i_int = jint1;
LOGD("jint-------->%d", i_int);
long l_long = jlong1;
LOGD("jlong-------->%d", l_long);
char c_char = jchar1;
LOGD("jchar-------->%d", c_char);
float f_float = jfloat1;
LOGD("jfloat-------->%f", f_float);
double d_double = jdouble1;
LOGD("jdouble-------->%f", jdouble1);
unsigned char b_boolean = jboolean1;
LOGD("jboolean-------->%d", jboolean1);
}
点击运行:
引用数据类型:String、Class、Object、Object[]和一些类
引用数据类型映射如下图。列举几个常用的类型,它们的数据结构不能直接和native代码产生映射。
java | native |
java.long.class | jclass |
java.long.string | jstring |
java.long.Throwable | jthrowable |
java.long.Object[] | jobjectArray |
Other object | jobject |
byte[] | jbyteArray |
short[] | jshortArray |
int[] | jintArray |
long[] | jlongArray |
float[] | jfloatArray |
double[] | jdoubleArray |
char[] | jcharArray |
Other arrays | jarray |
1.定义native方法
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("nativestudy");
}
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
putRefValueToJNI(
"我来自Java",
new byte[]{1, 2, 3},
new short[]{4, 5, 6},
new int[]{7, 8, 9},
new long[]{10, 11, 12},
new float[]{1f, 2f, 3f},
new String[]{"a", "b", "c"},
new boolean[]{true, true, false},
new Book("《道德经》", 100)
);
}
public native void putRefValueToJNI(
String name,
byte[] bytes,
short[] shorts,
int[] i,
long[] longs,
float[] floats,
String[] str,
boolean[] bool,
Book book
);
}
创建Java数据类
public class Book {
private String name;
private int num;
public Book() {
}
public Book(String name, int num) {
this.name = name;
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", num=" + num +
'}';
}
}
定义jni方法
extern "C"
JNIEXPORT void JNICALL
Java_com_phuket_tour_nativestudy_MainActivity_putRefValueToJNI(
JNIEnv *env,
jobject thiz,
jstring jstring1,
jbyteArray jbyteArray1,
jshortArray jshortArray1,
jintArray jintArray1,
jlongArray jlongArray1,
jfloatArray jfloatArray1,
jobjectArray strArray,
jbooleanArray jbooleanArray1,
jobject book) {
//接收Java传递的String类型
const char *j_string = env->GetStringUTFChars(jstring1, 0);
LOGD("string----->%s", j_string);
//接收byte[]
jbyte *byteArray = env->GetByteArrayElements(jbyteArray1, NULL);
//获取数组长度
jsize byteArraySize = env->GetArrayLength(jbyteArray1);
for (int i = 0; i < byteArraySize; ++i) {
LOGD("byte数组:第%d个值========%d", i, byteArray[i]);
}
//释放数组
env->ReleaseByteArrayElements(jbyteArray1, byteArray, 0);
//接收short[]
jshort *shortArray = env->GetShortArrayElements(jshortArray1, NULL);
//获取数组长度
jsize shortArraySize = env->GetArrayLength(jshortArray1);
for (int i = 0; i < byteArraySize; ++i) {
LOGD("short数组:第%d个值========%d", i, shortArray[i]);
}
//释放数组
env->ReleaseShortArrayElements(jshortArray1, shortArray, 0);
//.... int[] long[] float[]与上面byte和short[]类似
//获取String[]
jsize jsize1 = env->GetArrayLength(strArray);
for (int i = 0; i < jsize1; ++i) {
jobject str_obj = env->GetObjectArrayElement(strArray, i);
//强制转换 jni string
jstring j_string = static_cast<jstring>(str_obj);
//转换成 C string类型
const char *valueStr = env->GetStringUTFChars(j_string, 0);
LOGD("String[%d]的值=====%s", i, valueStr);
//回收
env->ReleaseStringUTFChars(j_string, valueStr);
}
//获取 boolean[]
jsize bool_size = env->GetArrayLength(jbooleanArray1);
jboolean *j_boolean = env->GetBooleanArrayElements(jbooleanArray1, 0);
for (int i = 0; i < bool_size; ++i) {
bool b1 = j_boolean[i];
jboolean b2 = j_boolean[i];
LOGD("boolean:%d", b1);
LOGD("jboolean:%d", b2);
}
//回收
env->ReleaseBooleanArrayElements(jbooleanArray1, j_boolean, 0);
//获取 Object 对象 Java bean Book
//1.类名全路径
const char *book_class_str = "com/phuket/tour/nativestudy/Book";
//2.根据类名路径获取 class 对象
jclass book_class = env->FindClass(book_class_str);
//3.获取方法签名 此签名是在jvm中存储的命名 使用javap -a命令可以获取 获取步骤在文章末尾图
const char *sign = "()Ljava/lang/String;";
jmethodID jmethodId = env->GetMethodID(book_class, "getName", sign);
//调用book的getName() 获取到 object对象
jobject obj_string = env->CallObjectMethod(book, jmethodId);
//强转成字符串
jstring bookNameStr = static_cast<jstring>(obj_string);
const char *targetStr = env->GetStringUTFChars(bookNameStr, NULL);
LOGD("Book的名字: %s", targetStr);
env->DeleteLocalRef(book_class);
env->DeleteLocalRef(book);
}
运行结果:
获取类方法签名步骤如下:
总结
学习没有捷径只有多加练习才能更好的掌握这项技能 通过不同数据类型,可以轻松实现JNI传值