1,C预处理器
C 预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器(C Preprocessor)简写为 CPP。
所有的预处理器命令都是以井号(#)开头。它必须是第一个非空字符,为了增强可读性,预处理器指令应从第一列开始。
#include 导入头文件
#if if判断操作 【if的范畴 必须endif】
#elif else if
#else else
#endif 结束if
#define 定义一个宏
#ifdef 如果定义了这个宏 【if的范畴 必须endif】
#ifndef 如果没有定义这个宏 【if的范畴 必须endif】
#undef 取消宏定义
#pragma 设定编译器的状态
#if 举例:
#if 1 // if
cout << "真" << endl;
#elif 0 // else if
cout << "假" << endl;
#else
cout << "都不满足" << endl;
#endif // 结束if
cout << "结束if" << endl;
#ifdef举例:
#ifndef isRelease // 如果没有isRelease这个宏
#define isRelease 1 // 是否是正式环境下 【我就定义isRelease这个宏】
#if isRelease == true
#define RELEASE // 正式环境下 定义RELEASE宏
#elif isRelease == false
#define DEBUG // 测试环境下 定义DEBUG宏
#endif
#endif
#ifdef DEBUG // 是否定义了DEBUG这个宏
cout << "在测试环境" << endl;
#else RELEASE
cout << "在正式环境" << endl;
#endif // 结束IF
宏的取消#undef:
#ifdef TEST// 是否定义了这个宏
cout << "TEST" << endl;
#undef TEST// 取消宏的定义,下面的代码,就没法用这个宏了,相当于:没有定义过TEST宏
#endif
宏变量:
#define VALUE_I 99
#define VALUE_S "字符串"
int main() {
int i = VALUE_I; // 预处理阶段 宏会直接完成文本替换工作,替换后的样子:int i = 99;
string s = VALUE_S; // 预处理阶段 宏会直接完成文本替换工作,替换后的样子:string s = "字符串";
return 0;
}
宏函数,宏函数都是大写
优点:文本替换,不会造成函数调用开销
缺点:会导致代码体积增大
#define ADD(n1, n2) n1 + n2
int main() {
int r = ADD(1, 2);
cout << r << endl;//返回3
return 0;
}
2,JNI java与native代码互调
在MainActivity中调用native方法:
public class MainActivity extends AppCompatActivity {
public String name = "测试"; // 签名:Ljava/lang/String;
static {
System.loadLibrary("native-lib"); //静态代码块中加载native-lib文件
}
public native void changeName();//调用native方法
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.txt_name);
changeName();
tv.setText(name);
}
}
使用命令javah -classpath . -jni com.test.MainActivity 生成头文件com_test_MainActivity.h
#include <jni.h>
#include <string>
// 解决循环Copy的问题 第二次就进不来了
#ifndef _Included_com_test_MainActivity // 如果没有定义这个宏
#define _Included_com_test_MainActivity // 我就定义这个宏
#ifdef __cplusplus // 如果是C++环境
extern "C" { // 全部采用C的方式 不准你函数重载,函数名一样的问题
#endif
// 函数的声明
JNIEXPORT jstring JNICALL Java_com_test_MainActivity_changeName
(JNIEnv *, jobject);
#ifdef __cplusplus // 省略 如果是C++,啥事不干
}
#endif
在native-lib.cpp里面实现changeName()方法
#include "com_test_MainActivity.h"
// NDK工具链里面的 log 库 引入过来
#include <android/log.h>
#define TAG "MainAcitvity"
//自动填充
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
// extern "C": 必须采用C的编译方式
// // 无论是C还是C++ 最终是调用到 C的JNINativeInterface,所以必须采用C的方式 extern "C"
// 函数的实现
extern "C"
JNIEXPORT // 标记该方法可以被外部调用
jstring // Java <---> native 转换用的
JNICALL // 代表是 JNI标记
// Java_包名_类名_方法名
// JNIEnv * env JNI:的桥梁环境 所有的JNI操作,必须靠他
// jobject jobj 谁调用,就是谁的实例 MainActivity this
// jclass clazz 谁调用,就是谁的class MainActivity.class
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_changeName(JNIEnv *env, jobject thiz) {
// 获取class
jclass j_cls = env->GetObjectClass(thiz);
// 获取属性 L对象类型 都需要L
// jfieldID GetFieldID(MainActivity.class, 属性名, 属性的签名)
jfieldID j_fid = env->GetFieldID(j_cls, "name", "Ljava/lang/String;");
// 转换工作 jstring 为JNI类型
jstring j_str = static_cast<jstring>(env->GetObjectField(thiz ,j_fid));
// 打印字符串 目标
char * c_str = const_cast<char *>(env->GetStringUTFChars(j_str, NULL));
LOGD("native : %s\n", c_str);
LOGE("native : %s\n", c_str);
LOGI("native : %s\n", c_str);
// 修改名字 返回给java层
jstring jName = env->NewStringUTF("张三");
env->SetObjectField(thiz, j_fid, jName);
}
签名规则:
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
Java调用native方法,native方法调用java方法,互调
在MainActivity中:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
public native void callSum();
public int sum(int number1, int number2) {
return number1 + number2;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
callSum();
}
}
在native-lib.cpp中,实现callSum()代码:
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_callSum(JNIEnv *env, jobject job) {
jclass mainActivityClass = env->GetObjectClass(job);
// GetMethodID(MainActivity.class, 方法名, 方法的签名)
jmethodID j_mid = env->GetMethodID(mainActivityClass, "sum", "(II)I");
// 调用 Java的方法
jint sum = env->CallIntMethod(job, j_mid, 8, 8);
LOGE("sum result:%d", sum);
}
3,JNI 数组操作
MainActivity:
public class MainActivity extends AppCompatActivity {
private final static String TAG = MainActivity.class.getSimpleName();
static {
// System.load(D:/xxx/xxxx/xxx/native-lib); 这种是可以绝对路径的加载动态链接库文件
System.loadLibrary("native-lib");
// 这种是从库目录遍历层级目录,去自动的寻找 apk里面的lib/libnative-lib.so
}
public native void testArrayAction(int count, String textInfo, int[] ints, String[] strs);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
arrTest();
}
public void arrTest() {
int[] ints = new int[]{1,2,3,4,5,6}; // 基本类型的数组
String[] strs = new String[]{"张三","周五","王五"}; // 对象类型的数组
testArrayAction(99, "你好", ints, strs);
}
}
在native-lib.cpp中:
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_testArrayAction(JNIEnv *env, jobject thiz,
jint count,
jstring text_info,
jintArray ints,
jobjectArray strs) {
// ① 基本数据类型 jint count, jstring text_info, 最简单的
int countInt = count; // jint本质是int,所以可以用int接收
LOGI("参数一 countInt:%d\n", countInt);
// const char* GetStringUTFChars(jstring string, jboolean* isCopy)
const char *textInfo = env->GetStringUTFChars(text_info, NULL);
LOGI("参数二 textInfo:%s\n", textInfo);
// ② 把int[] 转成 int*
// jint* GetIntArrayElements(jintArray array, jboolean* isCopy)
int *jintArray = env->GetIntArrayElements(ints, NULL);
// Java层数组的长度
// jsize GetArrayLength(jarray array) // jintArray ints 可以放入到 jarray的参数中去
jsize size = env->GetArrayLength(ints);
for (int i = 0; i < size; ++i) {
*(jintArray + i) += 100; // C++的修改,影响不了Java层
LOGI("参数三 int[]:%d\n", *jintArray + i);
}
// 目前无法控制Java的数组 变化 +100
// 操作杆 ----> JMV
// env->
/**
* 0: 刷新Java数组,并 释放C++层数组
* JNI_COMMIT: 只提交 只刷新Java数组,不释放C++层数组
* JNI_ABORT: 只释放C++层数组
*/
env->ReleaseIntArrayElements(ints, jintArray, 0);
// ③:jobjectArray 代表是Java的引用类型数组,不一样
jsize strssize = env->GetArrayLength(strs);
for (int i = 0; i < strssize; ++i) {
jstring jobj = static_cast<jstring>(env->GetObjectArrayElement(strs, i));
// 模糊:isCopy内部启动的机制
// const char* GetStringUTFChars(jstring string, jboolean* isCopy)
const char *jobjCharp = env->GetStringUTFChars(jobj, NULL);
LOGI("参数四 引用类型String 具体的:%s\n", jobjCharp);
// 释放jstring
env->ReleaseStringUTFChars(jobj, jobjCharp);
}
}
4,JNI对象操作
创建Student类
public class Student {
private final static String TAG = Student.class.getSimpleName();
public String name;
public int age;
public String getName() {
return name;
}
public void setName(String name) {
Log.d(TAG, "Java setName name:" + name);
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
Log.d(TAG, "Java setAge age:" + age);
this.age = age;
}
public static void showInfo(String info) {
Log.d(TAG, "showInfo info:" + info);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
MainActivity:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
public native void putObject(Student student, String str); // 传递引用类型,传递对象
public native void insertObject(); // 凭空创建Java对象
public native void testQuote(); // 测试引用
public native void delQuote(); // 释放全局引用
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void test02(View view) {
Student student = new Student(); // Java new
student.name = "史泰龙";
student.age = 88;
putObject(student, "九阳神功");
}
public void test03(View view) {
insertObject();
}
public void test04(View view) {
testQuote();
}
public void test05(View view) {
delQuote(); // 必须释放全局引用
}
@Override
protected void onDestroy() {
super.onDestroy();
delQuote(); // Activity销毁时: 必须释放全局引用
}
}
在native-lib.cpp中:
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_putObject(JNIEnv *env,
jobject thiz,
jobject student,
jstring str) {
const char *strChar = env->GetStringUTFChars(str, NULL);
LOGI("strChar:%s\n", strChar);
env->ReleaseStringUTFChars(str, strChar);
// --------------
// 1.寻找类 Student
// jclass studentClass = env->FindClass("com/test/Student"); // 第一种
jclass studentClass = env->GetObjectClass(student); // 第二种
// 2.Student类里面的函数规则 签名
jmethodID setName = env->GetMethodID(studentClass, "setName", "(Ljava/lang/String;)V");
jmethodID getName = env->GetMethodID(studentClass, "getName", "()Ljava/lang/String;");
jmethodID showInfo = env->GetStaticMethodID(studentClass, "showInfo", "(Ljava/lang/String;)V");
// 3.调用 setName
jstring value = env->NewStringUTF("Zhangsan");
env->CallVoidMethod(student, setName, value);
// 4.调用 getName
jstring getNameResult = static_cast<jstring>(env->CallObjectMethod(student, getName));
const char *getNameValue = env->GetStringUTFChars(getNameResult, NULL);
LOGE("调用到getName方法,值是:%s\n", getNameValue);
// 5.调用静态showInfo
jstring jstringValue = env->NewStringUTF("静态方法你好,我是C++");
env->CallStaticVoidMethod(studentClass, showInfo, jstringValue);
}
// C++ 堆 栈 ...
// JNI函数 局部引用,全局引用,...
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_insertObject(JNIEnv *env, jobject thiz) {
/*jstring str = env->GetStringUTFChars();
jstring str = env->GetStringUTFChars();
jstring str = env->GetStringUTFChars();
jstring str = env->GetStringUTFChars();
jstring str = env->GetStringUTFChars();
jstring str = env->GetStringUTFChars();
jstring str = env->GetStringUTFChars();
// 好习惯:
// 我用完了,我记释放,在我函数执行过程中,不会导致 内存占用多
env->ReleaseStringUTFChars()*/
// 1.通过包名+类名的方式 拿到 Student class 凭空拿class
const char *studentstr = "com/test/Student";
jclass studentClass = env->FindClass(studentstr);
// 2.通过student的class 实例化此Student对象 C++ new Student
jobject studentObj = env->AllocObject(studentClass); // AllocObject 只实例化对象,不会调用对象的构造函数
// 方法签名的规则
jmethodID setName = env->GetMethodID(studentClass, "setName", "(Ljava/lang/String;)V");
jmethodID setAge = env->GetMethodID(studentClass, "setAge", "(I)V");
// 调用方法
jstring strValue = env->NewStringUTF("zhansan");
env->CallVoidMethod(studentObj, setName, strValue);
env->CallVoidMethod(studentObj, setAge, 99);
// env->NewObject() // NewObject 实例化对象,会调用对象的构造函数
// ==================== 下面是 Person对象 调用person对象的 setStudent 函数等
// 4.通过包名+类名的方式 拿到 Student class 凭空拿class
const char *personstr = "com/test/Person";
jclass personClass = env->FindClass(personstr);
jobject personObj = env->AllocObject(personClass); // AllocObject 只实例化对象,不会调用对象的构造函数
// setStudent 此函数的 签名 规则
jmethodID setStudent = env->GetMethodID(personClass, "setStudent",
"(Lcom/test/Student;)V");
env->CallVoidMethod(personObj, setStudent, studentObj);
// 规范:一定记得释放【好习惯】
// 第一类
env->DeleteLocalRef(studentClass);
env->DeleteLocalRef(personClass);
env->DeleteLocalRef(studentObj);
env->DeleteLocalRef(personObj);
// 第二类
// env->ReleaseStringUTFChars()
// TODO 局部引用: jobject jclass jstring ... 【函数结束后,会自动释放】
}
jclass dogClass; // 你以为这个是全局引用,实际上他还是局部引用
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_testQuote(JNIEnv *env, jobject thiz) {
if (NULL == dogClass) {
/*const char * dogStr = "com/derry/as_jni_project/Dog";
dogClass = env->FindClass(dogStr);*/
// 升级全局引用: JNI函数结束也不释放,反正就是不释放,必须手动释放 ----- 相当于: C++ 对象 new、手动delete
const char * dogStr = "com/test/Dog";
jclass temp = env->FindClass(dogStr);
dogClass = static_cast<jclass>(env->NewGlobalRef(temp)); // 提升全局引用
// 记住:用完了,如果不用了,马上释放
env->DeleteLocalRef(temp);
}
// <init> V 是不会变的
// 构造函数一
jmethodID init = env->GetMethodID(dogClass, "<init>", "()V");
jobject dog = env->NewObject(dogClass, init);
// 构造函数2
init = env->GetMethodID(dogClass, "<init>", "(I)V");
dog = env->NewObject(dogClass, init, 100);
// 构造函数3
init = env->GetMethodID(dogClass, "<init>", "(II)V");
dog = env->NewObject(dogClass, init, 200, 300);
// 构造函数4
init = env->GetMethodID(dogClass, "<init>", "(III)V");
dog = env->NewObject(dogClass, init, 400, 500, 600);
env->DeleteLocalRef(dog); // 释放
// dogClass = NULL; // 是不是问题解决了,不能这样干(JNI函数结束后,还怎么给你释放呢)
// 这样就解决了
/*env->DeleteGlobalRef(studentClass);
studentClass = NULL;*/
}
// JNI函数结束,会释放局部引用 dogClass虽然被释放,但是还不等于NULL,只是一个悬空指针而已,所以第二次进不来IF,会崩溃
// 非常方便,可以使用了
extern int age; // 声明age
extern void show(); // 声明show函数 5000行代码
// 手动释放全局引用
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_delQuote(JNIEnv *env, jobject thiz) {
if (dogClass != NULL) {
LOGE("全局引用释放完毕,上面的按钮已经失去全局引用,再次点击会报错");
env->DeleteGlobalRef(dogClass);
dogClass = NULL; // 最好给一个NULL,指向NULL的地址,不要去成为悬空指针,为了好判断悬空指针的出现
}
// 测试下
show();
}
5,静态注册动态注册
静态注册:
前面例子所用的都是静态注册
静态注册的优点:开发简单
静态注册的缺点:
1,JNI函数名非常长
2,捆绑上层 需要java层的包名和类名
3,在运行期才会去匹配JNI函数,性能上低于动态注册
动态注册:
MainActivity:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib"); // 默认调用:JNI_OnLoad
}
public native void dynamicJavaMethod(); // 动态注册
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dynamicJavaMethod();
}
}
native-lib.cpp:
JavaVM *jVm = nullptr; //系统乱值,C++11后,取代NULL,作用是可以初始化指针赋值
const char *mainActivityClassName = "com/test/MainActivity";
// native 真正的函数
// void dynamicMethod(JNIEnv *env, jobject thiz) { // OK的
void dynamicMethod() { // 也OK 如果你用不到 JNIEnv jobject ,可以不用写
LOGD("我是动态注册的函数 dynamicMethod...");
}
static const JNINativeMethod jniNativeMethod[] = {
{"dynamicJavaMethod", "()V", (void *) (dynamicMethod01)},
};
// JNI JNI_OnLoad函数,如果你不写JNI_OnLoad,默认就有JNI_OnLoad,如果你写JNI_OnLoad函数 覆写默认的JNI_OnLoad函数
extern "C"
JNIEXPORT jint JNI_OnLoad(JavaVM *javaVm, void *) {
// this.javaVm = javaVm;
::jVm = javaVm;
// 做动态注册 全部做完
JNIEnv *jniEnv = nullptr;
int result = javaVm->GetEnv(reinterpret_cast<void **>(&jniEnv), JNI_VERSION_1_6);
// result 等于0 成功
if (result != JNI_OK) {
return -1; // 会崩溃
}
jclass mainActivityClass = jniEnv->FindClass(mainActivityClassName);
// jint RegisterNatives(Class, 我们的数组==jniNativeMethod, 注册的数量 = 1)
jniEnv->RegisterNatives(mainActivityClass,
jniNativeMethod,
sizeof(jniNativeMethod) / sizeof(JNINativeMethod));
LOGE("动态注册成功");
return JNI_VERSION_1_6; // // AS的JDK在JNI默认最高1.6
}
6,JNI线程
MainActivity:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib"); // 默认调用:JNI_OnLoad
}
public native void naitveThread(); // Java层 调用 Native层 的函数,完成JNI线程
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
naitveThread();
}
public void updateActivityUI() {
if (Looper.getMainLooper() == Looper.myLooper()) { // TODO C++ 用主线程调用到此函数 ----> 主线程
new AlertDialog.Builder(MainActivity.this)
.setTitle("UI")
.setMessage("updateActivityUI Activity UI ...")
.setPositiveButton("ok", null)
.show();
} else { // TODO C++ 用异步线程调用到此函数 ----> 异步线程
Log.d(TAG, "updateActivityUI 所属于子线程,只能打印日志了..");
runOnUiThread(new Runnable() { // 哪怕是异步线程 UI操作 正常下去 runOnUiThread
@Override
public void run() {
// 可以在子线程里面 操作UI
new AlertDialog.Builder(MainActivity.this)
.setTitle("updateActivityUI")
.setMessage("所属于子线程,只能打印日志了..")
.setPositiveButton("ok", null)
.show();
}
});
}
}
}
native-lib.cpp:
class TestContext {
public:
JNIEnv *jniEnv = nullptr; // 不能跨线程 ,会崩溃
jobject instance = nullptr; // 不能跨线程 ,会崩溃
};
void *myThreadTaskAction(void *pVoid) { // 当前是异步线程
LOGE("myThreadTaskAction run");
// 需求:有这样的场景,例如:下载完成 ,下载失败,等等,必须告诉Activity UI端,所以需要在子线程调用UI端
// 这两个是必须要的
// JNIEnv *env
// jobject thiz
TestContext * testContext = static_cast<TestContext *>(pVoid);
// jclass mainActivityClass = testContext ->jniEnv->FindClass(mainActivityClassName); // 不能跨线程 ,会崩溃
// mainActivityClass = myContext->jniEnv->GetObjectClass(myContext->instance); // 不能跨线程 ,会崩溃
// 解决方式 (安卓进程只有一个 JavaVM,是全局的,是可以跨越线程的)
JNIEnv * jniEnv = nullptr; // 全新的JNIEnv 异步线程里面操作
jint attachResult = ::jVm->AttachCurrentThread(&jniEnv, nullptr); // 附加当前异步线程后,会得到一个全新的 env,此env相当于是子线程专用env
if (attachResult != JNI_OK) {
return 0; // 附加失败,返回了
}
// 1.拿到class
jclass mainActivityClass = jniEnv->GetObjectClass(testContext->instance);
// 2.拿到方法
jmethodID updateActivityUI = jniEnv->GetMethodID(mainActivityClass, "updateActivityUI", "()V");
// 3.调用
jniEnv->CallVoidMethod(testContext->instance, updateActivityUI);
::jVm->DetachCurrentThread(); // 必须解除附加,否则报错
LOGE("C++ 异步线程OK")
return nullptr;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_naitveThread(JNIEnv *env, jobject job) { // 当前是主线程
TestContext * testContext = new TestContext;
testContext->jniEnv = env;
// testContext->instance = job; // 默认是局部引用,会崩溃
testContext->instance = env->NewGlobalRef(job); // 提升全局引用
pthread_t pid;
pthread_create(&pid, nullptr, myThreadTaskAction, testContext);
pthread_join(pid, nullptr);
}
小结:
1,JavaVM是全局引用,绑定当前进程,只有一个地址
2,JNIEnv线程绑定,绑定主线程,绑定子线程
3,jobject 谁调用JNI函数,谁的实例会给jobject
4,JNIEnv *env 不能跨线程,否在崩溃,可以跨函数,可以使用全局的JavaVM附加当前异步线程,得到权限env操作
5,jobject thiz不能跨线程,不能跨函数,否在崩溃,默认是局部引用,提升全局引用,可以解决此问题
6,JavaVM 能够跨线程,跨函数
7,静态缓存
1,先看下非静态缓存:
MainActivity:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
// 假设这里定义了一堆变量
static String name1 ="T1";
static String name2 ="T2";
static String name3 ="T3";
static String name4 ="T4";
static String name5 ="T5";
static String name6 ="T6";
public static native void localCache(String name); // 普通的局部缓存
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
localCache("修改后");
}
}
native-lib.cpp:
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_localCache(JNIEnv *env, jclass clazz, jstring name) {
// 非静态缓存
jfieldID f_id = nullptr;
if (f_id == nullptr) {
f_id = env->GetStaticFieldID(clazz, "name1", "Ljava/lang/String;"); // 有点耗费性能
} else {
LOGE("空的");
}
env->SetStaticObjectField(clazz, f_id, name); // 修改后
f_id = nullptr;
2,静态缓存:
MainActivity:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
static String name1 ="T1";
static String name2 ="T2";
static String name3 ="T3";
static String name4 ="T4";
static String name5 ="T5";
static String name6 ="T6";
public static native void initStaticCache(); // 初始化静态缓存
public static native void staticCache(String name);
public static native void clearStaticCache(); // 清除化静态缓存
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
// 事件
public void staticCacheAction(View view) {
// 初始化静态缓存
initStaticCache(); // 如果是在类里面, Person Student ,必须在类的构造函数初始化
staticCache("修改后");
staticCache("修改后");
staticCache("修改后");
staticCache("修改后");
staticCache("修改后");
staticCache("修改后");
staticCache("修改后");
}
@Override
protected void onDestroy() {
super.onDestroy();
clearStaticCache(); // 清除化静态缓存
}
}
native-lib.cpp:文章来源:https://www.toymoban.com/news/detail-655354.html
static jfieldID f_name1_id = nullptr;
static jfieldID f_name2_id = nullptr;
static jfieldID f_name3_id = nullptr;
static jfieldID f_name4_id = nullptr;
static jfieldID f_name5_id = nullptr;
static jfieldID f_name6_id = nullptr;
// 先缓存
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_initStaticCache(JNIEnv *env, jclass clazz) {
f_name1_id = env->GetStaticFieldID(clazz, "name1", "Ljava/lang/String;");
f_name2_id = env->GetStaticFieldID(clazz, "name2", "Ljava/lang/String;");
f_name3_id = env->GetStaticFieldID(clazz, "name3", "Ljava/lang/String;");
f_name4_id = env->GetStaticFieldID(clazz, "name4", "Ljava/lang/String;");
f_name5_id = env->GetStaticFieldID(clazz, "name5", "Ljava/lang/String;");
f_name6_id = env->GetStaticFieldID(clazz, "name6", "Ljava/lang/String;");
}
// 使用
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_staticCache(JNIEnv *env, jclass clazz, jstring name) {
// 不会反复 GetStaticFieldID 提供性能
env->SetStaticObjectField(clazz, f_name1_id, name);
env->SetStaticObjectField(clazz, f_name2_id, name);
env->SetStaticObjectField(clazz, f_name3_id, name);
env->SetStaticObjectField(clazz, f_name4_id, name);
env->SetStaticObjectField(clazz, f_name5_id, name);
env->SetStaticObjectField(clazz, f_name6_id, name);
}
// 清除
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_clearStaticCache(JNIEnv *env, jclass clazz) {
f_name1_id = nullptr;
f_name2_id = nullptr;
f_name3_id = nullptr;
f_name4_id = nullptr;
f_name5_id = nullptr;
f_name6_id = nullptr;
}
8,异常处理
public class MainActivity3 extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
static String name1 = "T1";
// 下面是异常处理
public static native void exception();
public static native void exception2() throws NoSuchFieldException; // NoSuchFieldException接收C++层抛上来的异常
public static native void exception3();
public static native String derryAction();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void exceptionAction(View view) {
exception(); // C++层自己做了补救措施了
// 捕获人家C++层抛上来的异常
try {
exception2();
} catch (NoSuchFieldException exception) {
exception.printStackTrace();
Log.d("test", "exceptionAction: 异常被我捕获了");
}
exception3();
String result = derryAction();
}
// 专门给 C++(native层) 层调用的 函数
public static void show() throws Exception {
Log.d("test", "show: 1111");
Log.d("test", "show: 1111");
Log.d("test", "show: 1111");
Log.d("test", "show: 1111");
Log.d("test", "show: 1111");
Log.d("test", "show: 1111");
throw new NullPointerException("我是Java中抛出的异常,我的show方法里面发送了Java逻辑错误");
}
}
native-lib.cpp:文章来源地址https://www.toymoban.com/news/detail-655354.html
// 异常1 【native层主动干的异常】
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_exception(JNIEnv *env, jclass clazz) {
// 假设现在想操作 name2 ,没有name2就会在native层崩溃
jfieldID f_id = env->GetStaticFieldID(clazz, "name2", "Ljava/lang/String;");
//崩溃后,有两种解决方案
// 方式1 补救措施
jthrowable thr = env->ExceptionOccurred(); // 监测本次执行,到底有没有异常 JNI函数里面代码有问题
if(thr) { // 非0 进去,监测到有异常
LOGD("C++层有异常 监测到了");
env->ExceptionClear(); // 此异常被清除
// 开始 补救措施
jfieldID f_id = env->GetStaticFieldID(clazz, "name1", "Ljava/lang/String;");
}
}
// 异常2 【native层主动干的异常】
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_exception2(JNIEnv *env, jclass clazz) {
// 假设现在想操作 name2 ,没有name2就会在native层崩溃掉
jfieldID f_id = env->GetStaticFieldID(clazz, "name2", "Ljava/lang/String;");
//崩溃后,有两种解决方案
// 方式2 往Java层抛
jthrowable jthrowable = env->ExceptionOccurred(); // 监测本次执行,到底有没有异常 JNI函数里面代码有问题
if(jthrowable) { // 非0 进去,监测到有异常
LOGD("C++层有异常 监测到了");
env->ExceptionClear(); // 此异常被清除
// Throw抛一个 Java的对象 java/lang/String java/xxxxx/xxx/NullExxx
jclass clz = env->FindClass("java/lang/NoSuchFieldException");
env->ThrowNew(clz, "NoSuchFieldException 找不到 name2");
}
}
// 异常3 【native层被动干的异常 被动 我是Java方法坑了】
extern "C"
JNIEXPORT void JNICALL
Java_com_test_MainActivity_exception3(JNIEnv *env, jclass clazz) {
jmethodID showID = env->GetStaticMethodID(clazz, "show", "()V");
env->CallStaticVoidMethod(clazz, showID); // 是不是这句话崩溃的 1是 2不是 答:不是,只是他引起的而已
// ExceptionCheck 《==》 慢慢的奔溃的,相当于给了你空余时间,既然不是马上崩溃,我就可以检测
// JNI函数里面代码有问题 没有问题,给你空余时间,慢慢的崩溃
if (env->ExceptionCheck()) {
env->ExceptionDescribe(); // 输出描述 信息
env->ExceptionClear(); // 此异常被清除 业务逻辑控制
}
// 注意实现:
/*// 崩溃后,下面的语句,照样打印
LOGI("C++层>>>>>>>>>>>>>>>>>>>>>>>>>>>>1");
LOGI("C++层>>>>>>>>>>>>>>>>>>>>>>>>>>>>2");
LOGI("C++层>>>>>>>>>>>>>>>>>>>>>>>>>>>>3");
LOGI("C++层>>>>>>>>>>>>>>>>>>>>>>>>>>>>4");
LOGI("C++层>>>>>>>>>>>>>>>>>>>>>>>>>>>>5");
env->NewStringUTF("AAAA"); // 局部引用 崩溃给磨平*/
}
到了这里,关于Android JNI3--JNI基础的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!