目录介绍

  • 01.学习JNI开发流程

    • 1.1 JNI开发概念
    • 1.2 JNI和NDK的关系
    • 1.3 JNI实际步骤
    • 1.4 NDK应用场景
    • 1.5 学习路线阐明
  • 02.NDK架构分层

    • 2.1 NDK分层构建层
    • 2.2 NDK分层Java层
    • 2.3 Native层
  • 03.JNI根底语法

    • 3.1 JNI三种援用
    • 3.2 JNI异样解决
    • 3.3 C和C++相互调用
    • 3.4 JNI外围原理
    • 3.5 注册Native函数
    • 3.6 JNI签名是什么
  • 04.一些必备操作

    • 4.1 so库生成打包
    • 4.2 so库查问操作
    • 4.3 so库如何反编译
  • 05.实际几个案例

    • 5.1 Java动态调用C/C++
    • 5.2 C/C++调用Java
    • 5.3 Java调三方so中API
    • 5.4 Java动静调C++
  • 06.一些技术原理

    • 6.1 JNIEnv创立和开释
    • 6.2 动静注册的原理
    • 6.3 注册JNI流程图
  • 07.JNI遇到的问题

    • 7.1 混同的bug
    • 7.2 留神字符串编译

01.学习JNI开发流程

1.1 JNI开发概念

  • .SO库是什么货色

    • NDK为了方便使用,提供了一些脚本,使得更容易的编译C/C++代码。在Android程序编译中会将C/C++ 编译成动静库 so 文件,相似java库.jar文件一样,它的生成须要应用NDK工具来打包。
    • so是shared object的缩写,见名思义就是共享的对象,机器能够间接运行的二进制代码。本质so文件就是一堆C、C++的头文件和实现文件打包成一个库。
  • JNI是什么货色

    • JNI的全称是Java Native Interface,即本地Java接口。因为 Java 具备跨平台的特点,所以Java 与 本地代码交互的能力十分弱。
    • 采纳JNI个性能够加强 Java 与本地代码交互的能力,使Java和其余类型的语言如C++/C可能相互调用。

1.2 JNI和NDK的关系

  • JNI和NDK学习内容太难

    • 其实难的不是JNI和NDK,而是C/C++语言,JNI和NDK只是个工具,很容易学习的。
  • JNI和NDK有何分割

    • 学习JNI之前,首先得先晓得JNI、NDK、Java和C/C++之间的关系。
    • 在Android开发中,有时为了性能和安全性(反编译),须要应用C/C++语言,然而Android APP层用的是Java语言,怎么能力让这两种语言进行交换呢,因为他们的编码方式是不一样的,这是就须要JNI了。
    • JNI能够被看作是代理模式,JNI是java接口,用于Java与C/C++之间的交互,作为两者的桥梁,也就是Java让JNI代其与C/C++沟通。
    • NDK是Android工具开发包,帮忙疾速开发C/C++动静库,相当于JDK开发java程序一样,同时能帮打包生成.so库

1.3 JNI实际步骤

  • 操作实际步骤

    • 第一步,编写native办法。
    • 第二步,依据此native办法编写C文件。
    • 第三步,应用NDK打包成.so库。
    • 第四步,应用.so库而后调用api。
  • 如何应用NDK打包.so库

    • 1,编写Android.mk文件,此文件用来告知NDK打包.so库的规定
    • 2,应用ndk-build打包.so库
  • 相干学习文档

    • NDK学习:https://developer.android.google.cn/ndk/guides?hl=zh-cn

1.4 NDK应用场景

  • NDK的应用场景个别在:

    • 1.为了晋升这些模块的性能,对图形,视频,音频等计算密集型利用,将简单模块计算封装在.so或者.a文件中解决。
    • 2.应用的是C/C++进行编写的第三方库移植。如ffmppeg,OpenGl等。
    • 3.某些状况下为了进步数据安全性,也会封装so来实现。毕竟应用纯Java开发的app是有很多逆向工具能够破解的。

