Android NDK 开发入门指引
前言:
Android NDK 是一套工具,允许 Android 应用开发者嵌入从 C 、 C++ 源代码文件编译来的本地机器代码到各自的应用软件包中 , 并通过 JNI 进行访问。 Android 从 1.5 开始支持 NDK.
环境搭建:
NDK 开发需要安装以下物体 :
1.cygwin ( 需要 1.7 以上版本,下载地址: http://www.cygwin.com 官方明确表示 MSys or Cygwin 1.5 is not supported), 这个我用的是在线安装方法(并且是全安装,非常大,很耗时)
安装好后启动 cygwin, 运行 :
Make –v
看到如下所示就 ok 了 ( 注意 :GNU Make 要 3.81 以上版本 )
2.NDK (android-ndk-r4b) ,此文档只针对 r4b 版进行说明 , ndk 可以到官方网 http://developer.android.com 上进行下载, 但这个网站已被 zf 和协,我是在这里下载: http://d131.d.iask.com/fs/800/1/c1631bdc5eaf7b8309db9cb23311693035788482/zip/android-ndk-r4b-windows.zip , 如下载不了,直接向我要吧 .
Ndk 下载下来随意解压到一个目录即可 ( 路径中不能有空格 )
如 : D:/android/android-ndk-r4b
然后为 cygwin 配好我们的 ndk PATH 环境
在 cygwin 用户路径 C:/cygwin/home/huangdingwu ( 请替换相应用户名 )
打开 .bash_profile
在文件末尾加上:
ANDROID_NDK_ROOT=/cygdrive/d/android/android-ndk-r4b
export ANDROID_NDK_ROOT
PATH=${ANDROID_NDK_ROOT}:${PATH}
配好后重新打开 cygwin
输入:
Ndk-build
如出现如下提示表示路径已配好:
3.eclipse + android sdk ( 这个 … 大家应该都已经安装好了 )
4.windows 中 Jdk 安装配置
第一步:下载 JDK
JDK 是 Java Developement Kit 的缩写,包括 Java 编译器和运行时环境 j2re , 可以从 SUN Java 站点下载
http://java.sun.com/javase/downloads/index.jsp
http://www6.software.ibm.com/dl/idpe/idpe-p?S_TACT=104AHW02
或它的前一面: http://www-106.ibm.com/developerworks/java/jdk/eclipse/
第二步:安装 JDK
直接运行 JDK 的安装程序,选择一个安装目录,按照安装向导即可轻松完成 JDK 的安装。如需了解安装细节,请参考 Java 2 SDK Installation Notes for Microsoft Windows 。
第三步:设置环境变量
JAVA_HOME=<JDK 安装目录 >
Path=< 原 Path>;%JAVA_HOME%/bin;%JAVA_HOME%/jre/bin
(注意其实没有等号)
本人使用的是 jdk-6-windows-i586.exe 默认安装路径 C:/Program Files/Java/jdk1.6.0 及 JAVA_HOME=C:/Program Files/Java/jdk1.6.0
第四步:编译一个 Java 程序
请用任何文本编辑器输入以下内容并保存为 Hello.java 文件(只能保存为 Hello.java ,不能使用别的文件名),请严格注意大小写:
public class Hello
{
public static void main(String[] args)
{
System.out.println("Hello, world.");
}
}
然后打开控制台,切换到存放 Hello.java 的目录下,输入: javac Hello.java
如果编译通过,屏幕上没有任何显示。否则,屏幕上会输出出错信息,请仔细检查源代码。
第五步:运行一个 Java 程序
打开控制台,切换到存放 Hello.class 的目录下,然后输入: java Hello ( 注意大小写啊 !)
稍等几秒钟,屏幕输出: Hello, world.
说明运行成功!
如果不成功可能环境变量不正确
简单的 NDK Demo 程序
好了,环境已搭建好, 现在我们开始建立一个简单的 ndk 程序来体验下整个 ndk 开发流程 :
第一步:首先我们在eclipse 中新建一个工程,工程名这里是NdkPassNormalData 。 在工程 中新建一个java 类:CallNativeDemo.java
内容如下:
package com.jiubang.Demo.Ndk.PassNormalData;
public class CallNativeDemo {
static {
System.loadLibrary ( "NdkPassDataDemo" );
// 注意库文件名,对于 NdkPassDataDemo 系统会在该类初始化时加载 //libNdkPassDataDemo.so 库 , 切记勿画蛇添足加上前面的 lib 与 .so 扩展名
}
public native int Sum( int a , int b); // 加上 native 表示该方法由 .so 实现
public native String StrCat(String str1 , String str2);
}
编写好该 java 文件后,先编译,然后用 javah 生成 ndk 中我们需要的 c 头文件 :
进入到工程的 bin 目录下输入:
javah -jni com.jiubang.Demo.Ndk.PassNormalData.CallNativeDemo
然后会在当前目录下生成com_jiubang_Demo_Ndk_PassNormalData_CallNativeDemo.h 文件
内容大概如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_jiubang_Demo_Ndk_PassNormalData_CallNativeDemo */
#ifndef _Included_com_jiubang_Demo_Ndk_PassNormalData_CallNativeDemo
#define _Included_com_jiubang_Demo_Ndk_PassNormalData_CallNativeDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_jiubang_Demo_Ndk_PassNormalData_CallNativeDemo
* Method: Sum
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_jiubang_Demo_Ndk_PassNormalData_CallNativeDemo_Sum
(JNIEnv *, jobject, jint, jint);
/*
* Class: com_jiubang_Demo_Ndk_PassNormalData_CallNativeDemo
* Method: StrCat
* Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_jiubang_Demo_Ndk_PassNormalData_CallNativeDemo_StrCat
(JNIEnv *, jobject, jstring, jstring);
#ifdef __cplusplus
}
#endif
#endif
第二步:在我们新的工程中加一个名为:jni 的文件夹。这个文件夹就是放原生态c/c++ 的源码的地方,我们make 的时候cygwin 就是编译这个文件夹的。
第三步:在jni 文件夹里新建一个Android.mk 文件。注意,后缀为.mk , 且文件夹一定要小写
Android.mk 的内容为:
LOCAL_PATH := $(call my-dir) <--------- 默认的,不需要更改
include $(CLEAR_VARS) <-------- 默认的,不需要更改
LOCAL_MODULE := native <-------- 在java 类引用时的名称
LOCAL_SRC_FILES := myNative.c <------ 在jni 文件夹下的 c/c++ 的名称
include $(BUILD_SHARED_LIBRARY) <-- 默认的,不需要更改
LOCAL_PATH := $(call my-dir) <--------- 返回当前路径
include $(CLEAR_VARS) <-------- 清除先前定义的环境变量
LOCAL_MODULE := NdkPassDataDemo <-------- 在 java 类引用时的名称
LOCAL_CPP_EXTENSION := cpp
LOCAL_SRC_FILES := Fun.cpp <------ 需要编译的源文件
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib –llog <-- 打 log 要用到的库,这里暂时不大明白为什么库名是这样 …
include $(BUILD_SHARED_LIBRARY) <-- 这里指的是动态库
第四步:在jni 文件夹里新建一个 Fun.cpp 的文件。这个文件就是c/c++ 文件。
Fun.cpp 的内容为:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <android/log.h>
#include "com_jiubang_Demo_Ndk_PassNormalData_CallNativeDemo.h"
// 对应 public native int Sum( int a , int b) 的实现
jint JNICALL Java_com_jiubang_Demo_Ndk_PassNormalData_CallNativeDemo_Sum(JNIEnv* aEnv, jobject aThis, jint aParamA, jint aParamB)
{
int sum = 0;
__android_log_print(ANDROID_LOG_INFO , "Ndk_PassNormalData" , "CallNativeDemo_Sum Coming");
sum = aParamA + aParamB;
FILE* file = fopen("/sdcard/hello.txt","w+");
if (file != NULL)
{
fputs("HELLO WORLD!/n", file);
fflush(file);
fclose(file);
}
__android_log_print(ANDROID_LOG_INFO , "Ndk_PassNormalData" , "CallNativeDemo_Sum Leaving");
return sum;
}
// 对应 public native String StrCat(String str1 , String str2) 实现
jstring JNICALL Java_com_jiubang_Demo_Ndk_PassNormalData_CallNativeDemo_StrCat(JNIEnv *aEnv, jobject aThis, jstring aStr1, jstring aStr2)
{
__android_log_print(ANDROID_LOG_INFO , "Ndk_PassNormalData" , "CallNativeDemo_StrCat Coming");
const char* str1 = aEnv->GetStringUTFChars(aStr1 , 0);
const char* str2 = aEnv->GetStringUTFChars(aStr2 , 0);
//char* str1 = (char*)(*aEnv)->GetStringUTFChars(aEnv ,aStr1 , 0);
//char* str2 = (char*)(*aEnv)->GetStringUTFChars(aEnv ,aStr2 , 0);
int str1_len = strlen(str1);
int str2_len = strlen(str2);
// char* pszRet = malloc(str1_len + str2_len + 1);
char* pszRet = new char[str1_len + str2_len + 1];
strcpy(pszRet, str1);
strcat(pszRet, str2);
//jstring jstrRet = (*aEnv)->NewStringUTF(aEnv, pszRet);
jstring jstrRet = aEnv->NewStringUTF(pszRet);
// free(pszRet);
delete pszRet;
__android_log_print(ANDROID_LOG_INFO , "Ndk_PassNormalData" , "CallNativeDemo_StrCat Leaving");
return jstrRet;
}
然后就可以开始编译了 :
打开 cygwin, 进入到工程的 jni 目录,然后执行 ndk-build
有看到 Install 就 OK 了
然后会发现工程目录下会多了一个 lib 与 obj 的目录
最后在 android 工程里加上调用这些 ndk 本地方法的代码:
运行,得到结果:
更多的 jni 数据传递处理相关资料参见:
http://andilyliao.javaeye.com/blog/494255
http://blog.csdn.net/sunyujia/archive/2008/05/03/2371557.aspx
NDK 示例开发参考:
http://blog.csdn.net/GEOLO/archive/2010/10/20/5953941.aspx
本教程工程下载:
http://download.csdn.net/source/3224679