Android NDK开发环境搭建,请见:http://patmusing.blog.163.com/blog/static/135834960201111311022896/
Step1: 创建一个Android项目

图1

图2
点击Finish,项目就创建好了。
Step2: 创建一个Java类,规定Native动态库的名称,以及其中需要实现的方法。
如下图选中NDK02/src/com.pnf.ndk/New/Class,

图3
指定类名为NativeLibrary,

图4
编辑NativeLibrary.java,

图5
NativeLibrary.java的内容如下:
package com.pnf.ndk;
public class NativeLibrary
{
// 装载动态库ndk02
static
{
System.loadLibrary("ndk02");
}
// 将两个整数相加,返回它们的和
public native int add( int x, int y );
// 将两个整数相减,返回它们的差
public native int min( int x, int y );
// 将两个整数相乘,返回它们的积
public native int mul(int x, int y);
// 将两个整数相除,返回它们的商
public native int divide(int x, int y);
}
System.loadLibrary("ndk02");中的动态库ndk02,可以是Windows平台上的ndk02.dll,也可以是Unix/Linux平台上的ndk02.so,在JNI中,loadLibrary的参数只是动态库的,不包括后缀的文件名。
Step3: 创建C头文件
启动Cygwin,进入到项目的bin子目录,用javah –jni com.pnf.ndk.NativeLibrary命令,将在bin目录下,会根据NativeLibrary.java生成一个C语言头文件 - com_pnf_ndk_NativeLibrary.h

图06
在Cygwin模拟的Unix环境中,Windows系统中的所有目录,都被映射在/cygdrive目录之下,比如Windows中的E盘根目录,在Cygwin中,就是/cygdrive/e。我们的项目在Eclipse的Workspace(目录E:\Patrick\Workspace\Android)中,项目名称是NDK02,因此该项目的下的bin子目录,就是E:\Patrick\Workspace\Android\NDK02\bin。
在文件浏览器中,我们可以如下图看到新生成的com_pnf_ndk_NativeLibrary.h:

