Android本地开发工具箱(Native Development Kit,NDK)是Android SDK的辅助工具。如果你使用NDK创建本地代码,则你的应用仍然需要打包到.apk文件中并在设备的虚拟机上运行。基本的Android应用模式没有改变。
设置NDK环境
为了使用NDK,首先必须安装和设置SDK。安装和使用NDK的系统需求如下:
·Windows XP(32位)或Vista(32位或64位),包含Cygwin 1.7版本或更新,Mac OS X 10.4.8版本或更新,Linux(32位或64位)
·GNU Make 3.8.1或更新
·GNU AWK或nawk
首先,下载并安装NDK(http://developer.android.com/sdk/ndk/index.html)。NDK的安装很简单:把NDK解压到任何目录下。这里,我们把它解压到ndk目录。根目录下会包含NDK的版本号。这里,我们把该目录命名为ndk。如果当前可用的版本号比我们在本章使用的版本号更新,你会发现其支持更多的本地API。
一旦下载并安装完NDK,就可以找到很多文档(在ndk/docs目录下)。强烈建议你阅读这些文档,从文件OVERVIEW.html开始。NDK中还提供了一些示例(在ndk/samples目录下)。这些示例涵盖的范围远远超出本章,因此当你对NDK稍有认识后,我们建议你查看并运行这些示例。
在Eclipse中编辑C/C++代码
要充分利用Eclipse来编写C语言代码,需要安装Eclipse C/C++开发工具,即Eclipse CDT。该工具可以在Eclipse环境中提供C语言代码编辑器,和Eclipse的Java编辑功能类似,它也提供语法高亮、格式化以及其他高级功能。
要使用哪个CDT库的版本取决于你的Eclipse版本。对于Eclipse Indigo,在Install New Software对话框中输入库http://download.eclipse.org/tools/cdt/releases/indigo/。关于如何使用Eclipse的更多说明,请参考第5章的为Eclipse添加包以及使用静态分析器两节的内容。
使用NDK编译
为了使用NDK开发本地代码,需要执行如下操作:
1.在项目中创建jni目录。
2.把源代码放到jni目录中。
3.在jni目录下创建Android.mk文件(或者Application.mk文件)。
4.在jni目录下运行ndk/ndk-build命令。
可选的Application.mk文件描述了应用需要什么样的本地模块,以及要编译的具体ABI类型。关于Application.mk的更多信息,可查看文档中的APPLICATION-MK.html文件。Application.mk示例文件如下:
# Build both ARMv5TE and ARMv7-A machine code.APP_ABI := armeabi armeabi-v7a # What platform to build against (android-3 (1.5) - android-9 (2.3))APP_PLATFORM := android-9
Android.mk文件是描述编译系统的源文件。它实质上是一个小的GNU Makefile文件,当编译应用时由编译系统解析它。关于Android.mk的更多信息,可查看文档中的ANDROID-MK.html文件。Android.mk示例文件如下所示:
# Must define the LOCAL_PATH and return the current dirLOCAL_PATH := $(call my-dir) # Cleans various variables... making a clean buildinclude $(CLEAR_VARS) # Identify the module/library/'s nameLOCAL_MODULE := sample # Specify the source filesLOCAL_SRC_FILES := sample.c # Load local libraries (here we load the log library)LOCAL_LDLIBS := -llog # Build the shared library defined aboveinclude $(BUILD_SHARED_LIBRARY)
Android.mk、Application.mk及本地源文件都准备好之后,可以在项目路径下运行ndk/ndk-build来编译你的库文件。只要编译成功,共享库会被复制到应用的根项目路径中,并被添加到应用的构建之中。
如果你下载并查看Android源代码,会发现在Android的编译系统中一直使用类似以上给出的makefile文件。熟悉JNI、本地代码以及本地代码如何在Android中编译的最好方式是自己编写一个使用Android NDK的简单应用。
JNI、NDK和SDK:应用示例
为了帮助你理解SDK和本地源代码具体是如何结合在一起的,我们提供了一个示例应用,其名称为SampleActivityWithNativeMethods,是一个活动。Android的manifest文件片段如下:
<activity android:name=/".SampleActivityWithNativeMethods/" android:label=/"Sample Activity With Native Methods/" android:debuggable=/"true/" />
这个SampleActivityWithNativeMethods示例应用使用的布局如下所示:
<?xml version=/"1.0/" encoding=/"utf-8/"?><LinearLayout xmlns:android=/"http://schemas.android.com/apk/res/android/" android:orientation=/"vertical/" android:layout_ android:layout_ ><Button android:id=/"@+id/whatami/" android:layout_ android:layout_ android:paddingTop=/"5dp/" android:paddingBottom=/"5dp/" android:text=/"What CPU am I?/" /></LinearLayout>
示例C语言库的源代码中实现的是一个名为whatAmI的方法,我们的Java activity会通过whatami ID hook到按钮上。还定义了一个函数,名为LOGINFO,归结为__android_log_print调用。以下是Android log示例:
// the jni library MUST be included#include <jni.h>// the log lib is included#include <android/log.h>// usage of log#define LOGINFO(x...) __android_log_print(ANDROID_LOG_INFO,/"SampleJNI/",x)jstring Java_com_oreilly_demo_android_pa_ndkdemo_SampleActivityWithNativeMethods_whatAmI( JNIEnv* env,jobject thisobject) { LOGINFO(/"SampleJNI/",/"Sample Info Log Output/"); return (*env)->NewStringUTF(env, /"Unknown/");}
示例应用的Android.mk文件如下所示。注意,它会加载log库:
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := sampleLOCAL_SRC_FILES := sample.cLOCAL_LDLIBS := -lloginclude $(BUILD_SHARED_LIBRARY)
最后,是Java activity SampleActivityWithNativeMethods的源代码。该类加载示例库,并声明本地方法whatAmI。当单击按钮时,会调用whatAmI方法,并返回“Unknown”,然后显示字符串“CPU:Unknown”。如果你觉得输出的信息量很低,不要着急,在下一节中会添加CPU信息:
package com.oreilly.demo.android.pa.ndkdemo;import com.oreilly.demo.android.pa.ndkdemo.R;import android.widget.Toast;public class SampleActivityWithNativeMethods extends Activity { static { System.loadLibrary(/"sample/"); // load our sample lib } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.sample); setupview; } public native String whatAmI; // sample lib native method private void setupview { findViewById(R.id.whatami).setOnClickListener( new View.OnClickListener { public void onClick(View v) { String whatami = whatAmI; Toast.makeText(getBaseContext, /"CPU: /"+whatami, Toast.LENGTH_SHORT).show; } }); }