日期:
来源:郭霖收集编辑:小余的自习室
https://juejin.cn/post/7204445469098410039
Android abi
构建工具
$GNUMAKE -f <ndk>/build/core/build-local.mk
BUILD_STATIC_LIBRARY: 构建静态库 PREBUILT_STATIC_LIBRARY: 对已有的静态库进行包装,使其成为一个模块。 BUILD_SHARED_LIBRARY:构建动态库、 PREBUILT_SHARED_LIBRARY: 对已有的静态库进行包装,使其成为一个模块。 BUILD_EXECUTABLE: 构建可执行文件。
APP_ABI := armeabi-v7a arm64-v8a x86
cmake_minimum_required(VERSION 3.10.2)
Reading
file(READ <filename> <out-var> [...])
file(STRINGS <filename> <out-var> [...])
file(<HASH> <filename> <out-var>)
file(TIMESTAMP <filename> <out-var> [...])
file(GET_RUNTIME_DEPENDENCIES [...])
Writing
file({WRITE | APPEND} <filename> <content>...)
file({TOUCH | TOUCH_NOCREATE} [<file>...])
file(GENERATE OUTPUT <output-file> [...])
file(CONFIGURE OUTPUT <output-file> CONTENT <content> [...])
...
find_file(myfile
${CMAKE_CURRENT_SOURCE_DIR}"/include/SerialPort.h)
add_library( # Sets the name of the library.
dy-register-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).//这里可以使用多个文件
dy_register.cpp people/people.cpp)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
target_link_libraries( # Specifies the target library.
dy-register-lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
link_libraries(${log-lib} //目标库名称)
target_include_directories(log-lib //目标库名称
PUBLIC //库类型
${CMAKE_CURRENT_SOURCE_DIR}/include //目标include文件夹路径)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include)
set(SRC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/include/SerialPort.h)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src _SRC_FILES)
add_library( # Sets the name of the library.
dy-register-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).//这里可以使用多个文件
${_SRC_FILES})
General messages
message([<mode>] "message text" ...)
mode:FATAL_ERROR SEND_ERROR WARNING,DEBUG等值。用法和logcat类似
Reporting checks
message(<checkState> "message text" ...)
checkState:CHECK_START CHECK_PASS CHECK_FAIL等值
Configure Log
message(CONFIGURE_LOG <text>...)
android {
defaultConfig {
externalNativeBuild {
// For ndk-build, instead use the ndkBuild block.
cmake {
//声明当前Cmake项目使用的Android abi
abiFilters "armeabi-v7a"
//提供给Cmake的参数信息 可选
arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"
//提供给C编译器的一个标志 可选
cFlags "-D__STDC_FORMAT_MACROS"
//提供给C++编译器的一个标志 可选
cppFlags "-fexceptions", "-frtti","-std=c++11"
//指定哪个so库会被打包到apk中去 可选
targets "libexample-one", "my-executible-demo"
}
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt" //声明cmake配置文件路径
version "3.10.2" //声明cmake版本
}
}
}
怎么选择正确的so?
sourceSets {
main {
jniLibs.srcDirs = ['jniLibs']
}
}
Java层如何调用so文件中的函数?
System.load("/data/local/tmp/libnative_lib.so");
System.loadLibrary("native_lib");
System.loadLibrary()
Runtime.loadLibrary()
Runtime.doLoad()
Runtime_nativeLoad()
LoadNativeLibrary()
dlopen()
dlsym()
JNI_OnLoad()
什么是JNI
JNI注册方式
package com.android.myapplication;
...
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
private native String stringFromJNI();
static {
System.loadLibrary("native-lib");
}
}
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_android_myapplication_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
#include <jni.h>
#include <string>
#include <assert.h>
extern "C" JNIEXPORT jstring JNICALL
Java_com_android_myapplication_MainActivity_stringFromJNI1(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
#define JNI_CLASS_NAME "com/android/myapplication/MainActivity" //java路径
static JNINativeMethod gMethods[] = {
{"stringFromJNI","()Ljava/lang/String;",(void *)Java_com_android_myapplication_MainActivity_stringFromJNI1},
};
int register_dynamic_Methods(JNIEnv *env){
std::string s = JNI_CLASS_NAME;
const char* className = s.c_str();
jclass clazz = env->FindClass(className);
if(clazz == NULL){
return JNI_FALSE;
}
//注册JNI方法
if(env->RegisterNatives(clazz,gMethods,sizeof(gMethods)/sizeof(gMethods[0]))<0){
return JNI_FALSE;
}
return JNI_TRUE;
}
//类加载时会调用到这里
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
if(vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK){
return JNI_ERR;
}
assert(env != NULL);
if(!register_dynamic_Methods(env)){
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
JNI基础语法
#define JNI_FALSE 0
#define JNI_TRUE 1
typedef jobject jclass;
class _jobject {};
class _jclass : public _jobject {};
// ...
typedef _jobject *jobject;
typedef _jclass *jclass;
struct _jfieldID; /* opaque structure */
typedef struct _jfieldID *jfieldID; /* field IDs */
struct _jmethodID; /* opaque structure */
typedef struct _jmethodID *jmethodID; /* method IDs */
long f (int n, String s, int[] arr);
(I;Ljava/lang/String;[I)J
JavaVM *jvm; /* already set */
f()
{
JNIEnv *env;
(*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
... /* use env */
}
C风格:(*env)->NewStringUTF(env, “Hellow World!”);
C++风格:env->NewStringUTF(“Hellow World!”);
#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif
struct _JNIEnv {
/* do not rename this; it does not seem to be entirely opaque */
const struct JNINativeInterface* functions;
#if defined(__cplusplus)
jint GetVersion()
{ return functions->GetVersion(this); }
jclass DefineClass(const char *name, jobject loader, const jbyte* buf,
jsize bufLen)
{ return functions->DefineClass(this, name, loader, buf, bufLen); }
jclass FindClass(const char* name)
{ return functions->FindClass(this, name); }
...
...
struct JNINativeInterface {
/*获取当前JNI版本信息:*/
jint (*GetVersion)(JNIEnv *);
/*
定义一个类:类是从某个字节数组吧buf中读取出来的
原型:jclass DefineClass(JNIEnv *env, const char *name, jobject loader,
const jbyte *buf, jsize bufLen);
*/
jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*,
jsize);
/*
找到某个类:
函数原型:
jclass FindClass(JNIEnv *env, const char *name);
参数name:为类的全限定名
如String类:"java/lang/String"
如java.lang.Object[] : "[Ljava/lang/Object;"
*/
jclass (*FindClass)(JNIEnv*, const char*);
/*
获取当前类的父类:
通常在使用FindClass获取到类之后,再调用这个函数
*/
jclass (*GetSuperclass)(JNIEnv*, jclass);
/*
函数原型:
jboolean IsAssignableFrom(JNIEnv *env, jclass clazz1,jclass clazz2);
定义某个类clazz1是否可以安全的强制转换为另外一个类clazz2
*/
jboolean (*IsAssignableFrom)(JNIEnv*, jclass, jclass);
/*检测是否发生了异常*/
jboolean (*ExceptionCheck)(JNIEnv*);
/*检测是否发生了异常,并返回异常*/
jthrowable (*ExceptionOccurred)(JNIEnv*);
/*打印出异常描述栈*/
void (*ExceptionDescribe)(JNIEnv*);
/*清除异常*/
void (*ExceptionClear)(JNIEnv*);
/* 抛出一个异常 成功返回0,失败返回其他值*/
jint (*Throw)(JNIEnv*, jthrowable);
/* 创建一个新的Exception,并制定message,然后抛出*/
jint (*ThrowNew)(JNIEnv *, jclass, const char *);
/*抛出一个FatalError*/
void (*FatalError)(JNIEnv*, const char*);
/*创建一个全局的引用,需要在不使用的时候调用DeleteGlobalRef解除全局引用*/
jobject (*NewGlobalRef)(JNIEnv*, jobject);
/*删除全局引用*/
void (*DeleteGlobalRef)(JNIEnv*, jobject);
/*删除局部引用*/
void (*DeleteLocalRef)(JNIEnv*, jobject);
/*是否是同一个Object*/
jboolean (*IsSameObject)(JNIEnv*, jobject, jobject);
/*创建一个局部引用*/
jobject (*NewLocalRef)(JNIEnv*, jobject);
/*在不调用构造函数的情况下,给jclass创建一个Java对象,注意该方法不能用在数组的情况*/
jobject (*AllocObject)(JNIEnv*, jclass);
/*创建一个Object,对于jmethodID参数必须使用GetMethodID获取到构造函数(with <init> as the method name and void (V) as the return type)*/
jobject (*NewObject)(JNIEnv*, jclass, jmethodID, ...);
jobject (*NewObjectV)(JNIEnv*, jclass, jmethodID, va_list);
jobject (*NewObjectA)(JNIEnv*, jclass, jmethodID, const jvalue*);
/*获取到当前对象的class类型*/
jclass (*GetObjectClass)(JNIEnv*, jobject);
/*某个对象是否是某个类的实现对象,和Java中instanceof类似*/
jboolean (*IsInstanceOf)(JNIEnv*, jobject, jclass);
/*获取某个类的方法类型id,非静态方法
原型:jfieldID GetFieldID(JNIEnv *env, jclass clazz,const char *name, const char *sig);
clazz:类权限定名
name:为方法名
sig:为方法签名描述
*/
jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
/*调用某个对象的方法
jobject:对象
jmethodID:对象的方法
返回值:jobject
*/
jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
jobject (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jobject (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
/*调用某个对象的方法
jobject:对象
jmethodID:对象的方法
返回值:jboolean
同理后面的CallByteMethod,CallCharMethodV,CallIntMethod只是返回值不一样而已。
*/
jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...);
jboolean (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jboolean (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);
jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jchar (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...);
jchar (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jchar (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jshort (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...);
jshort (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jshort (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
jint (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jint (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jlong (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);
jlong (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jlong (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jfloat (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...);
jfloat (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jfloat (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
jdouble (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...);
jdouble (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jdouble (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list);
void (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, const jvalue*);
/*
返回一个类的非静态属性id
原型:jfieldID GetFieldID(JNIEnv *env, jclass clazz,
const char *name, const char *sig);
参数name:属性的名字
sig:属性的签名
*/
jfieldID (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);
/*
获取当前类的某个属性值
同理:对于后面的GetShortField,GetBooleanField,GetByteField等只是属性的类型不一样。
在使用GetFieldID得到jfieldID属性id后,就可以使用Get<type>Field获取属性值。
*/
jobject (*GetObjectField)(JNIEnv*, jobject, jfieldID);
jboolean (*GetBooleanField)(JNIEnv*, jobject, jfieldID);
jbyte (*GetByteField)(JNIEnv*, jobject, jfieldID);
jchar (*GetCharField)(JNIEnv*, jobject, jfieldID);
jshort (*GetShortField)(JNIEnv*, jobject, jfieldID);
jint (*GetIntField)(JNIEnv*, jobject, jfieldID);
jlong (*GetLongField)(JNIEnv*, jobject, jfieldID);
jfloat (*GetFloatField)(JNIEnv*, jobject, jfieldID);
jdouble (*GetDoubleField)(JNIEnv*, jobject, jfieldID);
/*
设置当前类的某个属性值
同理:对于后面的BooleanField,SetByteField,SetShortField等只是属性的类型不一样。
在使用GetFieldID得到jfieldID属性id后,就可以使用Set<type>Field设置对应属性值。
*/
void (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject);
void (*SetBooleanField)(JNIEnv*, jobject, jfieldID, jboolean);
void (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte);
void (*SetCharField)(JNIEnv*, jobject, jfieldID, jchar);
void (*SetShortField)(JNIEnv*, jobject, jfieldID, jshort);
void (*SetIntField)(JNIEnv*, jobject, jfieldID, jint);
void (*SetLongField)(JNIEnv*, jobject, jfieldID, jlong);
void (*SetFloatField)(JNIEnv*, jobject, jfieldID, jfloat);
void (*SetDoubleField)(JNIEnv*, jobject, jfieldID, jdouble);
/*
获取某个类的静态方法id
*/
jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
/*
调用某个类的静态方法
同理:后面的CallStaticBooleanMethod,CallStaticByteMethod等方法只是返回类型不一样而已。
*/
jobject (*CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...);
jobject (*CallStaticObjectMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jobject (*CallStaticObjectMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jboolean (*CallStaticBooleanMethod)(JNIEnv*, jclass, jmethodID, ...);
jboolean (*CallStaticBooleanMethodV)(JNIEnv*, jclass, jmethodID,
va_list);
jboolean (*CallStaticBooleanMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jbyte (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...);
jbyte (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jbyte (*CallStaticByteMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jchar (*CallStaticCharMethod)(JNIEnv*, jclass, jmethodID, ...);
jchar (*CallStaticCharMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jchar (*CallStaticCharMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jshort (*CallStaticShortMethod)(JNIEnv*, jclass, jmethodID, ...);
jshort (*CallStaticShortMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jshort (*CallStaticShortMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jint (*CallStaticIntMethod)(JNIEnv*, jclass, jmethodID, ...);
jint (*CallStaticIntMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jint (*CallStaticIntMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jlong (*CallStaticLongMethod)(JNIEnv*, jclass, jmethodID, ...);
jlong (*CallStaticLongMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jlong (*CallStaticLongMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jfloat (*CallStaticFloatMethod)(JNIEnv*, jclass, jmethodID, ...);
jfloat (*CallStaticFloatMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jfloat (*CallStaticFloatMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
jdouble (*CallStaticDoubleMethod)(JNIEnv*, jclass, jmethodID, ...);
jdouble (*CallStaticDoubleMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jdouble (*CallStaticDoubleMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
void (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list);
void (*CallStaticVoidMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
//获取静态属性的id
jfieldID (*GetStaticFieldID)(JNIEnv*, jclass, const char*,
const char*);
/*
获取某个类的静态属性的值:
同理:GetStaticBooleanField,GetStaticByteField等后续函数都只是属性的类型不一样而已
*/
jobject (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID);
jboolean (*GetStaticBooleanField)(JNIEnv*, jclass, jfieldID);
jbyte (*GetStaticByteField)(JNIEnv*, jclass, jfieldID);
jchar (*GetStaticCharField)(JNIEnv*, jclass, jfieldID);
jshort (*GetStaticShortField)(JNIEnv*, jclass, jfieldID);
jint (*GetStaticIntField)(JNIEnv*, jclass, jfieldID);
jlong (*GetStaticLongField)(JNIEnv*, jclass, jfieldID);
jfloat (*GetStaticFloatField)(JNIEnv*, jclass, jfieldID);
jdouble (*GetStaticDoubleField)(JNIEnv*, jclass, jfieldID);
/*
设置某个类的静态属性的值
同理:SetStaticObjectField,SetStaticBooleanField只是设置的值属性类型不同罢了*/
void (*SetStaticObjectField)(JNIEnv*, jclass, jfieldID, jobject);
void (*SetStaticBooleanField)(JNIEnv*, jclass, jfieldID, jboolean);
void (*SetStaticByteField)(JNIEnv*, jclass, jfieldID, jbyte);
void (*SetStaticCharField)(JNIEnv*, jclass, jfieldID, jchar);
void (*SetStaticShortField)(JNIEnv*, jclass, jfieldID, jshort);
void (*SetStaticIntField)(JNIEnv*, jclass, jfieldID, jint);
void (*SetStaticLongField)(JNIEnv*, jclass, jfieldID, jlong);
void (*SetStaticFloatField)(JNIEnv*, jclass, jfieldID, jfloat);
void (*SetStaticDoubleField)(JNIEnv*, jclass, jfieldID, jdouble);
/*
从一段unicode字符串中创建一个String对象
原型:jstring NewString(JNIEnv *env, const jchar *unicodeChars,jsize len);
*/
jstring (*NewString)(JNIEnv*, const jchar*, jsize);
/*获取String对象的字符串长度,字符串是默认的UNICODE*/
jsize (*GetStringLength)(JNIEnv*, jstring);
/*
将jstring转换为一个Unicode字符串数组的指针,在调用ReleaseStringChars之前,这个指针都是有效的
原型:const jchar * GetStringChars(JNIEnv *env, jstring string,jboolean *isCopy);
*/
const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*);
/*释放一个Unicode字符串数组的指针*/
void (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*);
/*创建一个string对象,使用的字符串是UTF-8类型*/
jstring (*NewStringUTF)(JNIEnv*, const char*);
/*获取UTF-8类型的jstring对象的长度*/
jsize (*GetStringUTFLength)(JNIEnv*, jstring);
/* JNI spec says this returns const jbyte*, but that's inconsistent */
/*
返回一个string类型的utf-8类型字符串的指针。生命周期是在调用ReleaseStringUTFChars之前。
原型:const char * GetStringUTFChars(JNIEnv *env, jstring string,jboolean *isCopy);*/
const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
/*释放GetStringUTFChars获取到的指针*/
void (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*);
/*获取一个数组对象的长度*/
jsize (*GetArrayLength)(JNIEnv*, jarray);
/*创建一个Object类型的数组对象
原型:jobjectArray NewObjectArray(JNIEnv *env, jsize length,jclass elementClass, jobject initialElement);
elementClass:对象类型
initialElement:对象初始化元素*/
jobjectArray (*NewObjectArray)(JNIEnv*, jsize, jclass, jobject);
/*获取某个数组对象索引上的元素,最后一个参数为索引位置*/
jobject (*GetObjectArrayElement)(JNIEnv*, jobjectArray, jsize);
/*设置某个数组对象索引上的元素,倒数第二个参数为索引位置*/
void (*SetObjectArrayElement)(JNIEnv*, jobjectArray, jsize, jobject);
/*创建一个Boolean类型的数组对象,长度为jsize*/
jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize);
/*创建一个Byte类型的数组对象,长度为jsize*/
jbyteArray (*NewByteArray)(JNIEnv*, jsize);
jcharArray (*NewCharArray)(JNIEnv*, jsize);
jshortArray (*NewShortArray)(JNIEnv*, jsize);
jintArray (*NewIntArray)(JNIEnv*, jsize);
jlongArray (*NewLongArray)(JNIEnv*, jsize);
jfloatArray (*NewFloatArray)(JNIEnv*, jsize);
jdoubleArray (*NewDoubleArray)(JNIEnv*, jsize);
/*获取Boolean数组对象的第一个对象的地址指针:注意和ReleaseBooleanArrayElements配合使用
函数原型:NativeType *Get<PrimitiveType>ArrayElements(JNIEnv *env,ArrayType array, jboolean *isCopy);
isCopy:当前返回的数组对象可能是Java数组的一个拷贝对象
*/
jboolean* (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*);
/*获取Byte数组对象的第一个对象的地址指针*/
jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
/*同上*/
jchar* (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*);
jshort* (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*);
jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
jlong* (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*);
jfloat* (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*);
jdouble* (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*);
//是否数组对象内存
void (*ReleaseBooleanArrayElements)(JNIEnv*, jbooleanArray,
jboolean*, jint);
void (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray,
jbyte*, jint);
void (*ReleaseCharArrayElements)(JNIEnv*, jcharArray,
jchar*, jint);
void (*ReleaseShortArrayElements)(JNIEnv*, jshortArray,
jshort*, jint);
void (*ReleaseIntArrayElements)(JNIEnv*, jintArray,
jint*, jint);
void (*ReleaseLongArrayElements)(JNIEnv*, jlongArray,
jlong*, jint);
void (*ReleaseFloatArrayElements)(JNIEnv*, jfloatArray,
jfloat*, jint);
void (*ReleaseDoubleArrayElements)(JNIEnv*, jdoubleArray,
jdouble*, jint);
/*将一个数组区间的值拷贝到一个新的地址空间,然后返回这个地址空间的首地址,最后一个参数为接收首地址用
函数原型:
void Get<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array,jsize start, jsize len, NativeType *buf);
*/
void (*GetBooleanArrayRegion)(JNIEnv*, jbooleanArray,
jsize, jsize, jboolean*);
void (*GetByteArrayRegion)(JNIEnv*, jbyteArray,
jsize, jsize, jbyte*);
void (*GetCharArrayRegion)(JNIEnv*, jcharArray,
jsize, jsize, jchar*);
void (*GetShortArrayRegion)(JNIEnv*, jshortArray,
jsize, jsize, jshort*);
void (*GetIntArrayRegion)(JNIEnv*, jintArray,
jsize, jsize, jint*);
void (*GetLongArrayRegion)(JNIEnv*, jlongArray,
jsize, jsize, jlong*);
void (*GetFloatArrayRegion)(JNIEnv*, jfloatArray,
jsize, jsize, jfloat*);
void (*GetDoubleArrayRegion)(JNIEnv*, jdoubleArray,
jsize, jsize, jdouble*);
/* spec shows these without const; some jni.h do, some don't */
/*设置某个数组对象的区间的值*/
void (*SetBooleanArrayRegion)(JNIEnv*, jbooleanArray,
jsize, jsize, const jboolean*);
void (*SetByteArrayRegion)(JNIEnv*, jbyteArray,
jsize, jsize, const jbyte*);
void (*SetCharArrayRegion)(JNIEnv*, jcharArray,
jsize, jsize, const jchar*);
void (*SetShortArrayRegion)(JNIEnv*, jshortArray,
jsize, jsize, const jshort*);
void (*SetIntArrayRegion)(JNIEnv*, jintArray,
jsize, jsize, const jint*);
void (*SetLongArrayRegion)(JNIEnv*, jlongArray,
jsize, jsize, const jlong*);
void (*SetFloatArrayRegion)(JNIEnv*, jfloatArray,
jsize, jsize, const jfloat*);
void (*SetDoubleArrayRegion)(JNIEnv*, jdoubleArray,
jsize, jsize, const jdouble*);
/*注册JNI函数*/
jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,
jint);
/*反注册JNI函数*/
jint (*UnregisterNatives)(JNIEnv*, jclass);
/*加同步锁*/
jint (*MonitorEnter)(JNIEnv*, jobject);
/*释放同步锁*/
jint (*MonitorExit)(JNIEnv*, jobject);
/*获取Java虚拟机VM*/
jint (*GetJavaVM)(JNIEnv*, JavaVM**);
/*获取uni-code字符串区间的值,并放入到最后一个参数首地址中*/
void (*GetStringRegion)(JNIEnv*, jstring, jsize, jsize, jchar*);
/*获取utf-8字符串区间的值,并放入到最后一个参数首地址中*/
void (*GetStringUTFRegion)(JNIEnv*, jstring, jsize, jsize, char*);
/*
1.类似Get/Release<primitivetype>ArrayElements这两个对应函数,都是获取一个数组对象的地址,但是返回是void*,所以是范式编程,可以返回任何对象的首地址,而Get/Release<primitivetype>ArrayElements是指定类型的格式。
2.在调用GetPrimitiveArrayCcritical之后,本机代码在调用ReleasePrimitiveArray Critical之前不应长时间运行。我们必须将这对函数中的代码视为在“关键区域”中运行。在关键区域中,本机代码不得调用其他JNI函数,或任何可能导致当前线程阻塞并等待另一个Java线程的系统调用。(例如,当前线程不能对另一个Java线程正在编写的流调用read。)*/
void* (*GetPrimitiveArrayCritical)(JNIEnv*, jarray, jboolean*);
void (*ReleasePrimitiveArrayCritical)(JNIEnv*, jarray, void*, jint);
/*功能类似 Get/ReleaseStringChars,但是功能会有限制:在由Get/ReleaseStringCritical调用包围的代码段中,本机代码不能发出任意JNI调用,或导致当前线程阻塞
函数原型:const jchar * GetStringCritical(JNIEnv *env, jstring string, jboolean *isCopy);
*/
const jchar* (*GetStringCritical)(JNIEnv*, jstring, jboolean*);
void (*ReleaseStringCritical)(JNIEnv*, jstring, const jchar*);
//创建一个弱全局引用
jweak (*NewWeakGlobalRef)(JNIEnv*, jobject);
//删除一个弱全局引用
void (*DeleteWeakGlobalRef)(JNIEnv*, jweak);
/*检查是否有挂起的异常exception*/
jboolean (*ExceptionCheck)(JNIEnv*);
/*
创建一个ByteBuffer对象,参数address为ByteBuffer对象首地址,且不为空,capacity为ByteBuffe的容量
函数原型:jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity);*/
jobject (*NewDirectByteBuffer)(JNIEnv*, void*, jlong);
/*获取一个Buffer对象的首地址*/
void* (*GetDirectBufferAddress)(JNIEnv*, jobject);
/*获取一个Buffer对象的Capacity容量*/
jlong (*GetDirectBufferCapacity)(JNIEnv*, jobject);
/* added in JNI 1.6 */
/*获取jobject对象的引用类型:
可能为:a local, global or weak global reference等引用类型:
如下:
JNIInvalidRefType = 0,
JNILocalRefType = 1,
JNIGlobalRefType = 2,
JNIWeakGlobalRefType = 3*/
jobjectRefType (*GetObjectRefType)(JNIEnv*, jobject);
};
Local引用
Global引用
/* This code is illegal */
static jclass cls = 0;
static jfieldID fld;
JNIEXPORT void JNICALL
Java_FieldAccess_accessFields(JNIEnv *env, jobject obj)
{
...
if (cls == 0) {
cls = (*env)->GetObjectClass(env, obj);
if (cls == 0) {
... /* error */
}
fid = (*env)->GetStaticFieldID(env, cls, "si", "I");
}
/* access the member variable using cls and fid */
...
}
/* This code is correct. */
static jclass cls = 0;
static jfieldID fld;
JNIEXPORT void JNICALL
Java_FieldAccess_accessFields(JNIEnv *env, jobject obj)
{
...
if (cls == 0) {
jclass cls1 = (*env)->GetObjectClass(env, obj);
if (cls1 == 0) {
... /* error */
}
cls = (*env)->NewGlobalRef(env, cls1);
if (cls == 0) {
... /* error */
}
fid = (*env)->GetStaticFieldID(env, cls, "si", "I");
}
/* access the member variable using cls and fid */
...
}
Weak引用
void updateName(String name) throws Exception {
this.name = name;
Log.d("HelloJni","你成功调用了HelloCallBack的方法:updateName");
throw new Exception("dead");
}
jboolean hasException = env->ExceptionCheck();
if(hasException == JNI_TRUE){
//打印异常,同Java中的printExceptionStack;
env->ExceptionDescribe();
//清除当前异常
env->ExceptionClear();
}
/*检测是否有异常*/
jboolean hasException = env->ExceptionCheck();
if(hasException == JNI_TRUE){
//打印异常,同Java中的printExceptionStack;
env->ExceptionDescribe();
//清除当前异常
env->ExceptionClear();
/*方式2:抛出异常给上面,让Java层去捕获*/
jclass noFieldClass = env->FindClass("java/lang/Exception");
std::string msg(_fieldName);
std::string header = "找不到该字段";
env->ThrowNew(noFieldClass,header.append(msg).c_str());
env->ReleaseStringUTFChars(fieldName,_fieldName);
return;
}
try{
jni.callJavaField("com/android/hellojni/HelloCallBack","namesss");
}catch (Exception e){
e.printStackTrace();
return;
}
void log(int i);
void log(char c);
void log(float f);
void log(char* c);
_log_int
_log_char
_log_float
_log_string
//cHeader.h
#ifndef C_HEADER
#define C_HEADER
extern void _log(int i);
#endif // !C_HEADER
//cHeader.c
#include "cHeader.h"
#include <stdio.h>
void _log(int i) {
printf("cHeader %d\n", i);
}
//main.cpp
extern "C" {
//void _log(int i);
#include "cHeader.h"
}
int main() {
_log(100);
}
#pragma once
extern "C" {
void _log_i(int i);
}
#include "cppHeader.h"
#include <stdio.h>
void _log_i(int i) {
printf("cppHeader:%d\n", i);
}
extern void _log_i(int i);
int main() {
_log_i(120);
}
-shared 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件 -fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。 -g:为调试
结果:cppHeader:120