• Stars
    star
    266
  • Rank 154,103 (Top 4 %)
  • Language
    Java
  • License
    Apache License 2.0
  • Created about 7 years ago
  • Updated about 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

JNI学习案例,通过简单的案例快速入门jni开发。JNI基础语法介绍,so库生成打包和反编译,Java和C/C++相关调用案例

目录介绍

  • 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库
  • 相关学习文档

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 /build/core/build-local.mk
    • $GNUMAKE 指向 GNU Make 3.81 或更高版本, 则指向 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++代码之间做切换即可
    • image
  • 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.so
    find so file: /Users/yc/github/YCJniHelper/SafetyJniLib/build/intermediates/library_jni/debug/jni/armeabi-v7a/libsafetyjnilib.so
    find 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 函数的映射关系)有两种方式:静态注册和动态注册。
    • image
  • 分析下静态注册匹配 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

More Repositories

1

YCBlogs

技术博客笔记大汇总,包括Java基础,线程,并发,数据结构;Android技术博客等等;常用设计模式;常见的算法;网络协议知识点;部分flutter笔记;还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!转载请注明出处,谢谢!
6,403
star
2

YCAppTool

🔥🔥🔥 组件化综合案例,组件分层为:基础公共组件,功能组件,业务组件,主工程。每一层组件的建设,很详细的工程案例,很全面的一个组件化实践案例。一个超级综合案例!
Java
3,172
star
3

YCVideoPlayer

🔥🔥🔥 基础封装视频播放器player,可以在ExoPlayer、MediaPlayer原生MediaPlayer可以自由切换内核;该播放器整体架构:播放器内核(自由切换) + 视频播放器 + 边播边缓存 + 高度定制播放器UI视图层。支持视频简单播放,列表播放,仿抖音滑动播放,自动切换播放,使用案例丰富,拓展性强。
Java
2,192
star
4

YCWebView

基于腾讯x5开源库,提高webView开发效率,大概要节约你百分之六十的时间成本。该案例支持处理js的交互逻辑且无耦合、同时暴露进度条加载进度、可以监听异常error状态、支持视频播放并且可以全频、支持加载word,xls,ppt,pdf,txt等文件文档、发短信、打电话、发邮件、打开文件操作上传图片、唤起原生App、支持webView页面截图、x5库为最新版本,功能强大。
Java
1,735
star
5

YCAndroidTool

用于项目测试,崩溃重启操作,崩溃记录日志【可以查看,分享】和重启【多种重启app方式】;网路拦截查看的工具小助手,拦截请求和响应数据,统计接口请求次数,流量消耗,以及统计网络链接/dns解析/request请求/respond响应等时间。提高开发效率……
Java
749
star
6

YCStateLayout

State switching, so that the View state switch and Activity completely separate. Using builder mode to freely add the required state View, can set data, data is empty, load data error, network error, load and other states, and support the layout of custom state. At present has been used in other formal projects, strong expansion!
Java
556
star
7

YCSlideLayout

购物商场商品详情页面自定义控件,模仿淘宝、京东、考拉等商品详情页分页加载的UI效果。可以嵌套RecyclerView、WebView、ViewPager、ScrollView等等。支持设置上拉分页加载动画效果……
Java
491
star
8

YCCustomText

自定义文本控件,支持富文本,包含两种状态:编辑状态和预览状态。编辑状态中,可以对插入本地或者网络图片,可以同时插入多张有序图片和删除图片,支持图文混排,并且可以对文字内容简单操作加粗字体,设置字体下划线,支持设置文字超链接(超链接支持跳转),已经用于多个实际项目中……
Java
479
star
9

YCRefreshView

自定义支持上拉加载更多,下拉刷新,可以自定义头部和底部,可以添加多个headerView,使用一个原生recyclerView就可以搞定复杂界面。支持自由切换状态【加载中,加载成功,加载失败,没网络等状态】的控件,可以自定义状态视图View。拓展功能【支持长按拖拽,侧滑删除】,轻量级,可以选择性添加 。持续更新……
Java
454
star
10

