@cxm-2016
2016-11-21T04:07:22.000000Z
字数 3472
阅读 5481
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.1cmake_minimum_required(VERSION 3.4.1)# 这里用来添加一个库add_library( # 这里设置so库的名称为native-libnative-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 JNICALLJava_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"#endifreturn 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 SHAREDsrc/main/cpp/hello-jni.cpp)target_link_libraries(ndk-libandroidlogm)
接下来我们需要一个用来显示结果的ActivityHelloJniActivity,其中包含一个TextView
<TextViewandroid: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是程序与本地库关联。