1.5 学习路线阐明

  • JNI学习路线介绍

    • 1.首先要有点C/C++的根底,这个我是在 菜鸟教程 上学习的
    • 2.了解NDK和JNI的一些概念,以及NDK的一个大略的架构分层,JNI的开发步骤是怎么的
    • 3.把握案例练习,后期先写案例,比方java调用c/c++,或者c/c++调用java。把这个案例写熟,跑通即可
    • 4.案例练习之后,而后在思考NDK是怎么编译的,如何打包so文件,loadLibrary的流程,CMake工作流程等一些根底的原理
    • 5.在实际过程中,先记录遇到的问题。这时候可能不肯定懂,先放着,先实现案例或者简略的业务。而后边实际边推敲问题和背地的原理
  • 注意事项介绍

    • 防止一开始就钻研原理,或者把C/C++整体学习一遍,那样会比拟辛苦。焦点先放在JNI通信流程上,写案例学习
    • 把学习内容,分为几个不同类型:理解(可能扯淡),了解(大略晓得什么意思),把握(可能使用和实际),精通(能触类旁通和分享讲清楚)

02.NDK架构分层

  • 应用NDK开发最终目标是为了将C/C++代码编译生成.so动静库或者动态库文件,并将库文件提供给Java代码调用。
  • 所以按架构来分能够分为以下三层:

    • 1.构建层
    • 2.Java层
    • 3.native层

2.1 NDK分层构建层

  • 要失去指标的so文件,须要有个构建环境以及过程,将这个过程和环境称为构建层。

    • 构建层须要将C/C++代码编译为动静库so,那么这个编译的过程就须要一个构建工具,构建工具依照开发者指定的规定形式来构建库文件,相似apk的Gradle构建过程。
  • 在解说NDK构建工具之前,咱们先来理解一些对于CPU架构的知识点:Android abi

    • ABI即Application Binary Interface,定义了二进制接口交互规定,以适应不同的CPU,一个ABI对应一种类型的CPU。
  • Android目前反对以下7种ABI:

    • 1.armeabi:第5代和6代的ARM处理器,晚期手机用的比拟多。
    • 2.armeabi-v7a:第7代及以上的 ARM 处理器。
    • 3.arm64-v8a:第8代,64位ARM处理器
    • 4.x86:个别用在平板,模拟器。
    • 5.x86_64:64位平板。
  • 惯例的NDK构建工具有两种:

    • 1.ndk-build:
    • 2.Cmake
  • ndk-build其实就是一个脚本。晚期的NDK开发始终都是应用这种模式

    • 运行ndk-build相当于运行一下命令:$GNUMAKE -f <ndk>/build/core/build-local.mk
    • $GNUMAKE 指向 GNU Make 3.81 或更高版本,<ndk> 则指向 NDK 装置目录
    • 应用ndk-build须要配合两个mk文件:Android.mk和Application.mk。
  • Cmake是一个编译系统的生成器

    • 简略了解就是,他是用来生成makefile文件的,Android.mk其实就是一个makefile类文件,cmake应用一个CmakeLists.txt的配置文件来生成对应的makefile文件。
    • Cmake构建so的过程其实包含两步:步骤1:应用Cmake生成编译的makefiles文件;步骤2:应用Make工具对步骤1中的makefiles文件进行编译为库或者可执行文件。
    • Cmake劣势在哪里呢?在生成makefile过程中会主动剖析源代码,创立一个组件之间依赖的关系树,这样就能够大大缩减在make编译阶段的工夫。
  • Cmake构建我的项目配置

    • 应用Cmake进行构建须要在build.gradle配置文件中申明externalNativeBuild

2.2 NDK分层Java层

  • 如何抉择正确的so库呢

    • 通常状况下,咱们在编译so的时候就须要确定本人设施类型,依据设施类型抉择对应abiFilters。
    • 留神:应用as编译后的so会主动打包到apk中,如果须要提供给第三方应用,能够到build/intermediates/cmake/debug or release 目录中copy进去。
  • Java层如何调用so文件中的函数

    • 对于Android下层代码来说,在将包正确导入到我的项目中后,只须要一行代码就能够实现动静库的加载过程。有两种形式:

      System.load("/data/local/tmp/native_lib.so"); System.loadLibrary("native_lib");
    • 1.加载门路不同:load是加载so的残缺门路,而loadLibrary是加载so的名称,而后加上前缀lib和后缀.so去默认目录下查找。
    • 2.主动加载库的依赖库的不同:load不会主动加载依赖库;而loadLibrary会主动加载依赖库。
  • 无论哪种形式,最终都会调用到LoadNativeLibrary()办法,该办法次要操作:

    • 1.通过dlopen关上动静库文件
    • 2.通过dlsym找到JNI_OnLoad符号所对应的办法地址
    • 3.通过JNI_OnLoad去注册对应的jni办法