YCUpdateApp

轻量级版本更新弹窗,弹窗上支持更新进度条,可以设置普通更新或者强制更新。解决8.0以上通知栏不显示问题,解决7.0以上安装apk异常,下载完成则会自动提示安装;下载异常,失败,错误等状态,支持重启下载任务;还支持自定义下载路径。代码量少,调用简单……
Java
446
star
11

YCCaptureTool

App综合防抓包实践,关闭代理,设置配置文件;设置单向认证或双向认证;对数据加密和解密;sign签名参数;防止xposed,root,va挂载等操作。 比较通用全面的防抓包技术方案!
Java
421
star
12

YCScrollPager

仿抖音,快手,短视频,竖直方向,一次滚动一个页面的封装库。目前支持ViewPager做法,也支持RecyclerView做法……使用ViewPager则只是修改滑动速率,以及滚动翻页过渡时间;使用recyclerView打造丝滑切换视频的功能,更多内容可以看demo
Java
376
star
13

YCNotification

通知栏封装库,强大的通知栏工具类,链式编程调用,解决了8.0以上通知栏不显示问题,支持多种不同的使用场景,兼容老版本。还有自定义通知栏view,可高度定制布局……
Java
376
star
14

YCDialog

自定义弹窗,其中包括:自定义Toast;自定义dialog控件;自定义DialogFragment弹窗;自定义PopupWindow弹窗;还有自定义Snackbar等等;简单便用。目前已经用于多个正式项目中。
Java
359
star
15

YCStudyCpp

🔥🔥🔥 c++学习案例,系统学习,c++教程,基础案例,数据类型,变量和常量,运算符,表达式,判断和循环,函数,模版函数,指针,引用,数组和容器,IO输入和输出,结构和类,继承和派生,多线程,并发,线程安全锁,内存分配,堆和栈,异常,STL标准模版,预处理器,信号处理,程序调试,网络通信,综合案例等等
C++
353
star
16

YCStatusBar

关于状态栏方案总结案例,适合于绝大多数的使用场景……同时,兼容了小米,魅族等手机状态栏字体颜色设置失效问题。已经用于实际项目投资界,新芽,沙丘大学等APP,一键集成并使用!支持单Activity多Fragment动态修改状态栏颜色,入侵性低,一行代码即可实现你需要的需求
Java
313
star
17

YCFlutterUtils

Flutter Utils 全网最齐全的工具类。包含bus,颜色,日期,文件,json,log,sp,加解密,num,图片,网络,正则,验证,路由,文本,时间,spi,计时器,拓展类,编解码,发射,异常,字节转化,解析等等工具类。
Dart
245
star
18

YCThreadPool

轻量级简易线程池库,轻量级线程池异步库,支持线程执行过程中状态回调监测(包含成功,失败,异常等多种状态);支持创建异步任务,可以设置线程的名称,延迟执行时间,线程优先级,回调callback等;可以根据自己需要创建自己需要的线程池;线程异常时,可以打印异常日志,避免崩溃。
Java
224
star
19

YCCommonLib

组件化开发中基础公共库,activity栈管理;fragment周期监听;Lru缓存库;反射库;分区存储;Log日志打印和存储;通用缓存库(支持sp,mmkv,lru,disk,fastsp等多种存储方式切换);App重启;通用全面的工具类Utils;通用基类fragment,adpater,activity等简单封装;intent内容打印到控制台库;通用基础接口
Java
191
star
20

YCWidgetLib

自定义控件,其中包含自定义折叠布局,拓展性强使用简单;万能自定义红点控件,充分解耦合接入方便;多种方案实现阴影效果库;自定义view和viewGroup圆角控件,彻底解决圆角问题等
Java
186
star
21

YCWalleHelper

瓦力多渠道打包的Python脚本测试工具,通过该自动化脚本,自需要run一下或者命令行运行脚本即可实现美团瓦力多渠道打包,打包速度很快。配置信息十分简单,代码中已经注释十分详细。可以自定义输出文件路径,可以修改多渠道配置信息,简单实用。
Tcl
133
star
22

