NDK环境搭建
windows上搭建NDK开发环境:VS2013 + VisualGDB,参考链接:http://jingyan.baidu.com/article/a681b0de1a361c3b1843460c.html
VS2013新建一个安卓项目,选择创建一个安卓动态库,然后就可以开始编写C\C++代码了。
添加STL支持
STL是C++开发最常用的一个库了,直接在程序中添加头文件:
#include <string>
#include <vector>
using std::string;
using std::vector;</span>
会报错,找不到对应的头文件,这是因为我们没有将STL头文件的路径告诉编辑器(NDK工程的配置和我们windows上开发的配置是不一样的)。
1、打开Application.mk,添加链接STL静态库:APP_STL := stlport_static
2、打开Android.mk,增加STL头文件和库文件完整路径:
LOCAL_C_INCLUDES := $(NDK_ROOT)/sources/cxx-stl/stlport/stlport
LOCAL_STATIC_LIBRARIES := $(NDK_ROOT)/sources/cxx-stl/stlport/libs/armeabi/libstlport_static.a
NDK_ROOT是搭建开发环境时设置的环境变量,其路径就是NDK安装的位置根目录。
这时候,编译就不会有找不到头文件错误了。
在java代码中调用so
1、拷贝SO库文件到安卓项目的:libs\armeabi文件夹下(没有就创建改文件夹)
2、添加代码加载库文件:
static{
System.loadLibrary("AndroidProject2-shared");
System.loadLibrary("JniTest1-shared");
}
特别注意,加载库的文件名一定要去掉前面的lib,比如我这里生成的库文件是:libJniTest1-shared.so,在代码里加载时是:
System.loadLibrary("JniTest1-shared")。
当然了,所有要调用的C++函数都要在java类中声明为native函数,然后使用javah -jni com.example.test.**生成对应的C/C++文件
3、可能会遇到的问题。我用C++代码生成的so总是在安卓手机里运行时出错:找不到对应的native函数。于是我用文本方式打开这个so文件查找这个函数,发现函数的名称变得很奇怪了。学C++的一定会知道的,C代码和C++代码编译后生成的函数名规则是不一样的,C++考虑到函数的重载等会把函数名编译成函数名和参数类型组成的特殊字符串
。我们经常在C++代码写的DLL中在导出函数前面加上 extern "C" ,这就强制编译器将函数以C函数的方式编译。
所以NDK中使用C++生成的导出函数前面一定要加上:
#ifdef __cplusplus
extern "C" {
#endif
//函数声明处
#ifdef __cplusplus
}
#endif
重新编译后,一切OK,安卓程序运行正常。
附上代码
C++代码遍历java传入的一个文件路径中的所有文件,并返回文件数目。
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
#include <string>
#include <vector>
using std::string;
using std::vector;
#include "JniTest1.h"
int MyTestFunction(int x)
{
return x * 2;
}
JNIEXPORT jint JNICALL Java_jni_Test_CalFileCount
(JNIEnv *env, jobject obj, jstring str)
{
char szDir[128];
const char *p = env->GetStringUTFChars(str, NULL);
if (NULL == p)
return 0;
strcpy(szDir, p);
env->ReleaseStringUTFChars(str, p);
printf("String from java : %s", szDir);
vector<string> files;
ListDir(szDir, files);
return files.size();
}
void ListDir(char* pPath, vector<string>& fileList)
{
DIR *pDir = opendir(pPath);
if (NULL == pDir)
return ;
struct dirent *ent;
char szPath[256], szFile[256];
while ((ent = readdir(pDir)) != NULL)
{
if ((strcmp(ent->d_name, ".") == 0) || (strcmp(ent->d_name, "..") == 0))
continue;
sprintf(szPath, "%s/%s", pPath, ent->d_name);
if (ent->d_type & DT_DIR)
{
//递归调用
ListDir(szPath, fileList);
}
else
{//文件
fileList.push_back(szPath);
}
}
closedir(pDir);
}</span>
java代码
package jni;
public class Test {
static{
System.loadLibrary("AndroidProject2-shared");
System.loadLibrary("JniTest1-shared");
}
public native int Add(int m, int b);
public native int CalFileCount(String strDir);
public int GetAdd(int m ,int b){
int sum = Add(m, b);
System.out.println("调用C++函数,返回结果:" + sum);
return sum;
}
public int GetCalFileCount(String strDir){
return CalFileCount(strDir);
}
}
调用部分:
Test t1 = new Test();
int a = t1.GetAdd(100, 100);
String str = "计算结果:" + a;
System.out.println(str);
Toast.makeText(activity, str, Toast.LENGTH_SHORT).show();
String str1 = "/system/lib/egl";
int b = t1.GetCalFileCount(str1);
System.out.println("遍历文件个数:" + b);
运行后DDMS打印结果:
egl文件夹中刚好是8个文件: