名著阅读 > Android程序设计:第2版 > Android NDK >

Android NDK

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 dir
LOCAL_PATH := $(call my-dir)
  # Cleans various variables... making a clean build
include $(CLEAR_VARS)
  # Identify the module/library\'s name
LOCAL_MODULE := sample
  # Specify the source files
LOCAL_SRC_FILES := sample.c
  # Load local libraries (here we load the log library)
LOCAL_LDLIBS := -llog
  # Build the shared library defined above
include $(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    := sample
LOCAL_SRC_FILES := sample.c
LOCAL_LDLIBS    := -llog
include $(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;
            }
        });
    }