图07
这个头文件的名字,实际上就是Nativelibrary类的全名决定的。NativeLibrary类的全名是com.pnf.ndk.NativeLibrary,而生成的C语言头文件的名称为com_pnf_ndk_NativeLibrary.h。
com_pnf_ndk_NativeLibrary.h文件的内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_pnf_ndk_NativeLibrary */
#ifndef _Included_com_pnf_ndk_NativeLibrary
#define _Included_com_pnf_ndk_NativeLibrary
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_pnf_ndk_NativeLibrary
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_pnf_ndk_NativeLibrary_add
(JNIEnv *, jobject, jint, jint);
/*
* Class: com_pnf_ndk_NativeLibrary
* Method: min
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_pnf_ndk_NativeLibrary_min
(JNIEnv *, jobject, jint, jint);
/*
* Class: com_pnf_ndk_NativeLibrary
* Method: mul
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_pnf_ndk_NativeLibrary_mul
(JNIEnv *, jobject, jint, jint);
/*
* Class: com_pnf_ndk_NativeLibrary
* Method: divide
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_pnf_ndk_NativeLibrary_divide
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
正如第一行注释说的那样,不要直接编辑这个自动生成的文件。
在Eclispe环境中,NDK02项目根节点下,创建一个文件夹jni,

图08

图09
jni文件夹所在的位置如下图所示:

图10
现在将生成的头文件com_pnf_ndk_NativeLibrary.h,从E:\Patrick\Workspace\Android\NDK02\bin目录,直接拖到Eclipse中的jni文件夹下面。

图11
完成后,jni文件夹下面就有com_pnf_ndk_NativeLibrary.h了,

图12
Step4: 编写C代码
在jni文件夹里面创建一个文件ndk02.c,

图13

图14
我们将在ndk02.c里面,实现com_pnf_ndk_NativeLibrary.h头文件中声明的4个方法,其具体内容如下:
#include "com_pnf_ndk_NativeLibrary.h"
JNIEXPORT jint JNICALL Java_com_pnf_ndk_NativeLibrary_add
(JNIEnv * env, jobject obj, jint value1, jint value2)
{
return (value1 + value2);
}
JNIEXPORT jint JNICALL Java_com_pnf_ndk_NativeLibrary_min
(JNIEnv * env, jobject obj, jint value1, jint value2)
{
return (value1 - value2);
}
JNIEXPORT jint JNICALL Java_com_pnf_ndk_NativeLibrary_mul
(JNIEnv * env, jobject obj, jint value1, jint value2)
{
return (value1 * value2);
}
JNIEXPORT jint JNICALL Java_com_pnf_ndk_NativeLibrary_divide
(JNIEnv * env, jobject obj, jint value1, jint value2)
{
return (value1 / value2);
}
Step5: 编写make文件 - Android.mk
在jni文件夹里面创建一个文件Android.mk,

图15

图16
编辑Android.mk,是其内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Here we give our module name and source file(s)
LOCAL_MODULE := ndk02
LOCAL_SRC_FILES := ndk02.c
include $(BUILD_SHARED_LIBRARY)
注意:
1. LOCAL_MODULE := ndk02中的“ndk02”,指定了将要生成动态库(ndk02.so)的名称,它必须要和NativeLibrary类中的System.loadLibrary("ndk02");里面的“ndk02”保持一致。
2. LOCAL_SRC_FILES := ndk02.c中的“ndk02.c”必须和Step4中的C源文件的文件名保持一致。

图17
Step6: Build动态库
在Cygwin控制台中,进入NDK02项目所在的目录,然后调用ndk-build命令。操作过程如下:

图18
注意:ndk安装在D:\android-ndk-r5b,其中有个工具ndk-build,就是专门用来根据Android.mk指定的内容,build动态库的。
如上图所示,生成了一个动态库libndk02.so。在Unix/Linux环境下库文件前面都会有“lib”,它表示这个文件是一个库文件,在代码中使用它时,必须忽略这三个字母,就像在NativeLibrary类中所写的那样。
在Eclipse中,选中项目根节点NDK02,再按F5键刷新NDK02项目,

图19
如上图,我们将看到在出现了一个新文件夹libs,其下有一个文件 – libndk02.so。
Step7: 在NDK02Activity中,调用libndk02.so中的Native代码
首先准备NDK02Activity的界面。

图20
完善NDK02/res/layout下面的main.xml,使其内容如下:
<?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<EditText android:id="@+id/ed_operand1"
android:layout_width="83px"
android:layout_height="wrap_content"
android:text=""
android:textSize="18sp"
android:layout_x="6px"
android:layout_y="12px"
>
</EditText>
<Button android:id="@+id/btn_operator"
android:layout_width="36px"
android:layout_height="36px"
android:text="+"
android:layout_x="90px"
android:layout_y="16px"
>
</Button>
<EditText android:id="@+id/ed_operand2"
android:layout_width="83px"
android:layout_height="wrap_content"
android:text=""
android:textSize="18sp"
android:layout_x="127px"
android:layout_y="12px"
>
</EditText>
<Button android:id="@+id/btn_calculate"
android:layout_width="50px"
android:layout_height="wrap_content"
android:text="="
android:layout_x="215px"
android:layout_y="12px"
>
</Button>
<TextView android:id="@+id/tv_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:layout_x="7px"
android:layout_y="67px"
>
</TextView>
</AbsoluteLayout>
再完善NDK02Activity.java,使其内容如下:
package com.pnf.ndk;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class NDK02Activity extends Activity
implements
OnClickListener
{
// 声明界面元素
private EditText ed_operand1;
private Button btn_operator;
private EditText ed_operand2;
private Button btn_calculate;
private TextView tv_result;
// 声明一个NativeLibrary对象
private NativeLibrary nb;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 获取界面元素对象
ed_operand1 = (EditText)this.findViewById(R.id.ed_operand1);
btn_operator = (Button)this.findViewById(R.id.btn_operator);
ed_operand2 = (EditText)this.findViewById(R.id.ed_operand2);
btn_calculate = (Button)this.findViewById(R.id.btn_calculate);
tv_result = (TextView)this.findViewById(R.id.tv_result);
// 设定字体大小
ed_operand1.setTextSize(16.0f);
ed_operand2.setTextSize(16.0f);
tv_result.setTextSize(60.0f);
// 给按钮增加OnClickListener
btn_operator.setOnClickListener(this);
btn_calculate.setOnClickListener(this);
// 初始化NativeLibrary对象nb
nb = new NativeLibrary();
}
public void onClick(View v)
{
switch(v.getId())
{
// 点击按钮btn_operator时:
case R.id.btn_operator:
// 如果btn_operator当前的Text是"+",那么就让其Text变成"-"
if(btn_operator.getText().toString().equals("+"))
{
btn_operator.setText("-");
return;
}
// 如果btn_operator当前的Text是"-",那么就让其Text变成"*"
if(btn_operator.getText().toString().equals("-"))
{
btn_operator.setText("*");
return;
}
// 如果btn_operator当前的Text是"*",那么就让其Text变成"/"
if(btn_operator.getText().toString().equals("*"))
{
btn_operator.setText("/");
return;
}
// 如果btn_operator当前的Text是"/",那么就让其Text变成"+"
if(btn_operator.getText().toString().equals("/"))
{
btn_operator.setText("+");
return;
}
break;
// 如果点击按钮btn_calculate:
case R.id.btn_calculate:
// 首先从两个EditText中,获取操作数。
int operand1 = Integer.parseInt(ed_operand1.getText().toString());
int operand2 = Integer.parseInt(ed_operand2.getText().toString());
// result准备用于存放结果
String result = "";
// 如果btn_operator按钮上的Text是"+"
if(btn_operator.getText().toString().equals("+"))
{
// 调用nb对象中规定的Native方法add,并将结果转型为String
result = String.valueOf(nb.add(operand1, operand2));
}
// 如果btn_operator按钮上的Text是"-"
if(btn_operator.getText().toString().equals("-"))
{
// 调用nb对象中规定的Native方法min,并将结果转型为String
result = String.valueOf(nb.min(operand1, operand2));
}
// 如果btn_operator按钮上的Text是"*"
if(btn_operator.getText().toString().equals("*"))
{
// 调用nb对象中规定的Native方法mul,并将结果转型为String
result = String.valueOf(nb.mul(operand1, operand2));
}
// 如果btn_operator按钮上的Text是"/"
if(btn_operator.getText().toString().equals("/"))
{
// 调用nb对象中规定的Native方法divide,并将结果转型为String
result = String.valueOf(nb.divide(operand1, operand2));
}
// 将计算结果显示到tv_result中。
tv_result.setText(result);
break;
default:
break;
}
}
}
Step8: 运行

图21
运行结果。
加法运算:

图22
减法运算:

图23
乘法运算:

图24
除法运算:

图25