Android 安卓使用C/C++静态库/动态库指南

原文:http://blog.csdn.net/luofl1992/article/details/8663171


目前我在做HM(HEVC测试软件)移植到Android平台,在移植过程中出现了一些问题,顺便记录下来,供大家分享。也让大家在出现问题是能够有一个参照。这些天忙着调试程序中出现的BUG,没有来得及更新。

移植HM详细请看另外一篇日志:http://blog.csdn.net/luofl1992/article/details/8736149

一、必备的工具

        开始之前请确认你有这样几个工具 :

        Java JDK(Java Development Toolkit )

        Android SDK (简称ADK )可以直接下载Eclipse+ADT二合一版本

        Eclipse (需要集成 CDT,直接使用上面的ADK开发的无视这个)

        NDK (Android NDK)

        这些工具没有的安装,可以去官方网站下载。各个工具的安装教程网络上很多,自己找一找,这里就不再列出了。


二、开始

  1、使用IDE工具

        用Eclipse建立一个工程,配置相关选项,建立工程完毕,我们可以进入工程目录(即 PROJECT_DIR )。

        然后建立一个新的文件夹,叫做 jni 的目录。

        自己在里面添加两个文件 Application.mk 和 Android.mk (我没有装NDK的时候说一直再找哪里有这两个文件)

        然后,修改一下这两个文件的内容,具体说明请看:

       Android-ndk/docs/Android-mk.html  等等一系列参考文档,说明了参数的意义之类的。


  2、使用命令行

cd到任意目录下,首先执行这条命令查看一下Android对应的版本号(下面会用到)

  1. android list targets  
然后执行这样的指令:

  1. android create project \  
  2. --target <target-id> \  
  3. --name MyFirstApp \  
  4. --path <path-to-workspace>/MyFirstApp \  
  5. --activity MainActivity 、  
  6. --package com.example.myfirstapp  

这里  <target-id>是上面一条指令列出的版本号,数字,比如我执行上面一条指令的结果是9表示 Android 2.3.3,是我需要生成的目标平台版本号,

--name 后面为项目名称,比如我的是 --name NcHevcPlayer

--path 后面跟的是项目的路径,为文件夹的名字,比如我要在当前目录下建立 NcHevcPlayer 的文件夹作为工程目录,就是 --path NcHevcPlayer

--activity 后面跟的是主Activity的类名字,随意指定。

--package 后面跟着包名字,可以像这样 --package com.NcHevc

(先卖个关子,包的名字会对后面使用NDK、JNI造成影响)

那么整个命令就像这样子:

  1. android create project \  
  2. --target 9 \  
  3. -name NcHevcPlayer \  
  4. --path NcHevcPlayer \  
  5. --activity NcHevcPlayerActivity \  
  6. --package com.NcHevc  

更加详细的说明可以参考NDK的网站,或者在本机NDK目录下的docs里面阅读相关说明。

http://developer.android.com/tools/projects/projects-cmdline.html

如果没有错误,那么在当前目录下会出现一个NcHevcPlayer目录,由上面的--path指定。

或者可以对已有Android项目更新,其语法如下:

  1. android update project \  
  2. --name <project_name> \  
  3. --target <target_ID> \  
  4. --path <path_to_your_project>  


这个步骤完成之后工程的目录结构如下图:



三、JAVA调用

      JAVA调用C/C++的函数需要使用JNI,需要使用的指令有 javac 和 javah

      详细的介绍请参考这篇文章:http://www.cnblogs.com/hibraincol/archive/2011/05/30/2063847.html

      首先在某个类(比如Activity类)增加native函数,然后运行javac指令编译该类的java文件,

      生成一个class文件,再用javah命令,生成对应的头文件。

      建议cd到src目录,不要再进去了,执行

     javac com/NcHevc/NcHevcDecoder.java

     javah com.NcHevc.NcHevcDecoder

    就可以得到一个头文件了,见下面JNI目录的图中NcHevcDecoder.h。

