1 / 30

ANDROID 应用开发专题 2 JNI 与 NDK 开发

ANDROID 应用开发专题 2 JNI 与 NDK 开发. 刘健培 北京邮电大学 ljp020993@gmail.com 2013.05. 本次内容. Android 应用程序专题 之 NDK NDK 概念 NDK 开发 流程 JNI 原理 NDK 应用 示例. 参考资料. http://developer.android.com/tools/sdk/ndk/index.html http://groups.google.com/group/android-ndk NDK 安装目录 Docs/ 下的文档 Samples/ 下的示例 代码 JNI 接口规范

zareh
Download Presentation

ANDROID 应用开发专题 2 JNI 与 NDK 开发

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. ANDROID应用开发专题2 JNI与NDK开发 刘健培 北京邮电大学 ljp020993@gmail.com 2013.05

  2. 本次内容 • Android应用程序专题之NDK • NDK概念 • NDK开发流程 • JNI原理 • NDK应用示例

  3. 参考资料 • http://developer.android.com/tools/sdk/ndk/index.html • http://groups.google.com/group/android-ndk • NDK安装目录 • Docs/下的文档 • Samples/下的示例代码 • JNI接口规范 • http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/jniTOC.html

  4. 什么是NDK • http://developer.android.com/tools/sdk/ndk/index.html • Native Development Kit, Google为Android进行本地开发而提供的一个本地开发工具 • 本地工具集合 • 将C/C++编译成本地库的工具,提供相应mk文件隔离CPU、平台、ABI等差异,只需修改.mk文件创建.so • 把本地库嵌入到APK中的工具 • 相对稳定、功能有限的本地API头文件与二进制库文件 • 文档、示例程序与教程 • 本地API接口标准 • libc (C library) headers • libm (math library) headers • JNI interface headers • libz (Zlib compression) headers • liblog (Android logging) header • A Minimal set of headers for C++ support • OpenSL ES native audio libraries • Android native application APIS • OpenGL ES 1.1 and OpenGL ES 2.0 (3D graphics libraries,NDK r3) headers • libjnigraphics (Pixel buffer access) header (for Android 2.2 and above, ,NDK r4). • 完整API列表见NDK下载包的docs/ STABLE-APIS.html • 如使用NDK不支持的系统库/函数,Android平台升级时,所引用个的库/函数可能被删除、更新 • 定位:配合、补充SDK的一个工具 SDK NDK 编译插入 打包发布 Apk 应用主模块dex(Java) 本地库 so(C/C++) JNI 资源文件 arsc … 配置文件XML

  5. 什么是NDK • NDK 发布之前,Android不支持进行C/C++开发? • Android SDK(Java-Dalvik-jni-C/C++) • 如何编译so? • 如何将so打包进apk? • Android源代码开发 • 有了NDK,我们可以使用纯C开发Android 应用? • it is not a good way! • 缺少框架支持:事件处理、生命周期维护、UI等 • Native Activity

  6. 何时使用NDK • 优势 • 代码重用 • 利用现有C/C++代码,如开源项目、游戏代码 • 提高性能 • 执行计算密集的操作,如复杂的数学、物理、图形、音视频计算 • 硬件控制 • 硬件控制代码通常使用C/C++编写,再借助JNI与Java联系起来 • 应用安全 • Java编译后的dex文件容易遭到逆向破解,so保密性更好些 • 代价 • 增加代码编写、构建、调试的复杂性,开发难度大 • 缺乏Android应用框架的支持,需同时使用Java与C/C++ • 兼容性较Java差 • NDK生成的原生库只能运行于Android1.5及以上的设备上

  7. NDK应用开发流程 • 安装NDK开发环境 • 程序开发 • 使用JNI进行接口映射( Java <- -> C/C++) • 使用NativeActivity构建纯粹本地应用(C/C++) • 移植现有C/C++代码 • 测试调试 • 打包发布

  8. 搭建Android NDK开发环境 • 系统与软件需求 • Android SDK • 需要完整安装Android SDK(NDK没有用于生成apk包的工具) • Android 1.5 SDK及以上版本 • 支持的操作系统 • Windows XP (32-bit) or Vista (32- or 64-bit) • Mac OS X 10.4.8 or later (x86 only) • Linux (32 or 64-bit; Ubuntu 8.04, or other Linux distributions using GLibc 2.7 or later) • 需要的开发工具 • GNU Make 3.81或以上版本(NDK r8e已自带) • 最新版awk (GNU Awk或Nawk) (NDK r8e已自带) • Cygwin 1.7 或更高版本(仅限于Windows)(http://www.cygwin.com/) • Windows安装流程 • 可以直接下载Android NDK解压缩。 • http://developer.android.com/tools/sdk/ndk/index.html) • 为便于在命令行下编译本地代码,也可先安装cygwin(http://cygwin.com/setup.exe), 再下载Android NDK,然后将NDK的根目录添加到windows的Path环境变量中。 • 示例 • 安装Cygwin • 安装Android NDK • 整合Eclipse与NDK

  9. 第三方NDK开发工具 • vs-android • http://code.google.com/p/vs-android/ • VisualGDB • http://visualgdb.com/?features=android

  10. NDK组成结构 Docs 文档 Samples 示例代码 你的源文件 Platform NDK头文件+库 Sources 第三方源代码,如STL Prebuilt Wak、make等工具 Toolchains 交叉编译工具 ndk-build 主编译程序 Build 编译脚本 Tests NDK测试 So本地库 Java文件 资源文件 配置文件 SDK工具 APK文件 ndk-stack 打印调用栈 ndk-gdb gdb调试

  11. NDK帮助文档 • 几个重要文档 • documentation.html – NDK的概述

  12. NDK示例代码 • bitmap-plasma - 如何在NDK中使用bitmap的例子,早期的NDK版本不能直接使用bitmap,后来的版本中增加了对bitmap的支持 • hello-gl2 - 在NDK中如何使用OpenGLES的运用 • hello-jni- 最基本的NDK使用方式,通过NDK获取字符串然后在Android应用中显示出来 • hello-neon - 在NDK中有关neon的优化 • module-exports - 多个库的调用方式。foo被编译为静态库,bar被编译为动态库并调用了库foo,zoo被编译为动态库并调用了库bar。 • native-activity - 完全用NDK实现整个Android程序 • native-audio - 在NDK中有关音频的操作 • native-media - 在NDK中对视频的操作 • native-plasma - 完全用NDK实现整个Android程序并且提供了涉及plasma的优化 • san-angeles - 移植到Android平台的OpenGL ES的例子 • test-libstdc++ - 对C++的支持,但并非支持C++的全部特征 • two-libs- 两个库的使用,first为静态库,second为动态库,并且second库调用first库

  13. 第一个NDK程序(示例) • 建立包含本地代码的应用程序的工程目录 • 使用NDK编译该工程,生成本地库so • 使用SDK建立工程,生成应用程序APK

  14. Android.mk #一个Android.mk 文件从定义LOCAL_PATH 变量开始, 'my-dir'宏的功能由编译器提供,被用来返回当前目录的地址 LOCAL_PATH := $(call my-dir) #CLEAR_VARS 这个变量指明了一个GNU makefile文件,这个功能会清理掉除LOCAL_PATH之外所有的LOCAL_XXX,这句是必须的,保证变量的局部性 include$(CLEAR_VARS) #LOCAL_MODULE 变量必须被定义,用来区分Android.mk中的每一个模块。文件名必须是唯一的,不能有空格。最后会生成是'libhello-jni.so' LOCAL_MODULE := hello-jni #LOCAL_SRC_FILES 变量必须包含一个C 和C++源文件的列表,这些会被编译并聚合到一个模块中。头文件和被包含的文件并不需要列出,因为编译系统会自动计算相关的属性 LOCAL_SRC_FILES := hello-jni.c #BUILD_SHARED_LIBRARY收集从'include $(CLEAR_VARS)'开始的LOCAL_XXX 变量,并且决定哪些要被编译进行动态库。BUILD_STATIC_LIBRARY用于生成动态库 include$(BUILD_SHARED_LIBRARY)

  15. 跨语言互操作问题 • 2个问题 • 语法层次如何相互理解 • API、静态、形式 • 语义层次如何相互理解 • ABI、动态、内容 • 2种类型 • 使用第三方统一标准 • Socket、管道、RPC等 • Web Service等 • COM、COBRA等分布式组件模型 • .NET的CLS通用语言规范 • 其中一门语言建立标准 • 编译型 -> 编译型 • 编译型 -> 解释型 • 解释型 -> 解释型 • 解释型 -> 编译型

  16. JNI • JNI是本地编程接口(Java Native Interface) 。它使得在 Java 虚拟机 (VM) 内部运行的 Java 代码能够与用其它编程语言(如 C、C++ 和汇编语言)编写的应用程序和库进行互操作。 • 可以使用JNI从Java的程序中调用Native代码。 • 可以从Native程序中调用Java代码。 • 在Java代码和Native代码中需要按照固定的格式告诉JNI如何调用对方。 • 在Android Framework中,JNI是联系上层Java与底层C/C++的桥梁。 • Android中编写JNI的2种方式 • NDK • Android源代码

  17. Java -> C/C++ • 六步: • 编写Java代码,在Java类中声明Native方法 • native void funcName() • static {System.loadLibrary(“libName”)} • 编译Java代码,调用Native方法 • javac fileName.java • 生成C语言头文件,用javah生成包含JNI Native函数原型的头文件 • Javah -jni <包含以native声明方法的Java类名称> • javahclassName-> className.h • 生成的接口函数原型:Java_类名_本地方法名 • JNIEXPORT jint JNICALL Java_class_method(JNIEnv *env, jobjectobj); • 编写C/C++代码,实现JNI的Native函数 • JNIEXPORT jint JNICALL Java_class_method(JNIEnv *env, jobjectobj){…} • 生成C/C++共享库 • 运行Java程序,通过JNI调用Native函数

  18. Java- C/C++函数映射方法 • 2种方式 • 静态命名匹配 • 动态指针加载

  19. Java- C/C++函数映射方法 • 静态命名匹配 • 假设在com.android.xxx package中有个文件class.java,内有函数method,那么执行javah -jniom.android.xxx.test,会生成头文件com_android_xxx_class.h,内有函数java_com_android_xxx_class_method。这个名字就包括了该函数所对应Java版本所在的包、文件以及名称。 • 具体调用过程: • 程序运行时,调用System.loadLibrary()方法,将Native方法具体实现的C/C++运行库加载到内存中。 • Java虚拟机检索加载进来的库函数符号,在其中查找与Java Native方法拥有相同签名的JNI Native函数符号,若找到一致的,则将本地方法映射到具体的JNI Native函数。

  20. Java- C/C++函数映射方法 • 动态指针加载 • 静态命名匹配的方式是通过在加载运行库时,查找匹配的函数名称(字符串比对)来实现的,效率较低,且无法在运行时更改函数实现,不够灵活。 • 动态指针加载方式是通过在加载运行库时,直接提供一个映射表将JNINative函数与Java Native方法链接在一起实现的,效率更高,且可以在运行时更改映射表内容,从而达到弹性更换Native函数实现的目的。 • 具体实现过程: • 在Java代码中,调用System.loadLibrary()方法时,Java虚拟机会检索共享库的函数符号,检查JNI_OnLoad这个默认函数是否实现,是则调用JNI_OnLoad函数,否则继续选择函数命名匹配的方式。 • 所以程序员可以在JNI_OnLoad函数中调用RegisterNatives()将映射表注册到Java虚拟机。 • 示例

  21. C/C++ -> Java • 2种方式 • JNI Native函数调用Java端的代码 • 使用JNI函数(JNI规范标准接口) • C程序中直接嵌入Java虚拟机 • 使用Invocation API

  22. JNI函数 • Native代码通过调用 JNI 函数来访问 Java 虚拟机功能 • JNI 函数可通过接口指针(JNIEnv*)来获得。接口指针是指针的指针,它指向一个指针数组,而指针数组中的每个元素又指向一个接口函数。每个接口函数都处在数组的某个预定偏移量中。

  23. JNI函数 • 常用操作 • 创建Java对象 • 访问类静态成员域 • 调用类的静态方法 • 访问Java对象的成员变量 • 访问Java对象的方法 JNIEXPORT void JNICALL Java_Callbacks_nativeMethod(JNIEnv*env,jobjectobj,jint depth) { jclasscls=(*env)->GetObjectClass(env,obj); jmethodID mid =(*env)->GetMethodID(env,cls,"callback","(I)V"); if(mid !=0) (*env)->CallVoidMethod(env,obj, mid, depth); }

  24. Invocation API • JNI提供了一套Invocation API,它允许Native代码在自身内存区域内嵌入Java虚拟机,装载Java类,调用指定的方法。 • 厂商可以交付支持 Java 的应用程序,而不必链接 Java 虚拟机源代码。 • C/C++要调用JAVA 程序,必须先加载JAVA 虚拟机,由JAVA 虚拟机解释执行class文件。为了初始化JAVA 虚拟机,JNI 提供了一系列的接口函数,通过这些函数方便地加载虚拟机到内存中。 • Invocation API 允许本地应用程序用 JNI 接口指针来访问虚拟机特性。

  25. #include <jni.h> /* 其中定义了所有的事项 */ ... JavaVM*jvm;/* 表示 Java 虚拟机*/ JNIEnv*env;/* 指向本地方法接口的指针 */ JDK1_1InitArgs vm_args;/* JDK 1.1 虚拟机初始化参数 */ vm_args.version=0x00010001;/* 1.1.2 中新增的:虚拟机版本 */ /* 获得缺省的初始化参数并且设置类路径 */ JNI_GetDefaultJavaVMInitArgs(&vm_args); vm_args.classpath=...; /* 加载并初始化 Java 虚拟机,返回 env中的JNI 接口指针 */ JNI_CreateJavaVM(&jvm,&env,&vm_args); /* 用 JNI 调用 Main.test方法 */ jclasscls=env->FindClass("Main"); jmethodID mid =env->GetStaticMethodID(cls,"test","(I)V"); env->CallStaticVoidMethod(cls, mid,100); /* 结束。*/ jvm->DestroyJavaVM();

  26. NDK应用示例 • 建立工程 • 编写代码 • 编译程序 • 运行程序 • 调试程序

  27. 调试 • Eclipse+CDT+gdb • Cygwin+ant+adb • __android_log_print #include <android/log.h> int __android_log_print(intprio,constchar*tag,constchar*fmt,...) LOCAL_LDLIBS :=-llog

  28. Native Activity • Native Activity是Android 2.3中正式发布的功能。 • 可以在没有Java代码的情况下,完全由本地代码生成应用,需要进行显示和事件处理方面的工作。 • Android.app包中的NativeActivity类方便开发者实现一个使用纯粹本地代码所实现的activity。但本地代码并不需要继承这个类,只需在AndroidManifest.xml中引用它,并调用NDK的API即可。 • 编写Native Activity需要的API头文件在<ndk目录>\platforms\android-xx\arch-arm\usr\include\android下 • <ndk目录>\sources\android\native_app_glue\是一个编写Native Activity的辅助库,帮助显示和事件处理,使用该库的Native Activity需要使用android_main()函数作为入口。 • android的ndk在<ndk目录>\samples\目录下有2个Native Activity的例子: • native-activity:在本地构建的应用,使用OpenGLES构建截面 • native-plasma:在本地构建的应用,使用本地访问位图构建界面

  29. NDK vs. RenderScript • Android平台为应用程序在传统的Android应用边界外面运行提供了两种方法:NDK、RenderScript。 • Renderscript(RS)是Android3.0引入的一组提供高性能的3D图形渲染和密集型计算的API(C99标准)。 • 编程语言和可移植性 • NDK可以利用现有的C/C++库。 • RenderScript使用C99语法,最终编译成原生代码。RenderScript无法从其他C应用程序移植过来,不过它在Android设备上比NDK更为常见(Google TV) 。 • 编译和调试 • 用NDK编写的代码必须事先针对每一个目标原生平台来编译。采用NDK的应用程序可以使用gdb进行行级调试。 • RenderScript在开发机器上进行第一遍编译,然后在目标设备上进行最后一遍编译,因而带来了更高效的原生二进制代码。RenderScript应用程序在运行时无法调试。 • 性能 • NDK和RenderScript都未能在性能方面提供完美方案。两者都增加了项目的复杂性,降低了可移植性,提高了测试需求,加大了调试难度,还给项目增加了维护负担。 • 如果纯粹是用于计算,RenderScript的设置和配置很容易,最终的运行速度实际上可能胜过使用NDK的类似实现方法,需要编写的代码比较少。 • NDK比较适合高性能OpenGL应用程序或需要访问图形软件开发工具包(SDK)更多功能或访问第三方库的游戏。

  30. 谢 谢!

More Related