一、JNI简介
JNI = Java Native Interface,Java本地接口。
二、交叉编译
在什么平台运行的软件就需要在什么平台进行编译,为什么Android可以在Windows下kaifa-并在手机的unix系统上运行呢?这是因为Java是运行在虚拟机上的。为什么说Java是一次编译到处执行?
交叉编译:在一个平台下编译出另一个平台下可以运行的本地代码
- cpu平台 x86 arm
- 操作系统平台 windows linux mac os unix
- 不同的cpu和不同的操作系统所支持的指令集是不一样的,所以在哪个平台上的软件需要在哪个平台上进行编译。
三、NDK简介
NDK = native develop kit,本地开发工具集。
通过NDK来实现交叉编译,NDK是Google提供的。
四、NDK集成开发流程(以cmake工程为例)
1、下载NDK
解压NDK的zip包到非中文目录
2、配置环境变量
根目录中有ndk-build.cmd文件,将根目录配置环境变量path中
3、在local.properties 中配置ndk根路径
ndk.dir=C:\Users\94317\AppData\Local\Android\ndk\android-ndk-r25b
4、在gradle.properties 中配置useDeprecatedNdk 属性
android.useDeprecatedNdk = true,作用是兼容老的ndk
目前,useDeprecatedNdk 已经不支持,改为cmake或者ndk-bundle
5、定义本地接口
public class JNI {
/**
* 定义本地接口
*/
public native String stringFromJNI();
}
6、编写C++代码
在main目录下创建一个任意名称的文件夹,比如cpp文件夹,在cpp文件夹中创建一个cpp文件,比如:native-lib.cpp,代码如下:
#include
#include
using namespace std;
extern "C" JNIEXPORT jstring JNICALL
Java_com_nobug_jniproject_JNI_stringFromJNI(JNIEnv* env,jobject /* this */) {
string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
extern "C":在编译时,模式是以C++11标准编译,不支持C语言的编译,所以需要添加 extern "C",能够让C和C++互相调用。
jstring:表示返回值是string类型
7、cmake配置
在cpp目录下新建cmake文件,文件名固定,不可修改:CMakeLists.txt,配置如下:
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.18.1)
# Declares and names the project.
project("jniproject")
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
jniproject
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
native-lib.cpp)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
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)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
jniproject
# Links the target library to the log library
# included in the NDK.
${log-lib})
cmake_minimum_required:指定cmake的最小版本
project("jniproject"):cmake工程名称
add_library:添加库。jniproject:库名,SHARED:表示动态库
8、模块的build.gradle 下配置cmake
android {
compileSdk 32
defaultConfig {
// ...
externalNativeBuild {
cmake {
abiFilters 'armeabi-v7a'
cppFlags "-std=c++11 -frtti -fexceptions -Os -Wall"
}
}
ndk { // "armeabi-v7a", "arm64-v8a"
abiFilters 'armeabi-v7a'
}
}
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
version '3.18.1'
}
}
}
cmake 指定c++11标准,并且cpu架构需要指定为 armeabi-v7a,如果不指定cpu架构,默认的cpu架构是:arm64-v8a
9、编译
编译之后生成 so 库:
10、加载动态库
public class JNI {
static {
// 导入动态库
System.loadLibrary("jniproject");
}
...
}
11、链接动态库
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// Example of a call to a native method
TextView tv = binding.sampleText;
tv.setText(new JNI().stringFromJNI());
}
}
五、生成头文件
头文件代码如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_nobug_jniproject_JNI */
#ifndef _Included_com_nobug_jniproject_JNI
#define _Included_com_nobug_jniproject_JNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_nobug_jniproject_JNI
* Method: stringFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_nobug_jniproject_JNI_stringFromJNI
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
该文件可以帮助程序员快速完成JNI接口的程序编写。
生成该文件只需要一个命令即可生成。
第一步:切换目录
cd .\app\src\main\java\
第二步:拷贝Java本地接口文件的引用
复制出来的内容是:com.nobug.jniproject.JNI
第三步:执行命令生成头文件
javah com.nobug.jniproject.JNI
六、快速创建JNI函数
除了使用 javah 生成JNI代码,还可以使用快捷键可以直接生成。
首先定义好native函数,按下快捷键 alt+enter,如下图:
选择"Create JNI function for xxxx",可以快速生成JNI代码。
[本章完...]