其内容如下:

  1. /* DO NOT EDIT THIS FILE - it is machine generated */  
  2. #include <jni.h>  
  3. /* Header for class com_NcHevc_NcHevcDecoder */  
  4.   
  5. #ifndef _Included_com_NcHevc_NcHevcDecoder  
  6. #define _Included_com_NcHevc_NcHevcDecoder  
  7.   
  8. // 这三行是额外添加的  
  9. #include "TLibCommon/CommonDef.h"  
  10.   
  11. typedef unsigned char byte; // typedefine type as unsigned char  
  12.   
  13. // 以下为JAVAH生成的函数原型  
  14. #ifdef __cplusplus  
  15. extern "C" {  
  16. #endif  
  17. /* 
  18.  * Class:     com_NcHevc_NcHevcDecoder 
  19.  * Method:    Open 
  20.  * Signature: (Ljava/lang/String;)Z 
  21.  */  
  22. JNIEXPORT jboolean JNICALL Java_com_NcHevc_NcHevcDecoder_Open  
  23.   (JNIEnv *, jobject, jstring);  
  24.   
  25. /* 
  26.  * Class:     com_NcHevc_NcHevcDecoder 
  27.  * Method:    GetPixelsBuffer 
  28.  * Signature: ([I)V 
  29.  */  
  30. JNIEXPORT void JNICALL Java_com_NcHevc_NcHevcDecoder_GetPixelsBuffer  
  31.   (JNIEnv *, jobject, jintArray);  
  32.   
  33. /* 
  34.  * Class:     com_NcHevc_NcHevcDecoder 
  35.  * Method:    DecodeFrame 
  36.  * Signature: ()Z 
  37.  */  
  38. JNIEXPORT jboolean JNICALL Java_com_NcHevc_NcHevcDecoder_DecodeFrame  
  39.   (JNIEnv *, jobject);  
  40.   
  41. #ifdef __cplusplus  
  42. }  
  43. #endif  
  44. #endif  



