JNI和NDK开发(1)_创建JNI程序

版权声明:本博客主要记录学习笔记和遇到的一些问题解决方案,转载请注明出处! https://blog.csdn.net/u010982507/article/details/85345985

开始学习JNI开发技术,在网上看了很多文章,但讲解的都是基础或者过时的技术,没有系统的关于JNI和NDK的学习教程,现在我写《JNI和NDK开发》系列文章,主要是记录自己从零开始学习遇到的一些问题和知识点,希望对大家也有些帮助。对于文章,本人也是边学边写, 所以可能会更新的慢一点,大家有问题可以留言。

本系列文章主要解决的问题是:

  • JNI和NDK开发常用API的使用
  • JNI和NDK开发常见问题的解决
  • JNI和NDK应用场景实践

JNI和NDK的概念

JNI全称Java Native Interface,意为java本地接口,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。

NDK全称Native Development Kit,是 Android的一个工具开发包 ,在Android开发中使用NDK编写JNI程序就能实现对C和C++代码的互相调用。

使用JNI和NDK的好处

  • 效率
    大家都知道,Android的底层是使用C&C++实现的,所以使用C&C++编写的程序,效率和性能上比java编写的程序高。
  • 安全
    应用层编写的java代码很容易被反编译,而通过JNi编译出的so库是一个二进制文件,对于反编译难度要大一点,如果在程序中再加上一些反调试代码和签名验证,又更提高了反编译的难度,所以一些核心加密的算法会使用JNI来编写。
  • 调用Linux函数
    众所周知,Android的核心是Linux系统,通过java是无法调用Linux系统函数的,而使用JNI编写C&C++代码却可以调用Linux的系统函数,例如通过调用fork函数创建子进程来实现关键进程的保活,使用mmap映射文件来将日志写入到内存中等。
  • 使用C&C++编译的开源库
    使用C&C++编写的著名开源库有很多(例如音视频处理库FFmpeg,图形图像处理库OpenCV等),而Android应用层想要使用使用这些库就必须通过JNI程序调用。

JNI和NDK应用场景

  • 音视频处理
    像优酷、Bilibili、爱奇艺、抖音等这些视频播放和直播软件都是用的JNI程序调用FFmpeg开源库。
  • 图形图像处理
    例如人脸识别、图像识别技术。
  • 通信协议加密SDK
    封装一个Http请求的通信加密的SDK,加密算法是在JNI层实现的。
  • Android软件调用硬件设备
    例如手机软件

创建第一个JNI程序

网上很多文章讲的都是Android Studio2.2之前通过Android.mk来创建编译JNI程序,因为时间有限,我也没去学习和研究,所以直接使用Android Studio2.2之后通过CMake来编译JNI程序,我当前的Android环境配置是:Android studio版本是3.0.1,gradle插件版本3.0.1,gradle版本是4.1,NDK版本是16。废话不多说,下面开始创建第一个JNI程序。

1、 NDK环境配置
在Android Studio中查看Android SDK的SDK Tools配置,如下图所示,保证这三个SDK要下载。
在这里插入图片描述

2、Include C++ support
打开Android Studio创建一个新工程,如下图所示,红框标注的意思是增加JNI的支持,这里如果选中就会创建一个JNI程序,点击执行下一步。
在这里插入图片描述

配置C++编译环境

  • C++ Standard
    指定编译库的环境,其中Toolchain Default使用的是默认的CMake环境;C++ 11也就是C++环境。
  • Exceptions Support
    如果选中复选框,则表示当前项目支持C++异常处理,如果支持,在项目Module级别的build.gradle文件中会增加一个标识 -fexceptions到cppFlags属性中,并且在so库构建时,gradle会把该属性值传递给CMake进行构建。
  • **Runtime Type Information Support**
    选中复选框,项目支持RTTI,属性cppFlags增加标识-frtti。

在这里插入图片描述

创建完成后运行程序, 就会在手机上显示Hello from C++,从源码中可以看到这段文本是从native-lib.cpp中返回的。效果看到了那就开始分析JNI的实现逻辑。

3、加载so库
在MainActivity中可以看到这段代码

static {
  System.loadLibrary("native-lib");
}

这段代码的意思是加载名为native-lib的so文件,那native-lib这个名字是在哪定义的呢,其实是在CMakeLists.txt中。
4、CMakeLists.txt配置
打开app目录下的CMakeLists.txt文件,可以看到很大一堆注释,让人看着就不简单,所以我把注释去掉,只留下有用的部分,无非就四行。

// 要求使用的cmake最小版本
cmake_minimum_required(VERSION 3.4.1)
// 添加共享库:命名为native-lib;设置为SHARED共享;文件路径是src/main/cpp/native-lib.cpp
add_library( native-lib SHARED src/main/cpp/native-lib.cpp )
// 找到Android自带的日志库log,并赋值给log-lib变量
find_library( log-lib log )
// 将native-lib库和log-lib库
target_link_libraries( native-lib ${log-lib} )

简单说一下CMake的这几条指令的意思:

  • add_library
    将指定的文件生成与库链接的目标文件。
    -find_library
    查找库,并将库赋值给变量。
  • target_link_libraries
    将目标文件与生成的库文件链接。

5、build.gradle中配置
在Android Studio中CMakeLists.txt文件是怎么创建的呢?看一下app下的build.gradle文件,在android节点下配置

externalNativeBuild {
    cmake {
        path "CMakeLists.txt"
    }
}

这段代码的作用就是配置CMakeLists.txt文件路径,Android Studio会根据这个路径找到并创建。
除了CMakeLists.txt文件路径的配置,还有一段externalNativeBuild的配置,在defaultConfig节点下:

externalNativeBuild {
    cmake {
        cppFlags ""
    }
}

这段意思是配置CMake编译环境,在app目录下可以看到.externalNativeBuild编译文件。
6、C&C++文件
app目录下,有一个cpp目录,里面放的就是我们的JNI源程序,打开native-lib.cpp文件,就可以看到JNI代码的语法结构。

#include <jni.h>
#include <string>
extern "C"
JNIEXPORT jstring JNICALL
Java_com_rzr_jni_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

7、原生接口定义和调用
打开MainActivity找到代码如下:

/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();

这段代码定义了原生与JNI的接口函数,我们可以通过调用这个接口来获取JNI返回的结果。

// Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());

到此,我们就创建和分析了一个带JNI的Android工程。

猜你喜欢

转载自blog.csdn.net/u010982507/article/details/85345985