最近花了一个星期研究NDK,写了一个简要的开发demo,遇到一些问题,在这里简要总结。
JNI是JAVA NATIVE INTERFACE的缩小,它允许Java代码与用其它语言写的代码之间进行交互,例如典型的C或C++语言。Java是上层语言,而C或C++是底层语言(用来实现本地方法),所以JNI是起中间件的作用,将上层(交互层)和下层(驱动层)联系起来。步骤在上层用JAVA申明本地函数,在底层用C或者C++实现本地函数,实现步骤如下。
步骤1.建立native函数,编译生成.class文件
用Java语言,在MainActivity里面建立本地函数 static native String getStringFromC();
编译项目,生成MainActivity.class文件,目录为G:\Android\AndroidStudio\LastPra\app\build\intermediates\classes\debug\com\aiap\lastpra\MainActivity.class,如下图:
步骤2.生产.h头文件
进入android studio自带的终端terminal,用以下命令生成头文件
先进入main文件夹 cd app\src\main
命令 javah –d jni–classpathD:\ProgramFiles\Android\sdk\platforms\android-22\android.jar;D:\ProgramFiles\Android\sdk\extras\android\support\v7\appcompat\libs\android-support-v7-appcompat.jar;D:\ProgramFiles\Android\sdk\extras\android\support\v4\ android-support-v4.jar;G:\Android\AndroidStudio\LastPra\app\build\intermediates\classes\debugcom.aiap.lastpra.MainActivity。在app\src\main\jni文件夹下面生成头文件com_aiap_lastpra_MainActivity.h ,如图
步骤3.编写本地函数
用c(或c++)语言,在jni文件夹下面新建c文件,如getString.c。在getString.c里面实现头文件com_aiap_lastpra_MainActivity.h里面的函数
JNIEXPORTjstring JNICALL Java_com_aiap_lastpra_MainActivity_getStringFromC (JNIEnv *env, jclass c);
这里的代码为
#include"com_aiap_lastpra_MainActivity.h"
JNIEXPORT jstring JNICALL Java_com_aiap_lastpra_MainActivity_getStringFromC
(JNIEnv *env, jclass c){
return(*env)->NewStringUTF(env,"This just a test for Android Studio NDK JNIdeveloper!");
}
步骤4.生产.so库
执行Build>MakeProject,在目录app\build\intermediates\ndk\debug\lib下生成.so库文件,如图
步骤5.调用库文件运行程序
Run app, 结果如下
Successful!
在开发过程中,遇到很多错误,简要记录如下。
Error 1 生成.h文件时,对于javah命令,出现Exceptionin thread “main” java.lang.IllegalArgumentException: not a valid class name,如图
解决方案:报错很奇怪,类名是正确的,但是在前面加上操作 –classpath就可以了,如下,(虽然此时出现了其它错误)。
注意 最后的包名一定是 \debug<package>.<activity>, 如这里是……\debugcom.aiap.jni2.MainActivity
Error 2 无法访问android.app.Activity 找不到android.app.Activity的类文件,如下
解决方案:
在-classpath 里加上android.jar的路径,如 D:\ProgramFiles\Android\sdk\platforms\android-22\android.jar
Error 3无法访问android.support.v7.app.AppCompatActivity,找不到android.support.v7.app.AppCompatActivity的类文件,如下
解决方案:
在-classpath 里加上android-support-v7-appcompat.jar的路径,如
D:\ProgramFiles\Android\sdk\extras\android\support\v7\appcompat\libs\android-support-v7-appcompat.jar
Error 4生成.h文件之后,出现了如下错误。
Error:Executionfailed for task ':jni2:compileDebugNdk'.
> Error: NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin. For details,seehttp://tools.android.com/tech-docs/new-build-system/gradle-experimental. Set "android.useDeprecatedNdk=true"in gradle.properties to continue using the current NDK integration. 如下图:
解决方案:
在gradle.properties中加入语句 android.useDeprecatedNdk=true。 gradle.properties的内容如下
Error 5JNI错误之:error: baseoperand of '->' has non-pointer type '_JNIEnv'
解决方案:
写这个代码时报错:pEnv->NewStringUTF(),是因为c和c++对JNIEnv的定义是不同的,从jni.h下面代码看得出来,解决方案为:
- 使用c++来写代码,文件名就必须.cpp后缀:
C++ codemust have .cpp extension.,必须cpp后缀,c后缀不行;
- 使用c来写代码,文件名就必须.c后缀;
c语音和c++语言的代码不一样,分别如下:
//return(*env)->NewStringUTF(env, "Hello from JNI !");//C格式
//returnenv->NewStringUTF((char *)"Hello from JNI !");//C++格式
Error 6
G:\Android\AndroidStudio\JNItest\jni2\src\main\jni\/com_aiap_jni2_MainActivity.h:2:17: fatal error: jni.h: No such file or directory
解决方案:
将android-ndk-r9d 升级为android-ndk-r10e,将androidstudio1.4升级为androidstudio 1.5
Error 7
写好本地函数后,始终无法生成.so动态库文件。
解决方案:
原来是本地函数所在文件夹的位置和命名的问题。Androidstudio 的NDK编译时,默认本地函数在\src\main\jni\的文件下,如果不在这里,或者文件夹命名不是jni(我之前的命名是jnis),系统就找不到,自然就不能生成so文件。