四、修改Andoid.mk文件

        我的是这样子的:

  1. LOCAL_PATH := $(call my-dir)  
  2. # 是否采用改进了的 HM 库  
  3. USING_IMPROVED_HM := true  
  4. # 一些路径的设置,相对路径和绝对路径  
  5. ifeq ($(USING_IMPROVED_HM),true)  
  6. # 优化的版本,加快运行效率  
  7. PATH_HM := libHM  
  8. else  
  9. # 标准版本(只修改错误使其能够正常在Android平台运行)  
  10. PATH_HM := libHMStandard  
  11. endif  
  12. PATH_HM_ABSOLUTE := $(LOCAL_PATH)/$(PATH_HM)  
  13.   
  14. #include $(CLEAR_VARS)  
  15. #LOCAL_MODULE := HmDecoder      # name it whatever  
  16. #LOCAL_SRC_FILES := libHM.a   
  17. #include $(PREBUILT_STATIC_LIBRARY)  
  18.   
  19. # Here we give our module name and source file(s)  
  20. include $(CLEAR_VARS)  
  21. LOCAL_MODULE := NcHevcDecoder  
  22. LOCAL_CPP_EXTENTION := .cpp  
  23. LOCAL_CXXFLAGS := -D__cplusplus -g  
  24. # LOCAL_CPPFLAGS += -fexceptions  
  25. #设置系统默认包含路径,避免编译时出现找不到头文件的情况  
  26. LOCAL_C_INCLUDES := ~/android-ndk/sources/cxx-stl/stlport/stlport:$(LOCAL_PATH)  
  27. LOCAL_C_INCLUDES += $(PATH_HM_ABSOLUTE)  
  28. LOCAL_CPP_INCLUDES := ~/android-ndk/sources/cxx-stl/stlport/stlport:$(LOCAL_PATH)  
  29. LOCAL_CPP_INCLUDES += $(PATH_HM_ABSOLUTE)  
  30. # find . -name '*.c*' 编码器相关的库实际上没有使用到,可以不予添加  
  31. LOCAL_SRC_FILES := NcHevcDecoder.cpp TAppDecTop.cpp JNI_OnLoad.cpp \  
  32. $(PATH_HM)/libmd5/libmd5.c \  
  33. $(PATH_HM)/TLibEncoder/TEncEntropy.cpp \  
  34. $(PATH_HM)/TLibEncoder/TEncBinCoderCABAC.cpp \  
  35. $(PATH_HM)/TLibEncoder/TEncSbac.cpp \  
  36. $(PATH_HM)/TLibEncoder/SyntaxElementWriter.cpp \  
  37. $(PATH_HM)/TLibEncoder/TEncGOP.cpp \  
  38. $(PATH_HM)/TLibEncoder/TEncSearch.cpp \  
  39. $(PATH_HM)/TLibEncoder/TEncPic.cpp \  
  40. $(PATH_HM)/TLibEncoder/NALwrite.cpp \  
  41. $(PATH_HM)/TLibEncoder/TEncCavlc.cpp \  
  42. $(PATH_HM)/TLibEncoder/TEncPreanalyzer.cpp \  
  43. $(PATH_HM)/TLibEncoder/TEncBinCoderCABACCounter.cpp \  
  44. $(PATH_HM)/TLibEncoder/WeightPredAnalysis.cpp \  
  45. $(PATH_HM)/TLibEncoder/TEncAnalyze.cpp \  
  46. $(PATH_HM)/TLibEncoder/TEncRateCtrl.cpp \  
  47. $(PATH_HM)/TLibEncoder/SEIwrite.cpp \  
  48. $(PATH_HM)/TLibEncoder/TEncSlice.cpp \  
  49. $(PATH_HM)/TLibEncoder/TEncSampleAdaptiveOffset.cpp \  
  50. $(PATH_HM)/TLibEncoder/TEncCu.cpp \  
  51. $(PATH_HM)/TLibEncoder/TEncTop.cpp \  
  52. $(PATH_HM)/TLibDecoder/SyntaxElementParser.cpp \  
  53. $(PATH_HM)/TLibDecoder/TDecGop.cpp \  
  54. $(PATH_HM)/TLibDecoder/TDecTop.cpp \  
  55. $(PATH_HM)/TLibDecoder/TDecEntropy.cpp \  
  56. $(PATH_HM)/TLibDecoder/TDecCAVLC.cpp \  
  57. $(PATH_HM)/TLibDecoder/TDecCu.cpp \  
  58. $(PATH_HM)/TLibDecoder/NALread.cpp \  
  59. $(PATH_HM)/TLibDecoder/TDecSbac.cpp \  
  60. $(PATH_HM)/TLibDecoder/SEIread.cpp \  
  61. $(PATH_HM)/TLibDecoder/TDecSlice.cpp \  
  62. $(PATH_HM)/TLibDecoder/AnnexBread.cpp \  
  63. $(PATH_HM)/TLibDecoder/TDecBinCoderCABAC.cpp \  
  64. $(PATH_HM)/TAppCommon/program_options_lite.cpp \  
  65. $(PATH_HM)/TLibVideoIO/TVideoIOYuv.cpp \  
  66. $(PATH_HM)/TLibCommon/TComRdCostWeightPrediction.cpp \  
  67. $(PATH_HM)/TLibCommon/TComCABACTables.cpp \  
  68. $(PATH_HM)/TLibCommon/TComPic.cpp \  
  69. $(PATH_HM)/TLibCommon/TComSampleAdaptiveOffset.cpp \  
  70. $(PATH_HM)/TLibCommon/TComPattern.cpp \  
  71. $(PATH_HM)/TLibCommon/TComLoopFilter.cpp \  
  72. $(PATH_HM)/TLibCommon/TComPrediction.cpp \  
  73. $(PATH_HM)/TLibCommon/SEI.cpp \  
  74. $(PATH_HM)/TLibCommon/TComPicYuvMD5.cpp \  
  75. $(PATH_HM)/TLibCommon/TComRdCost.cpp \  
  76. $(PATH_HM)/TLibCommon/TComBitStream.cpp \  
  77. $(PATH_HM)/TLibCommon/TComTrQuant.cpp \  
  78. $(PATH_HM)/TLibCommon/TComSlice.cpp \  
  79. $(PATH_HM)/TLibCommon/TComPicYuv.cpp \  
  80. $(PATH_HM)/TLibCommon/TComDataCU.cpp \  
  81. $(PATH_HM)/TLibCommon/TComWeightPrediction.cpp \  
  82. $(PATH_HM)/TLibCommon/TComInterpolationFilter.cpp \  
  83. $(PATH_HM)/TLibCommon/ContextModel.cpp \  
  84. $(PATH_HM)/TLibCommon/TComRom.cpp \  
  85. $(PATH_HM)/TLibCommon/TComMotionInfo.cpp \  
  86. $(PATH_HM)/TLibCommon/ContextModel3DBuffer.cpp \  
  87. $(PATH_HM)/TLibCommon/TComYuv.cpp \  
  88. $(PATH_HM)/TLibCommon/TComPicSym.cpp  
  89. LOCAL_STATIC_LIBRARIES := ~/android-ndk/sources/cxx-stl/stlport/libs/armeabi/libstlport_static.a  
  90. LOCAL_LDLIBS := -llog  
  91. # 导出动态库  
  92. include $(BUILD_SHARED_LIBRARY)  

