Android NDK开发入门指引(有源码)

    技术2025-12-03  8

    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 本地方法的代码:

     

            CallNativeDemo nativeDemo = new CallNativeDemo(); int value = nativeDemo.Sum(10, 20); String str = nativeDemo.StrCat("who", "are you"); TextView v = (TextView)findViewById(R.id.textview1); v.setText(str); 

    运行,得到结果:

     

    更多的 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

     

    最新回复(0)