2.3 Native层

  • 如何了解JNI的设计思维

    • JNI(全名Java Native Interface)Java native接口,其能够让一个运行在Java虚拟机中的Java代码被调用或者调用native层的用C/C++编写的基于本机硬件和操作系统的程序。简略了解为就是一个连贯Java层和Native层的桥梁。
    • 开发者能够在native层通过JNI调用到Java层的代码,也能够在Java层申明native办法的调用入口。
  • JNI注册形式

    • 当Java代码中执行Native的代码的时候,首先是通过肯定的办法来找到这些native办法。JNI有动态注册和动静注册两种注册形式。
    • 动态注册先由Java失去本地办法的申明,而后再通过JNI实现该申明办法。动静注册先通过JNI重载JNI_OnLoad()实现本地办法,而后间接在Java中调用本地办法。

03.JNI根底语法

3.1 JNI三种援用

  • 在JNI标准中定义了三种援用:

    • 部分援用(Local Reference)、全局援用(Global Reference)、弱全局援用(Weak Global Reference)。
  • Local援用

    • JNI中应用 jobject, jclass, and jstring等来标记一个Java对象,然而在JNI办法在应用的过程中会创立很多援用类型,如果应用过程中不留神就会导致内存泄露。
    • 间接应用:NewLocalRef来创立。Local援用其实就是Java中的部分援用,在申明这个局部变量的办法完结或者退出其作用域后就会被GC回收。
  • Global援用全局援用

    • 全局援用能够跨办法、跨线程应用,直到被开发者显式开释。一个全局援用在被开释前保障援用对象不被GC回收。
    • 和部分利用不同的是,能创立全局援用的函数只有NewGlobalRef,而开释它须要应用ReleaseGlobalRef函数。
  • Weak援用

    • 弱援用能够应用全局申明的形式。弱援用在内存不足或者缓和的时候会主动回收掉,可能会呈现短暂的内存泄露,然而不会呈现内存溢出的状况。

3.2 JNI异样解决

  • native层异样

    • 解决形式1:native层自行处理
    • 解决形式2:native层抛出给Java层解决

3.4 JNI外围原理

  • java运行在jvm,jvm自身就是应用C/C++编写的,因而jni只须要在java代码、jvm、C/C++代码之间做切换即可

  • JNIEnv是什么?

    • JINEnv是以后Java线程的执行环境,一个JVM对应一个JavaVM构造体,一个JVM中可能创立多个Java线程,每个线程对应一个JNIEnv构造,它们保留在线程本地存储TLS中。
    • 因而不同的线程JNIEnv不同,而不能互相共享应用。 JavaEnv构造也是一个函数表,在本地代码通过JNIEnv函数表来操作Java数据或者调用Java办法。

3.5 注册Native函数

  • JNI动态注册:

    • 步骤1.在Java中申明native办法,比方:public native String stringFromJNI()
    • 步骤2.在native层新建一个C/C++文件,并创立对应的办法(倡议应用AS快捷键主动生成函数名),比方:testjnilib.cpp: Line 8
  • JNI动静注册

    • 通过RegisterNatives办法把C/C++中的办法映射到Java中的native办法,而无需遵循特定的办法命名格局,这样书写起来会省事很多。
    • 动静注册其实就是应用到了后面剖析的so加载原理:在最初一步的JNI_OnLoad中注册对应的jni办法。这样在类加载的过程中就能够主动注册native函数。比方:
    • 与JNI_OnLoad()函数绝对应的有JNI_OnUnload()函数,当虚拟机开释该C库的时候,则会调用JNI_OnUnload()函数来进行善后革除工作。
  • 那么如何抉择应用动态注册or动静注册

    • 动静注册和动态注册最终都能够将native办法注册到虚拟机中,举荐应用动静注册,更不容易写错,动态注册每次减少一个新的办法都须要查看原函数类的包名。

3.6 JNI签名是什么

  • 为什么JNI中忽然多出了一个概念叫”签名”:

    • 因为Java是反对函数重载的,也就是说,能够定义雷同办法名,然而不同参数的办法,而后Java依据其不同的参数,找到其对应的实现的办法。
    • 这样是很好,所以说JNI必定要反对的,如果仅仅是依据函数名,没有方法找到重载的函数的,所以为了解决这个问题,JNI就衍生了一个概念——”签名”,行将参数类型和返回值类型的组合。
    • 如果领有一个该函数的签名信息和这个函数的函数名,就能够程序的找到对应的Java层中的函数。
  • 如何查看签名呢:能够应用javap命令。

    • javap -s -p MainActivity.class