同时可以在JNI目录下建立一个Application.mk文件,内容如下:

  1. APP_PROJECT_PATH := $(call my-dir)/..    
  2. APP_PLATFORM := android-10    
  3. APP_STL := stlport_static    
  4. APP_ABI := armeabi-v7a    
  5. APP_CPPFLAGS += -fexceptions  
JNI目录的结构是这样子的::


五、编译


      命令行下cd到工程文件目录,即 PROJECT_DIR ,然后执行 ndk-build 指令即可。

      不知到在什么情况下需要JNI_OnLoad,想知道怎么弄可以看这里:http://blog.csdn.net/luhuajcdd/article/details/7750146

      完毕后在Eclipse下执行build,生成并且 Run 就会生成apk文件

     命令行模式下需要安装ant工具,首先执行 sudo apt-get install ant

      安装完毕后创建一个签名文件,详细请看http://blog.csdn.net/aeolus1019/article/details/8121031

      为了使ant编译工程时能够自动打包签名文件,在工程目录的ant.properties文件的末尾增加这样几行:

  1. key.alias=mNcDecoder.keystore  
  2. key.store=mNcDecoder.keystore  
  3. key.store.password=这里该成密码  
  4. key.alias.password=这里改成密码  
      上述操作都完成后最后执行:

      ant release

(如果编译出错,请查看local.properties中的 SDK 路径是否正确,这只针对自己出于某些需要移动了SDK目录的情况)

六、生成apk与测试

        用Eclipse选择 Run as Android app,然后会自动启动模拟器或者连接Android设备,

        大家可以用自己的手机进行测试,模拟器太慢了啊。

        怎么创建模拟器这里不讨论了。

        命令行模式下,需要用adb指令。可以这么弄:

        abd install -r bin/NcHevcPlayer-release.apk

        其中-r参数表示重新安装,可以在此之前先卸载  adb uninstall com.NcHevc

最后为了使整个过程简单完成,可以写这样的一个shell脚本,保存成run.sh文件放到工程目录下,并且附加执行权限:

  1. echo -e "\033[41;32;1m 清除 bin 文件目录下的内容 \033[0m"  
  2. rm -r bin  
  3. echo -e "\033[41;32;1m 建立共享库 NcHevcDecoder  \033[0m"  
  4. ndk-build  
  5. echo -e "\033[41;32;1m 编译生成 APK \033[0m"  
  6. ant release  
  7. echo -e "\033[41;32;1m 清除原先安装版本 \033[0m"  
  8. adb devices  
  9. adb uninstall com.NcHevc  
  10. echo -e "\033[41;32;1m 安装最新版到手机或模拟设备 \033[0m"  
  11. adb install -r bin/NcHevcPlayer-release.apk  
  12. echo -e "\033[41;32;1m 正在清除系统日志 \033[0m"  
  13. adb logcat -c  
  14. echo -e "\033[41;32;1m 操作完成,请进行手机测试 \033[0m"  
  15. echo -e "\033[41;32;1m 以下为调试信息,结束查看日志按Ctrl+C即可 \033[0m"  
  16. # adb logcat -v time *:I > logcat.txt  
  17. adb logcat -v time NcHevcPlayerActivity:I *:S  

以后修改了源代码只要运行这个脚本,就可以了,

停止该脚本的方法是按住 左Ctrl + C 两个键,就会自动终止。

最后的几条语句是使用logcat输出调试信息,具体可以查看:http://blog.csdn.net/czbever/article/details/5910640

或者:http://blog.chinaunix.net/uid-23173926-id-109068.html



七、其他

      JAVA的对象其实是C++的指针,所以使用之前必须要赋值。否则会出现 NullPointerException,自己还不知道怎么回事。

      特别注意:在ARM平台(Android开发)下,char类型默认为 unsigned char,而GCC和MSVC默认为 signed char.

                         使用不慎会导致严重BUG。

        例如这样的程序:

  1. char k = -1;  
  2. int i = k + 1;  // 值应该为0吧。(但是最终结果是256,瞎了把)  
  3. int a[20];  
  4. int j;  
  5. j = a[i];   // 只是举例说明,最后这里会出现动态时错误  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值