随着Android Studio 2.2的发布,开发包含C ++代码的Android应用程序比以往任何时候都更加容易。 在本教程中,我将向您展示如何使用通常称为NDK的Android Native Development Kit创建一个本机C ++库,其功能可用于Java类。
先决条件
为了能够遵循本教程,您将需要以下内容:
- 最新版本的Android Studio
- 对C ++语法的基本了解
1.为什么要编写本机代码?
根据经验,您将仅使用Java开发Android应用程序。 添加C ++代码会大大增加其复杂性,并降低其可移植性。 不过,出于某些原因,您仍然想要这样做:
- 最大化性能 :通过用C ++实现其业务逻辑中占用大量CPU的部分,您可以提高Android应用程序的性能,尽管只是略有提高。
- 要使用高性能API :NDK中包括Vulkan Graphics和OpenSL ES之类的API规范的实现。 因此,Android游戏开发人员倾向于使用NDK。
- 要使用流行的C / C ++库,请执行以下操作 :现有许多C和C ++库没有Java等效项。 如果您想在Android应用程序中使用它们,则可以使用NDK。
- 重用代码 :只要不包含任何特定于平台的依赖项,用C ++编写的代码就可以在Android和iOS应用程序中使用,通常只需很少的更改即可。 如果您正在开发大型应用程序并打算同时支持iOS和Android平台,则使用C ++可能会提高您的生产率。
2.创建一个新项目
在Android Studio 2.2或更高版本中,项目创建向导可让您快速创建支持C ++代码的新项目。
首先启动Android Studio,然后在欢迎屏幕中按“ 启动新的Android Studio项目”按钮。 在下一个屏幕中,为您的应用程序指定一个有意义的名称,然后选中“ 包括C ++支持”字段。
![项目配置画面](https://i-blog.csdnimg.cn/blog_migrate/90d767c7b01be47ff649895a8d9afd6a.png)
在向导的活动创建屏幕中,选择“不添加活动”选项。 在向导的最后一个屏幕中,确保“ C ++标准”字段的值设置为“ 默认工具链” ,然后按“ 完成”按钮。
![工具链选择屏幕](https://i-blog.csdnimg.cn/blog_migrate/2bdbd7e35f96cc1a8705b6a3728d4924.png)
默认情况下未安装Android NDK及其依赖的工具。 因此,生成项目后,您将看到如下错误:
![没有发现NDK错误](https://i-blog.csdnimg.cn/blog_migrate/b48be425795015b6e4870c7b14fa8a01.png)
要解决该错误,请转到工具> Android> SDK Manager,然后切换到“ SDK工具”标签。
在可用的开发人员工具列表中,选择CMake和NDK ,然后按“ 应用”按钮。
![SDK工具对话框](https://i-blog.csdnimg.cn/blog_migrate/8e64678650711157947945aaf2affdb5.png)
安装完成后,重新启动Android Studio。
3.创建本地库
支持C ++的Android Studio项目还有一个名为cpp的附加源代码目录。 您可能已经猜到了,所有C ++文件和库都必须放在其中。 默认情况下,该目录具有一个名为native-lib.cpp的文件。 现在,我们将在其中编写所有C ++代码。
在本教程中,我们将创建一个简单的本机库,其中包含一个使用公式πr²计算圆的面积的函数 。 该函数将圆的半径作为jdouble
,并将面积作为jstring
返回。
首先向文件添加以下include
伪指令:
#include <jni.h>
#include <string>
#include <math.h>
jni.h是一个头文件,其中包含一些宏定义,类型,结构和函数,在使用NDK时,所有这些都是必不可少的。 (JNI表示Java本机接口,这是允许Java Runtime与本机代码进行通信的框架。) 字符串头文件是必需的,因为我们将在库中使用jstring
类型。 math.h头文件包含π的值。
默认情况下,为了支持多态,C ++编译器会修改您在代码中定义的所有函数的名称。 此功能通常称为名称修改。 由于名称修饰,从Java代码调用C ++函数将导致错误。 为避免这些错误,您可以通过在extern "C"
块内定义函数来禁用名称修饰。
extern "C" {
// Your functions must be defined
// here
}
可通过JNI访问的C ++函数的名称必须具有以下格式:
- 它们必须具有Java_前缀。
- 它们必须包含包装名称的错误形式,其中点用下划线替换。
- 它们必须包含它们所属的Java类的名称。
此外,您必须指定功能的可见性。 您可以使用JNIEXPORT
宏来实现。 按照惯例,大多数开发人员还将JNICALL
宏包含在函数定义中,尽管它目前在Android中没有任何作用。
下面的代码定义了一个称为calculateArea
的函数,可以从一个名为MainActivity
的Java类访问该函数:
JNIEXPORT jstring JNICALL
Java_com_tutsplus_mynativeapplication_MainActivity_calculateArea(
JNIEnv *jenv,
jobject self,
jdouble radius
) {
}
请注意,除了半径之外,该函数还接受JNIEnv
类型(具有可用于处理Java类型的实用程序函数)和jobject
实例,该实例是对MainActivity
实例的引用。 当然,在本教程的后面,我们将创建MainActivity
。
计算面积很容易。 您需要做的就是将M_PI
宏乘以radius
的平方。
jdouble area = M_PI * radius * radius;
如此您就知道在使用JNI时如何处理字符串,现在让我们创建一个新字符串,其中包含一条消息,说明该区域是什么。 为此,您可以使用sprintf()
函数。
char output[40];
sprintf(output, "The area is %f sqm", area);
因为Java无法直接处理C ++字符数组,所以函数的返回类型为jstring
。 要将output
数组转换为jstring
对象,必须使用NewStringUTF()
函数。
return jenv->NewStringUTF(output);
至此,我们的C ++代码已准备就绪。
4.使用本机库
在上一步中,您看到了calculateArea()
函数必须属于MainActivity
Java类。 通过右键单击Java包名称并选择File> New> Empty Activity来开始创建类。
在弹出的对话框中,将活动命名为MainActivity 。 确保选中“ 启动器活动”选项后,按“ 完成”按钮。
![创建一个新的启动器活动](https://i-blog.csdnimg.cn/blog_migrate/8e94abf2596efc9eabf50f63bad29e62.png)
必须先加载本机库,然后才能使用它。 因此,向该类添加一个static
块,并使用System
类的loadLibrary()
方法加载该库。
static {
System.loadLibrary("native-lib");
}
为了能够在活动中使用calculateArea()
C ++函数,必须将其声明为本native
方法。
private native String calculateArea(double radius);
现在,您可以像任何普通的Java方法一样使用calculateArea()
方法。 例如,可以将以下代码添加到onCreate()
方法中,以计算和打印半径为5.5的圆的面积:
Log.d(TAG, calculateArea(5.5f));
如果运行该应用程序,则应该能够在logcat窗口中看到以下输出:
![在logcat中输出](https://i-blog.csdnimg.cn/blog_migrate/59c7217621b8b4ceb19ef4beb2c50871.png)
结论
在本教程中,您学习了如何创建本机C ++库并在Android应用程序中使用它。 值得注意的是,默认情况下,本机生成过程会为NDK支持的每个CPU体系结构生成一个单独的.so文件。 因此,您可以确定自己的应用程序可以在大多数Android设备上运行,而不会出现任何问题。
要了解有关Android NDK的更多信息,建议您参考《 NDK指南》 。