YCBannerView

轮播图,支持多种自定义属性,可以设置轮播红点或者轮播数字,支持设置引导页。可以根据不同使用场景,可以选择无限循环,静态管理或者动态管理adapter,还可以设置暂停和开始轮播。后期添加了RecyclerView轮播图,同时自定义多种类型SnapHelper,卡片滑动流畅,目前已经用于多个正式项目中!!
Java
106
star
23

YCLeetcode

组件之间的通信,很友好起到隔离效果,接口+实现类,使用注解生成代码方式,无需手动注册,将使用步骤简单化,支持组件间以暴露接口提供服务的方式进行通信。
Java
76
star
24

YCStudyWeb

Web学习案例
JavaScript
71
star
25

YCToolLib

通用工具库组件,包括前后台判断,拦截器时间,心跳轮询库,Task任务库,二维码扫码库,转场动画库,通用TTS音频播放库,国际化locale库等等
Java
66
star
26

YCServerLib

gRPC学习案例,使用gRPC作为网络方案可以带来高效性、跨平台和语言、可靠性、易于使用和可扩展性等收益。同时可以减少手动编写代码的工作量,提高开发效率。
Java
51
star
27

YCStudyC

系统性学习C编程,变量,数据类型,循环,选择,输入和输出,数组和容器,函数,结构体,指针,预处理,文件操作,调试等。练习多个完整小项目:学生管理系统,贪吃蛇,万年历,推箱子,五子棋。是入手学习C的练习完整项目
C
49
star
28

YCDesignHelper

注解学习小案例,比较系统性学习注解并且应用实践。简单应用了运行期注解,通过注解实现了setContentView功能;简单应用了编译器注解,通过注解实现了防暴力点击的功能,同时支持设置时间间隔;使用注解替代枚举;使用注解一步步搭建简单路由案例。结合相应的博客,在来一些小案例,从此应该对注解有更加深入的理解……
Java
45
star
29

YCDesignBlog

设计模式,创建型设计模式,结构型设计模式,行为型设计模式,面向对象设计原则,六大设计原则,面向对象设计思想,项目架构演进。持续迭代笔记和完善案例,打造全网系统高质量的设计模式笔记!
Java
44
star
30

YCTimerHelper

倒计时工具,分别使用了handler,自定义CountDownTimer,Timer和TimerTask,chronometer控件,和属性动画实现倒计时的功能。封装CountDownTimer倒计时器【在原有基础上增加了暂停和恢复倒计时功能】,解决了倒计时无法到0和从最大值开始bug
Java
13
star
31

YCJavaBlog

Java高级进阶专栏博客,数据类型原理,泛型设计思想,面向对象,继承,封装,io流,序列化,拷贝,高效读写,反射,注解,spi机制,异常,四种引用,线程原理,线程通信,线程池设计思想,线程并发优化,上下文切换原理,CAS,锁的设计和原理,类加载,对象布局,代码攻击,内存模型,即时编译,内存回收机制,性能调优,集合设计思想
Java
7
star
32

YCComputerBlog

计算机基础博客,计算机组成原理,存储器的设计,局部性原理,地址映射,CPU处理器用途,指令架构,CPU性能,缓存一致性,Cache,MESI协议,伪共享,CPU三级缓存设计思想,输入和输出,总线系统设计,指令编程,程序如何运行,内存存储设计,物理内存和虚拟内存,共享内存设计,计算机二进制的由来,异常处理,IO流操作
4
star
33

YCSplashSdk

打开app启动页sdk,支持图片广告,也支持视频
3
star
34

YCUniApp

uni开发的项目,由于公司项目做混合开发,因此边学边做。基础入门级的案例分析,近期学习更新中
Vue
3
star
35

YCPluginTool

Plugin插件学习工具
2
star
36

YCAndroidBlog

Android技术专栏
1
star
37

yangchong211

1
star