04.一些必备操作

4.1 so库生成打包

  • 什么是so文件库

    • so库,行将C或者C++实现的性能进行打包,将其打包为共享库,让其余程序进行调用,这能够进步代码的复用性。
  • 对于.so文件的生成有两种形式

    • 能够提供给大家参考,一种是CMake主动生成法,另一种是传统打包法。
  • so文件在程序运行时就会加载

    • 所以想应用Java调用.so文件,必有某个Java类运行时load了native库,并通过JNI调用了它的办法。
  • cmake生成.so计划

    • 第一步:创立native C++ Project我的项目,创立native函数并实现,先测试本地JNI函数调通
    • 第二步:获取.so文件。将生成的.apk文件改为.zip文件,而后进行解压缩,就能看到.so文件。如果想反对多种库架构,则可在module的build.gradle中配置ndk反对。
    • 第三步:so文件测试。新建一个一般的Android程序,将so库放入程序,而后创立类(留神要雷同的包名、文件名及办法名)去加载so库。
    • 总结一下:Android Studio主动创立的native C++我的项目默认反对CMake形式,它反对JNI函数调用的入口在build.gradle中。
  • 传统打包生成.so计划【不举荐这种形式】

    • 第一步:在Java类中申明一个本地办法。
    • 第二步:执行指令javah取得C申明的.h文件。
    • 第三步:取得.c文件并实现本地办法。创立Android.mk和Application.mk,并配置其参数,两个文件如不编写或编写失常会呈现报错。
    • 第四步:打包.so库。cd到\app目录下,执行命令 ndk-build即可。生成so库后,最初测试ok即可。

4.2 so库查问操作

  • so库如何查找所对应的地位

    • 第一步:在 app 模块的 build.gradle 中,追加以下代码:
    • 第二步:执行命令行:./gradlew assembleDebug 【留神如果遇到gradlew找不到,则输出:chmod +x gradlew】
  • so文件查问后果后。就能够查问到so文件属于那个lib库的!如下所示:libtestjnilib.so文件属于TestJniLib库的

    find so file: /Users/yc/github/YCJniHelper/TestJniLib/build/intermediates/library_jni/debug/jni/armeabi-v7a/libtestjnilib.sofind so file: /Users/yc/github/YCJniHelper/SafetyJniLib/build/intermediates/library_jni/debug/jni/armeabi-v7a/libsafetyjnilib.sofind so file: /Users/yc/github/YCJniHelper/SignalHooker/build/intermediates/library_jni/debug/jni/armeabi-v7a/libsignal-hooker.so

05.实际几个案例

5.1 Java动态调用C/C++

  • Java调用C/C++函数调用流程

    • Java层调用某个函数时,会从对应的JNI层中寻找该函数。依据java函数的包名、办法名、参数列表等多方面来确定函数是否存在。
    • 如果没有就会报错,如果存在就会就会建设一个关联关系,当前再调用时会间接应用这个函数,这部分的操作由虚拟机实现。
  • Java层调用C/C++办法操作步骤

    • 第一步:创立java类NativeLib,而后定义native办法stringFromJNI()

      public native String stringFromJNI();
    • 第二步:依据此native办法编写C文件,能够通过命令后或者studio提醒生成C++对应的办法函数

      //java中stringFromJNI//extern “C”    指定以"C"的形式来实现native函数extern "C"//JNIEXPORT     宏定义,用于指定该函数是JNI函数。示意此函数能够被内部调用,在Android开发中不可省略JNIEXPORT jstring//JNICALL       宏定义,用于指定该函数是JNI函数。,无实际意义,然而不可省略JNICALL//以留神到jni的取名规定,个别都是包名 + 类名,jni办法只是在后面加上了Java_,并把包名和类名之间的.换成了_Java_com_yc_testjnilib_NativeLib_stringFromJNI(JNIEnv *env, jobject /* this */) {//JNIEnv 代表了JNI的环境,只有在本地代码中拿到了JNIEnv和jobject//JNI层实现的办法都是通过JNIEnv 指针调用JNI层的办法拜访Java虚拟机,进而操作Java对象,这样就能调用Java代码。//jobject thiz//在AS中主动为咱们生成的JNI办法申明都会带一个这样的参数,这个instance就代表Java中native办法申明所在的std::string hello = "Hello from C++";//思考一下,为什么间接返回字符串会呈现谬误提醒?//return "hello";return env->NewStringUTF(hello.c_str());}
  • 举一个例子

    • 例如在 NativeLib 类的native stringFromJNI()办法,程序会主动在JNI层查找 Java_com_yc_testjnilib_NativeLib_stringFromJNI 函数接口,如未找到则报错。如找到,则会调用native库中的对应函数。

