zoukankan      html  css  js  c++  java
  • android平台中编写jni模块的方法(3)

    这篇文章来说说ndk的使用方法,其实主要是关于ndk的一些编译选项的研究和翻译(其实人家google的文档已经说的很清楚了)。偶选用的测试环境是 slackware 12.0 + android 1.5 r1 for linux + jdk 1.6.0_12,ndk选用的是android 1.5 ndk r1这个版本的(直接解压就行,免安装的)。

    1、从ndk安装说起
    ndk安装的时候需要运行一 个~/android-ndk-1.5_r1/build/目录下面的一个叫做host-setup.sh的脚本。大略读了一下这个脚本,发现这个主要是 用来生成out/host/host/config.mk文件的。主要用于指定用户操作系统的判断以及支持的编译器类型(设置makefile中的 cc,ar,ld之类的变量)
    ndk的目录介绍。

    2、ndk的目录结构分析
    进入android-ndk-1.5_r1目录,看到如下目录结构:
    GNUmakefile: 标准的makefile格式的文件,用于引用build/core/main.mk的编译脚本。
    README.TXT:基本的说明,没啥大用,真正有用的文档都在docs目录下面。
    apps/:存放带有jni接口的android工程目录(工程里面有利用native关键字定义的java函数)
    build/:存放着几乎所有的ndk编译相关的脚本以及必要的静态链接库。
    docs/:存放这ndk的所有“官方”文档,每一篇文档对于jni编写者来说这里面的任何一点点资料都是无价的。
    out/:存放一些中间的临时文件,例如jni的.c/.cpp文件编译过程中产生的.o文件等。
    sources/:存放jni文件的.c/.cpp的源代码文件。

    3、基本的使用方法
    (1)创建一个android工程
    进入apps目录,运行如下命令:
    android create project --target 2 --package com.TWM --activity NDKTest --path ./NDKTest/project
    通 过命令行创建一个叫做NDKTest的activity,注意这里的--path需要设置为./XXXXX/project这个目录,这个XXXXX目录 主要是为了ndk的make区分不同项目和工程使用的。编写Application.mk文件的时候,一定要把Application.mk写到这个 XXXXX目录下面。
    $NDK/apps/<myapp>/Application.mk

    另外,编译jni库的时候使用的命令也是如此:
    make APP=<your app name>
    这里的<your app name>实际上也是这个XXXXX目录。

    (2)为工程添加一个jni的java调用接口
    进入app/NDKTest/project/src/com/TWM/NdkTest目录,建立一个新的java文件(例如:NDKJni.java),然后把代码写成类似下面这个样子:
    package com.TWM.NdkTest ;
    public class NDKJni {
        public native int MyFunc(int a, int b) ;
        static {
            System.loadLibrary("NDKjni") ;
        }
    }
    这里的MyFunc由于是使用native修饰,因此,这个MyFunc函数是一个调用jni的函数。

    (3)为java工程编写Application.mk文件
    该文件主要放在app/NDKTest目录下,用于告知ndk的编译脚本,当前的程序需要哪个jni模块。
    看上去应该是这个样子的:
    APP_PROJECT_PATH := $(call my-dir)/project   ---> 当前目录下的project目录包含了jni模块的java接口
    APP_MODULES      := NDKTest                               --->当前模块的名字叫做NDKTest


    (4)弄清楚java程序的包层次
    以当前的这个project为例,就是上面代码中的package com.TWM.NdkTest,定义的类名为NDKJni。因此,根据这个包的层次,可以根据jni文件的函数命名规则定义函数:
    JNIEXPORT jint JNICALL Java_com_TWM_NdkTest_NDKJni_MyFunc(JNIEnv * env, jobject thiz, jint a, jint b) ;
    当然,手工根据包层次定义jni函数还是很痛苦的,可以借助于javah工具:
    mkdir -p apps/NDKTest/project/jni
    cd apps/NDKTest/project/jni
    javah -classpath "../bin/classes" com.TWM.NdkTest.NDKJni
    然后就会自动生成一个叫做com_TWM_NdkTest_NDKJni.h的文件,里面的内容基本上跟手工生成的差不多:
    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_TWM_NdkTest_NDKJni */

    #ifndef _Included_com_TWM_NdkTest_NDKJni
    #define _Included_com_TWM_NdkTest_NDKJni
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     com_TWM_NdkTest_NDKJni
     * Method:    MyFunc
     * Signature: (II)I
     */
    JNIEXPORT jint JNICALL Java_com_TWM_NdkTest_NDKJni_MyFunc
      (JNIEnv *, jobject, jint, jint);

    #ifdef __cplusplus
    }
    #endif
    #endif
    好了,有了这个函数的定义,就可以准备去编写jni了。

    (5)进入source目录
    建立目录ndktest,然后在里面放置两个文件,一个是随便命名例如a.c,另外一个是一个叫做Android.mk的编译脚本文件。
    里面的内容如下:
    LOCAL_PATH := $(call my-dir)

    include $(CLEAR_VARS)

    LOCAL_MODULE    := NDKTest         --->这里是指定jni模块的名字,生成的so库应该叫做libNDKTest.so,这个名字一定要与Application.mk文件中的APP_MODULES相同。
    LOCAL_SRC_FILES := a.c

    include $(BUILD_SHARED_LIBRARY)  --->这里是告诉编译脚本生成的库是共享库(本身NDK是可以生成动态库和静态库的)。

    然后在a.c里面写入的内容如下:
    JNIEXPORT jint JNICALL
    Java_com_TWM_NdkTest_NDKJni_MyFunc(JNIEnv * env, jobject thiz, jint a, jint b)
    {
      return a+b ;
    }

    (6)开始编译jni模块
    首先进入android-ndk-1.5_r1目录,然后运行如下命令:
    make APP=NDKTest [回车]
    这个时候就会看到它开始编译并且在apps/NDKTest/projects/目录下建立了libs/armeabi/目录,并且把生成的libNDKTest.so拷贝到该目录下。
    看到这里或许有人会问,它的编译参数怎么没有,我怎么调试阿?!其实很简单,只要多加一个编译参数即可。
    make APP=NDKTest V=1 [回车]

    你就会看到如下的输出(偶的测试程序里面把上面说的a.c改成NDKTest.c了,所以看到的内容略有不同):
    wayne@wayne:~/android-ndk-1.5_r1$ make APP=NDKTest V=1
    Android NDK: Building for application 'NDKTest'
    Compile thumb  : NDKTest <= sources/ndktest/NDKTest.c
    build/prebuilt/linux-x86/arm-eabi-4.2.1/bin/arm-eabi-gcc -Ibuild/platforms/android-1.5/arch-arm/usr/include -march=armv5te -mtune=xscale -msoft-float -fpic -mthumb-interwork -ffunction-sections -funwind-tables -fstack-protector -fno-short-enums -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__   -Isources/ndktest -DANDROID  -O2 -DNDEBUG -g    -c -MMD -MP -MF out/apps/NDKTest/android-1.5-arm/objs/NDKTest/NDKTest.o.d.tmp sources/ndktest/NDKTest.c -o out/apps/NDKTest/android-1.5-arm/objs/NDKTest/NDKTest.o
    build/core/mkdeps.sh out/apps/NDKTest/android-1.5-arm/objs/NDKTest/NDKTest.o out/apps/NDKTest/android-1.5-arm/objs/NDKTest/NDKTest.o.d.tmp out/apps/NDKTest/android-1.5-arm/objs/NDKTest/NDKTest.o.d
    SharedLibrary  : libNDKTest.so
    build/prebuilt/linux-x86/arm-eabi-4.2.1/bin/arm-eabi-gcc -nostdlib -Wl,-soname,libNDKTest.so -Wl,-shared,-Bsymbolic  out/apps/NDKTest/android-1.5-arm/objs/NDKTest/NDKTest.o -Wl,--whole-archive  -Wl,--no-whole-archive   build/platforms/android-1.5/arch-arm/usr/lib/libc.so build/platforms/android-1.5/arch-arm/usr/lib/libstdc++.so build/platforms/android-1.5/arch-arm/usr/lib/libm.so   -Wl,--no-undefined   -Wl,-rpath-link=build/platforms/android-1.5/arch-arm/usr/lib /home/wayne/android-ndk-1.5_r1/build/prebuilt/linux-x86/arm-eabi-4.2.1/bin/../lib/gcc/arm-eabi/4.2.1/interwork/libgcc.a -o out/apps/NDKTest/android-1.5-arm/libNDKTest.so
    Install        : libNDKTest.so => apps/NDKTest/project/libs/armeabi
    mkdir -p apps/NDKTest/project/libs/armeabi
    install -p out/apps/NDKTest/android-1.5-arm/libNDKTest.so apps/NDKTest/project/libs/armeabi/libNDKTest.so
    build/prebuilt/linux-x86/arm-eabi-4.2.1/bin/arm-eabi-strip --strip-debug  apps/NDKTest/project/libs/armeabi/libNDKTest.so

    (7)开始编译android本地java程序
    进入apps/NDKTest/project目录,然后运行ant debug来生成调试版本的apk包,注意,此时,apk包里面会自动把刚刚生成的libNDKTest.so打包进去的。这一点可以通过把apk文件用unzip命令解包来验证,在此不再赘述。

    这就是android-ndk编译jni程序的全过程,确实要比偶在上一篇文章中描述的方法来得简单许多,总结一下:
    (a)在apps目录里面创建带有native关键字声明的java项目。(注意,目录需要多打一层,用来放Application.mk文件)
    (b)在sources目录里面创建真正的jni模块目录,里面一定要包含一个叫做Android.mk的文件。
    (c)在apps里面的Application.mk与sources目录里面的Android.mk在MODULE的名字上一定要“遥相呼应”。
    (d)编译的方法是,进入android-ndk-1.5_r1目录,运行make APP=<your app name> V=1生成jni库;其实不止如此,make APP=<your app name> clean也可以清除掉。

    但愿这篇文章能够对各位致力于android开发和移植的朋友们有所帮助。

  • 相关阅读:
    算法训练 表达式计算
    基础练习 十六进制转十进制
    基础练习 十六进制转十进制
    基础练习 十六进制转十进制
    New ways to verify that Multipath TCP works through your network
    TCP的拥塞控制 (Tahoe Reno NewReno SACK)
    Multipath TCP Port for Android 4.1.2
    How to enable ping response in windows 7?
    NS3
    Multipath TCP Port for Android
  • 原文地址:https://www.cnblogs.com/xiaoxiaoboke/p/2348946.html
Copyright © 2011-2022 走看看