浏览人次:【13826】
什么是Android NDK?
每个Android应用程式都是透过Dalvik虚拟机器执行,由虚拟机器来负责程式所需的资源管理,就像Java程式是透过Java虚拟机器执行一样。 Dalvik对于Android应用程式使用原生程式码的概念也学习自Java。 Dalvik实作了标准的JNI(Java Native Interface,Java原生介面,Java语言用来与C或C++等原生程式码沟通的介面)功能,使Android应用程式中的Java类别能顺利的与原生函式库沟通。这些函式库可能是原本已内建于Android系统中的核心函式库(如:libc、WebKit、SQLite等),或是由开发者自行撰写并编译完成的函式库。 Android应用程式透过Dalvik虚拟机器的JNI功能与原生函式库沟通的方式,如图一所示:
《图一 Android应用程式透过Dalvik虚拟机器中的JNI功能呼叫由原生程式码实作的使用者自订函式库或内建核心函式库。 》
![](/art/2010/03/181355035143/p1.gif) |
事实上,各地的开发者也对Android系统的内部架构相当感兴趣;一部份开发者希望能藉由与原生函式库的沟通来再利用原本已存在的原生程式函式库,例如:将写好的游戏图像或资料处理引擎移植到Android平台;另一部份的开发者则希望能透过Dalvik的JNI功能来使得他们自己的Android应用程式能直接调用Android系统中的核心函式库来达到较佳的程式效能。也有许多人提出并建议了如何在Android应用程式中协作Java程式码与原生函式库的方法,然而这些方法大多相当繁杂且多出于推论。 NDK的出现,就是Google希望为开发者为这类型的开发工作订出一套标准化的工具与模式。
因此,Android NDK本身是用来辅助在开发有需要在Android应用程式中放入原生语言函式库时SDK(Software Development Kit)的不足,而不是必须有了此套件才能开发与原生语言函式库协同运作的Android应用程式。
Google方面则强调,Android平台在设计之初就已经将Java类别与Dalvik虚拟机器间可能的效能瓶颈给考虑进去,两方面间的沟通介面已经过许多改进而有不差的效能,诸如Dalvik使用的改良过的.dex档而非传统.class档等;因此使用原生语言撰写在大部分的状况下并不会提升应用程式的效能。由于Android平台整个开发框架的限制,Android应用程式必须使用Activity类别作为程式的进入点,这使得开发者无法撰写百分之百由原生语言构成的Android应用程式,盲目的使用原生语言反而可能造成程式结构杂乱,且可维护性降低等缺点。
Android NDK包含了什么?
NDK主要提供了以下工具:
●一组可协助开发者在Linux、OS X、Windows等平台上生成原生语言函式库的工具链(toolchains),其中包含专用的编译器、连结器等等。
●目前Android系统平台所提供的,可供外部程式呼叫的核心函式库API(Application Programming Interface,应用程式开发介面)。
●一套可以统整并处理单一格式建置档案(make file)的建置系统(build system)。
如同前述所言,Google希望能统一各平台上开发具有原生程式码之Android应用程式的方式。所以提出了具有特定格式的建置档供开发者使用;其一是用来叙述单一个函式库之结构与内容的Android.mk设定档,第二是用来叙述应用程式中所需原生程式码函式库状况的Application.mk设定档;这两类设定档所面对的都是Android NDK所提供的建置系统,因此同样的一份专案与设定档不管移植到哪个平台都不需要再因为使用了不同的建置环境而再做变动。
值得注意的是,Google非常明白有许多的开发者都希望能不要透过撰写Java类别而直接使用Android的内建函式库来实作他们所需的功能。而这些内建函式库也可以透过追踪Android系统已释出的原始码来得知其使用方式。但由于Android系统内部的各项开发与改进工作仍然持续在进行中,因此NDK的文件中有特别说明,大部分的系统函式库在未来的版本中都还有变动的可能,例如更名或删除,因此强烈不建议开发者使用NDK中所没有提及的函式库API。
而目前NDK中所列出的系统函式库在未来就不会再多做变动,Google也承诺会在未来的版本中持续增加可供使用的函式库(例如:Android 1.6 NDK相较于1.5 NDK就新增了OpenGL ES函式库;而Android NDK Project Page上,相关工作人员也承诺会在近期的版本中增加更多对于多媒体的函式库支援)。
Android NDK使用实例
在使用Android NDK之前必须先确定系统中已有安装对应版本的SDK,以及GNU Make工具3.81或更新的版本。在下载好Android NDK套件后,必须解压缩并安装NDK,安装动作是为了确保原生程式码的编译环境不会出错,并针对开发平台做些许微调。
使用NDK开发使用自行撰写之原生程式码的应用程式约分为以下四步骤:
1. 于将使用到原生语言函式的Java类别中以native关键字宣告该方法。并以System.loadLibrary()方法明确的要求Dalvik虚拟机器于执行时期载入该函式库。
2. 撰写该方法于原生语言的实作(此时称为函式)。注意此实作必须遵从JNI的相关规范;例如必须使用JNI所定义的资料型态,以及函式的命名规则等。
3. 为每个原生函式库撰写Android.mk设定档,由于Dalvik虚拟机器遵从Solaris平台的JNI函式库命名规则,所以函式库档案必须以”lib”开头,”.so”作为副档名。
4. 为整个应用程式专案撰写Application.mk设定档。
5. 执行make指令进行原生程式码的编译与连结动作,Android NDK的建置系统会将编译好的函式库放到专案中适合的位置。
透过以下范例我们将可以知道Android对原生函式的呼叫可以是双向的;可自Java类别中呼叫原生函式,也可自原生函式中呼叫类别方法。图三的JNIDemo类别首先以静态初始化(static initializer)的方式载入libjnidemo.so函式库,其中有原生方法nativeMethod()的实作内容(见图四),并在萤幕上绘制一个按钮。当按钮按下时,Dalvik虚拟机器会呼叫以C语言实作的nativeMethod()函式,nativeMethod()则以标准的JNI方式转而呼叫JNIDemo类别中的callback()方法,令萤幕显示”Method callback () has been invoked.”字样。
《图二 JNI Demo专案的目录结构》 ![](/art/2010/03/181355035143/p2.gif) |
此范例完整的专案目录结构则如图二。我们可以看到,除了在一般Android专案中可以见到的src、gen、res目录外,jni目录则用来放置以原生语言撰写的原始码档案与其Android.mk设定档。在经过Android NDK建置过后,建置系统会将编译好的函式库档案(在此例中为libjnidemo.so档)放在/libs/armeabi/目錄下。"/libs/armeabi/目录下。这些档案与目录应在需要安装时包装为apk档,并以一般安装方式载入并安装在Android平台上。
《图三 JNI Demo类别》
![](/art/2010/03/181355035143/p3.gif) |
《图四 jni demo.c档案内容》
![](/art/2010/03/181355035143/p4.gif) |
Android NDK目前的缺点
目前Android NDK最为人诟病的是提供的系统函式库API数量实在太少(仅有C/C++语言的部份标准函式库、Android Log功能函式库、与ZLib函式库)。除了1.6版新提供的OpenGL ES函式库,对于需要绘制3D画面的开发者有较实质的帮助外,对一般希望能藉由调用Android系统函式库来提升应用程式效能的开发者来说,帮助实在非常渺小。也因此,NDK的释出对许多已经开发者而言,只不过提供了一个工具来协助他们连结应用程式与想要使用的原生函式库;对于系统函式库的存取,大部分对Android原始码已有相当认识的开发者,大多会选择无视Google的警告,持续使用他们想要使用的系统函式库。例如,开发者可能会需要加速应用程式上图像的绘制,而进一步想直接操控Android系统中负责图片绘制的Skia绘图引擎,此需求是必要且急迫的,那就只能硬着头皮使用,而以版本控制的方式来避免未来该函式库更名或移除的危机了。
另一点,则是关于原生程式码部分除错的难度。在一般开发C或C++程式时,开发者们通常会选择使用IDE所附的除错器,或以类似gdb的除错工具来协助除错,例如设定中断点、查看变数内容等。 Google在针对Android应用程式的开发上也有提供LogCat工具来协助开发者查看程式运行状况。但对于要使用在Android应用程式内部的原生语言函式库的开发就没有以上工具的协助了,这使得原生语言部分的开发相当困难,除错效率也相当低落。开发者目前几乎必须先将原生语言的部分先加以抽离开来实作,同时进行传统的除错工作,待功能性无误后,再代换入必须与Android应用程式之Java类别相接的部分来完成开发工作,而无法两造同步进行开发。
Google自第一版NDK释出后就不断的在相关文件与网页上宣称在未来版本的NDK中会加入为原生程式码设计的除错工具。此工具将会透过gdb连线的方式,给予开发者尽可能多的资讯,且在不更动目前所有原始码与设定档的前提下协助开发者进行除错的工作。不过截至目前为止各方尚没有相关的讯息出现。
笔者截稿当晚Google恰好发布Android NDK第3版,此版本主要是为了对应Android 2.0平台上OpengGL ES 2.0函式库所做的更新。并更换所使用的编译器版本,以及修改工具中的些许臭虫。此版本没有开放新的系统函式库,对于除错器也没有进一步的消息。
结语
C与C++等原生语言对于Android应用程式的开发目前限制甚多,Google官方也屡屡强调原生语言仅适合实作于应用程式的某些部份。因此将应用程式的主要逻辑以Java类别形式撰写,提升开发效率与程式结构,有需要使用极高效率的运算处理时才使用原生语言或系统函式库,这才是整个Android平台开发框架的立意所在。
---本文作者任职于资策会网路多媒体研究所---
注1.Google释出的第三版Android NDK将名称由类似「Android 1.6 NDK, Release 1(释出1版)」的形式改为「Android NDK, Revision 3(修订3版)」,此项更动是由于有很多开发者被之前1.6的版本名称所误导,误以为Android 1.6 NDK所生成的应用程式只能执行在1.6版本或以上的Android平台。未来NDK的版本都会以「Android NDK, Revision x」的形式作为名称,代表发布的版本数,而支援的平台版本则另作说明。
|