5.2 C/C++调用Java

  • Native层调用Java层的类的字段和办法的操作步骤

    • 第一步:创立一个Native C++的Android我的项目,创立 Native Lib 我的项目
    • 第二步:在cpp文件夹下创立:calljnilib.cpp文件,calljnilib.h文件(用来申明calljnilib.cpp中的办法)。
    • 第三步:开始编写配置文件CmkaeLists.txt文件。应用add_library创立一个新的so库
    • 第四步:编写 calljnilib.cpp文件。因为要实现native层调用Java层字段和办法,所以这里定义了两个办法:callJavaField和callJavaMethod
    • 第五步:编写Java层的调用代码此处要留神的是调用的类的类名以及包名都要和c++文件中申明的统一,否则会报错。具体看:CallNativeLib
    • 第六步:调用代码进行测试。而后查看测试后果

5.3 Java调三方so中API

  • 间接拿后面案例的 calljnilib.so 来测试,然而为了实现三方调用还须要对文件进行革新

    • 第一步:要实现三方so库调用,在 calljnilib.h中申明两个和 calljnilib.cpp中对应的办法:callJavaField和callJavaMethod,个别状况下这个头文件是第三方库一起提供的给内部调用的。
    • 第二步:对CMakeLists配置文件革新。次要是做一些库的配置操作。
    • 第三步:编写 third_call.cpp文件,在这外部调用第三方库。这里须要将第三方头文件导入进来,如果CmakeLists文件中没有申明头文件,就应用#include "include/calljnilib.h" 这种形式导入
    • 第四步:最初测试下:callThirdSoMethod("com/yc/testjnilib/HelloCallBack","updateName");

5.4 Java动静调C++

  • 先说一下动态调C++的问题:

    • 在实现stringFromJNI()时,能够看到c++外面的办法名很长 Java_com_yc_testjnilib_NativeLib_stringFromJNI。
    • 这是jni动态注册的形式,依照jni标准的命名规定进行查找,格局为Java_类门路_办法名。Studio默认这种形式名字太长了,是否设置短一点。
    • 程序运行效率低,因为首次调用native函数时须要依据依据函数名在JNI层中搜寻对应的本地函数,而后建设对应关系,这个过程比拟耗时。
  • 动静注册办法解决下面问题

    • 当程序在Java层运行System.loadLibrary("testjnilib");这行代码后,程序会去载入testjnilib.so文件。
    • 于此同时,产生一个Load事件,这个事件触发后,程序默认会在载入的.so文件的函数列表中查找JNI_OnLoad函数并执行。与Load事件绝对,在载入的.so文件被卸载时,Unload事件被触发。
    • 此时,程序默认会去载入的.so文件的函数列表中查找JNI_OnLoad函数并执行,而后卸载.so文件。
    • 因而开发者常常会在JNI_OnLoad中做一些初始化操作,动静注册就是在这里进行的,应用env->RegisterNatives(clazz, gMethods, numMethods)。
  • 动静注册操作步骤:

    • 第一步:因为System.loadLibrary()执行时会调用此办法,实现JNI_OnLoad办法。
    • 第二步:调用FindClass找到须要动静注册的java类【定义要关联的对应Java类】,留神这个是native办法那个类的门路字符串
    • 第三步:定义一个静态数据(JNINativeMethod类型),外面寄存须要动静注册的native办法,以及参数名称
    • 第四步:通过调用jni中的RegisterNatives函数将注册函数的Java类,以及注册函数的数组,以及个数注册在一起,这样就实现了绑定。
  • 动静注册劣势剖析

    • 相比动态注册,动静注册的灵活性更高,如果批改了native函数所在类的包名或类名,仅调整native函数的签名信息即可。
    • 还有一个劣势:动静注册,java代码不须要更改,只须要更改native代码。
    • 效率更高:通过在.so文件载入初始化时,即JNI_OnLoad函数中,后行将native函数注册到VM的native函数链表中去,后续每次java调用native函数时都会在VM中的native函数链表中找到对应的函数,从而加快速度。

