@cxm-2016
2016-11-21T12:07:22.000000Z
字数 3472
阅读 4644
OpenGL-ES
版本:1
作者:陈小默
声明:禁止商业,禁止转载
本篇内容介绍NDK环境的基本搭建和最简单的NDK示例。示例工程发布到GitHub-NDK。在开始本系列之前,请确保已经了解JNI的相关知识,如果没有,可以到这里学习JNI完全指南系列。水平有限,如有错误,恳请批评指正。
Android Studio在其2.2及以上版本继承了流行的C++编译器CMake,于是我们很方便的就能够搭建出NDK开发环境。
在Android Studio 2.2 及以上版本创建NDK的工程只需要勾选Include C++ Support即可。
在工程创建完成之后,我们将目录视图切换到Project,我们发现app目录下多了一个CMakeList.txt文件,这个文件的作用是指引CMake编译器如何去编译我们的源文件,打开文件我们会看见如下内容(注释已精简)。
# 这里指定了CMake的最低版本为3.4.1
cmake_minimum_required(VERSION 3.4.1)
# 这里用来添加一个库
add_library( # 这里设置so库的名称为native-lib
native-lib
# 这里设置该库为共享
SHARED
# 这里是源文件的路径,可以有多个,最终这个源文件将被编译并打包进native-lib库。
src/main/cpp/native-lib.cpp )
# 这里用来查找一个库,并设置到路径变量中去
find_library( # 设置路径变量
log-lib
# 你希望CMake编译器加载的NDK函数库
log )
# 将一个库关联到目标函数库中
target_link_libraries( # 目标函数库
native-lib
# 想要在目标函数库中使用的函数库,其中${路径变量}
${log-lib} )
更详细的CMakeList.txt可以参照这里CMakeList.txt说明。
目前kotlin语言的版本是1.0.4。搭建过程详见在Android Studio中配置Kotlin开发环境。
本章内容为调用JNI方法,并返回一段字符串。
在{$package}/lib包下创建一个Java文件HelloJniLib.java
,其中的内容为
public class HelloJniLib {
public static native String helloJNI();
}
当我们声明了一个native方法后,需要在一个源文件中实现业务逻辑,接下来,我们在cpp文件夹下创建一个hello-jni.cpp
,并在其中实现声明的本地方法:
#include <jni.h>
extern "C"
JNIEXPORT jstring JNICALL
Java_com_github_cccxm_ndk_lib_HelloJniLib_helloJNI(JNIEnv *env,
jobject thiz) {
#if defined(__arm__)
#if defined(__ARM_ARCH_7A__)
#if defined(__ARM_NEON__)
#if defined(__ARM_PCS_VFP)
#define ABI "armeabi-v7a/NEON (hard-float)"
#else
#define ABI "armeabi-v7a/NEON"
#endif
#else
#if defined(__ARM_PCS_VFP)
#define ABI "armeabi-v7a (hard-float)"
#else
#define ABI "armeabi-v7a"
#endif
#endif
#else
#define ABI "armeabi"
#endif
#elif defined(__i386__)
#define ABI "x86"
#elif defined(__x86_64__)
#define ABI "x86_64"
#elif defined(__mips64) /* mips64el-* toolchain defines __mips__ too */
#define ABI "mips64"
#elif defined(__mips__)
#define ABI "mips"
#elif defined(__aarch64__)
#define ABI "arm64-v8a"
#else
#define ABI "unknown"
#endif
return env->NewStringUTF("Hello from JNI ! Compiled with ABI " ABI ".");
}
在这里我们需要指定生成的so库的名字,还有需要被编联到这个库的源文件的位置,注意,这里的源文件路径是相对于CMakeList.txt的路径。这里我们指定库名为ndk-lib。
elseif (${ANDROID_PLATFORM_LEVEL} LESS 18)
add_definitions("-DDYNAMIC_ES3")
set(OPENGL_LIB GLESv2)
else ()
set(OPENGL_LIB GLESv3)
endif (${ANDROID_PLATFORM_LEVEL} LESS 11)
add_library(ndk-lib SHARED
src/main/cpp/hello-jni.cpp)
target_link_libraries(ndk-lib
android
log
m)
接下来我们需要一个用来显示结果的ActivityHelloJniActivity
,其中包含一个TextView
<TextView
android:text="TextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/tv_hello_jni" />
class HelloJniActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_hello_jni)
tv_hello_jni.text = HelloJniLib.helloJNI()
}
}
在使用本地方法之前,我们需要通知JVM加载本地库,由于本地库仅需要加载一次,而且必须在使用native方法之前,所以这里可以将加载过程放置到Application类中,这样程序启动时就会自动链接本地库。
class NDKApplication : Application() {
init {
System.loadLibrary("ndk-lib")
}
}
到此为止,我们的第一个程序就完成了,如果运行以上程序,我们就能在Activity上看到JNI方法返回的数据。现在我们总结一下一个NDK程序的开发过程:
extern "C"
。add_library
中将源文件添加进相应的库中,有必要的话,再在target_link_libraries
中将库与其他相关源码进行关联。System.loadLibrary
是程序与本地库关联。