- 1.背景
- 2.介绍
- 3.下载 NDK 和工具
- 4.配置NDK的环境变量
- 5.创建native相关方法
- 5.1 native相关方法去掉报红 - 6.创建c/c++文件
- 6.1 生成头文件
- 6.2 添加 c/c++文件 - 7.添加mk文件
- 7.1 添加Android.mk
文件(必加)
- 7.2 添加Application.mk
文件(可选) - 8.编译so库文件
- 9.用Gradle链接c++项目
- 10.加载so库、运行app
1.背景
本来一直在做商城类的项目舒坦着,突然老板拿了一块Android的主板和芯片过来,说我们打算做一款自动售货机,从没做过这类项目的我,当时就一脸懵逼了,芯片、自动售货机,What?还好我依稀记得,这类项目是关于NDK、JNI的,于是,我来了!
2.介绍
- 什么是NDK?
NDK全称是Native Development Kit,NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。NDK集成了交叉编译器(交叉编译器需要UNIX或LINUX系统环境),并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。
- 为什么使用NDK?
- 代码的保护。由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大。
- 可以方便地使用现存的开源库。大部分现存的开源库都是用C/C++代码编写的。
- 提高程序的执行效率。将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率。
- 便于移植。用C/C++写得库可以方便在其他的嵌入式平台上再次使用。
- 什么是JNI?
JNI的全称是Java Native Interface,它提供了若干的API实现了Java和其他语言的通信(主要是C和C++)。
- 为什么使用JNI?
JNI的目的是使java方法能够调用c实现的一些函数。
- 安卓中的so文件是什么?
android中用到的so文件是一个c++的函数库。在android的JNI中,要先将相应的C语言打包成so库,然后导入到lib文件夹中供java调用。
3.下载 NDK 和工具
为了给应用编译和调试原生代码,我们需要以下组件:
- Android 原生开发工具包 (NDK):这套工具集允许您为 Android 使用 C 和 C++ 代码,并提供众多平台库,让您可以管理原生 Activity 和访问物理设备组件,例如传感器和触摸输入。
- CMake:一款外部构建工具,可与 Gradle 搭配使用来构建原生库。如果您只计划使用 ndk-build,则不需要此组件。
- LLDB:一种调试程序,Android Studio 使用它来调试原生代码。
我们可以在SDK 管理器安装这些组件
- 依次打开 Settings>Appearance&Behavior>System Settings>Android SDK>SDK Tools
- 勾上LLDB、CMake 和 NDK进行下载
4.配置NDK的环境变量
- 打开File > Project Structure > SDK Location,选择默认NDK的路径
- 复制NDK的路径
- 右击我的电脑>属性>高级系统设置>环境变量>新建,添加一个系统变量NDK_HOME,并把刚才复制的ndk-bundle的路径填上去,记得确认。
- 找到Path系统变量(不需要创建),新建一个%NDK_HOME%,也就是上面NDK_HOME的变量添加进去。
-
在Terminal/cmd中不需要考虑路径,直接输入 ndk-build ,如出现如下内容,则表明NDK环境配置成功!(配置NDK环境前如果已经打开了Android Studio或者Cmd需要重新启动,否则可能没效果!)
5.创建native相关方法
5.1 native相关方法去掉报红
取消检测即可,打开 Settings>Editor>Inspections>Android>Missing JNI function 去掉勾选。
去掉后,效果如下:
6.创建c/c++文件
6.1 生成头文件
- Terminal终端,通过下面命令 切换到
项目xx\app\
目录下。
cd D:\Workspace\NDKFirst\app>
注:由于下面的路径都比较长,我们可以右击相应的目录进行快捷复制:
- 根据java文件生成c的头文件, 执行如下命令
格式:
javah d jni -encoding utf-8 classpath java文件夹路径 包名+类名
javah d jni classpath D:\Workspace\NDKFirst\app\src\main\java com.brainbg.ndkfirst.NDKUtils
或者
javah d jni -encoding utf-8 classpath D:\Workspace\NDKFirst\app\src\main\java com.brainbg.ndkfirst.NDKUtils
其中
- javah :
- d jni :创建jni目录
- -encoding utf-8 :指定编码格式为utf-8
- classpath D:\Workspace\NDKFirst\app\src\main\java\ :到java目录的路径
- com.brainbg.ndkfirst.JNIUtils :包名+类名
注:不加上-encoding utf-8
,可能会提示错误: 编码GBK的不可映射字符
。
- 执行后,收缩app目录后重新打开,会发现多了一个jni的目录,
com_brainbg_ndkfirst_NDKUtils.h
就是新生成的头文件。
com_brainbg_ndkfirst_NDKUtils.h
内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_brainbg_ndkfirst_NDKUtils */
#ifndef _Included_com_brainbg_ndkfirst_NDKUtils
#define _Included_com_brainbg_ndkfirst_NDKUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_brainbg_ndkfirst_NDKUtils
* Method: getStringFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_brainbg_ndkfirst_NDKUtils_getStringFromJNI
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
中间一段最为核心:
格式:Java_包名类名方法名
6.2 添加 c/c++文件
- 添加cpp文件
first.cpp
- 添加c文件
first.c
#include
#include "com_brainbg_ndkfirst_NDKUtils.h"
JNIEXPORT jstring JNICALL
Java_com_brainbg_ndkfirst_NDKUtils_getStringFromJNI(JNIEnv* env, jobject obj) {
return (*env)->NewStringUTF(env,"This is my first jni!");
}
修改好的内容后,你会留意到上面还有提示:大意就是目前的c/c++文件还不属于项目中的一部分!为此,我们还需要处理build.gradle、Android.mk等文件。
7.添加mk文件
7.1 添加 Android.mk
文件(必加)
注意mk文件里面不能添加注释,不然编译不通过。
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := first-jni
LOCAL_SRC_FILES := first.c
include $(BUILD_SHARED_LIBRARY)
更多内容,可以直接查看:Android.mk 官方介绍
7.2 添加 Application.mk
文件(可选)
APP_PLATFORM := android-16
APP_ABI :=all
- APP_PLATFORM :指定so库所支持最低的API
- APP_ABI:指定生成平台的so库
注:不添加Application.mk
,会提示Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-16.
更多内容,可以直接查看:Application.mk 官方介绍
8.编译so库文件
进入app目录,执行ndk-build
进行编译
cd D:\Workspace\NDKFirst\app
ndk-build
执行成功后,效果如下
同时项目中会得到相应的so包,其中lib为核心,obj为编译中产生的文件,可删除。
9.用Gradle链接c++项目
- jni目录中右击任意文件选择
Link C++ project with Gradle
- 其中
Build System
选择ndk-build ,Project Path
选择Android.mk的路径,而后确认。
- 完成上面的操作后,app/build.gradle里面会出现如下代码
android {
......
externalNativeBuild {
ndkBuild {
path file('jni/Android.mk')
}
}
}
当然,下次项目的话,我们直接加入上面代码也可。
10.加载so库、运行app
- NDKUtils.java
public class NDKUtils {
public static final String TAG = NDKUtils.class.getSimpleName();
static {
try {
System.loadLibrary("first-jni"); //加载so库
} catch (UnsatisfiedLinkError e) {
e.printStackTrace();
Log.e(TAG,"loadLibrary fail !");
}
}
public static native String getStringFromJNI();
}
- MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tvContent = findViewById(R.id.tv_content);
tvContent.setText(NDKUtils.getStringFromJNI());
}
}
- activity_main.xml
- 运行后效果
到此为止,第一个关于NDK、JNI的Demo已经完成,相关文章,后续可能、应该、大概也会推出吧。
11.下载地址
GitHub项目下载
参考资料
https://developer.android.google.cn/ndk/guides
https://blog.csdn.net/young_time/article/details/80346631
https://yq.aliyun.com/articles/60710?spm=a2c4e.11153940.0.0.11bc68d9CLrDix
作者:Brainbg
GitHub:https://github.com/Brainbg
博客:https://www.brainbg.com/
:https://www.jianshu.com/u/94518ede7100
CSDN:https://blog.csdn.net/u014720022