06.一些技术原理

6.1 JNIEnv创立和开释

  • JNIEnv的创立形式

    • C 中——JNIInvokeInterface:JNIInvokeInterface是C语言环境中的JavaVM构造体,调用 (AttachCurrentThread)(JavaVM, JNIEnv*, void) 办法,可能取得JNIEnv构造体;
    • C++中 ——_JavaVM:_JavaVM是C++中JavaVM构造体,调用jint AttachCurrentThread(JNIEnv* p_env, void thr_args) 办法,可能获取JNIEnv构造体;
  • JNIEnv的开释:

    • C 中开释:调用JavaVM构造体JNIInvokeInterface中的(DetachCurrentThread)(JavaVM)办法,可能开释本线程的JNIEnv
    • C++ 中开释:调用JavaVM构造体_JavaVM中的jint DetachCurrentThread(){ return functions->DetachCurrentThread(this); } 办法,就能够开释 本线程的JNIEnv
  • JNIEnv和线程的关系

    • JNIEnv只在以后线程无效:JNIEnv仅仅在以后线程无效,JNIEnv不能在线程之间进行传递,在同一个线程中,屡次调用JNI层不便,传入的JNIEnv是同样的
    • 本地办法匹配多个JNIEnv:在Java层定义的本地办法,可能在不同的线程调用,因而可能承受不同的JNIEnv

6.2 动静注册的原理

  • 在Android源码开发环境下,大多采纳动静注册native办法。

    • 利用构造体JNINativeMethod保留Java Native函数和JNI函数的对应关系;
    • 在一个JNINativeMethod数组中保留所有native函数和JNI函数的对应关系;
    • 在Java中通过System.loadLibrary加载完JNI动静库之后,调用JNI_OnLoad函数,开始动静注册;
    • JNI_OnLoad中会调用AndroidRuntime::registerNativeMethods函数进行函数注册;
    • AndroidRuntime::registerNativeMethods中最终调用jni RegisterNativeMethods实现注册。
  • 动静注册原理剖析

    • RegisterNatives 形式的实质是间接通过构造体指定映射关系,而不是等到调用 native 办法时搜寻 JNI 函数指针,因而动静注册的 native 办法调用效率更高。
    • 此外,还能缩小生成 so 库文件中导出符号的数量,则可能优化 so 库文件的体积。

6.3 注册JNI流程图

  • 提到了注册 JNI 函数(建设 Java native 办法和 JNI 函数的映射关系)有两种形式:动态注册和动静注册。

  • 剖析下动态注册匹配 JNI 函数的执行过程

    • 第一步:以 loadLibrary() 加载 so 库的执行流程为线索进行剖析的,最终定位到 FindNativeMethod() 这个办法。
    • 第二步:查看java_vm_ext.cc中FindNativeMethod办法,而后看到jni_short_name和jni_long_name,获取native办法对应的短名称和长名称。
    • 第三步:在java_vm_ext.cc,通过FindNativeMethodInternal查找曾经加载的so库中搜寻,先搜寻短名称,而后再搜寻长名称
    • 第四步:建设外部数据结构,建设 Java native 办法与 JNI 函数的函数指针的映射关系,调用 native 办法,则间接调用已记录的函数指针。

07.JNI遇到的问题

7.1 混同的bug

  • 在Android工程中要排除对native办法以及所在类的混同(java工程不须要),否则要注册的java类和java函数会找不到。proguard-rules.pro中增加。

    # 设置所有 native 办法不被混同-keepclasseswithmembernames class * {    native <methods>;}# 不混同类-keep class com.yc.testjnilib.** { *; }

7.2 留神字符串编译

  • 比方:对于JNI办法来说,应用如下办法返回或者调用间接解体了,有点搞不懂原理?

    env->CallMethod(objCallBack,_methodName,"123");
  • 这段代码编译没问题,然而在运行的时候就报错了:

    JNI DETECTED ERROR IN APPLICATION: use of deleted global reference
  • 最终定位到是最初一个参数须要应用jstring而不能间接应用字符串示意。如下所示:

    //思考一下,为什么间接返回字符串会呈现谬误提醒?为何这样设计……//return "hello";return env->NewStringUTF(hello.c_str());

代码案例:https://github.com/yangchong211/YCJniHelper

其余案例:https://github.com/yangchong211/YCAppTool