最近项目中需要使用到NDK编译so库,控制硬件。由于不熟悉JNI和NDK,网上搜了一些方法,做个笔记备忘和分享给用需要的朋友。
那如何用Android Studio编写JNI代码,然后生成so库呢?
来看看一个简单的实例: 编写一个native的加法函数,然后在MainActivity中调用并显示结果。
需要准备的事:
1 配置好的JDK环境和Android Studio开发环境、SDK
2 下载好NDK工具 (http://www.androiddevtools.cn/ 网站有很多Android开发需要的工具。)
2 添加一个类,类中包含你想用C/C++语言实现的native方法,只是声明。
MyJni.java
package com.example.hui.myjnitest ;
public class MyJni {
static {
System.loadLibrary ("myjni" ) ;
}
public native int add (int a , int b) ;
}
其中有个静态代码块,在MyJni类加载时,执行块内代码。
System.loadLibrary (“myjni” ) ; 程序运行时,加载库文件,在Android(Linux)中,会搜索并加载名字为libmyjni.so的文件,如果找不到,会报异常。在Windows中会搜索myjni.dll的库
3 调用native方法
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate (Bundle savedInstanceState) {
super .onCreate(savedInstanceState) ;
setContentView(R.layout. activity_main );
MyJni myJni = new MyJni();
Toast. makeText( this, "1+3=" + myJni.add( 1 , 3 ) , Toast. LENGTH_LONG).show() ;
}
}
MyJni myJni = new MyJni(); 获得具有native方法的类对象,然后调用该对象的方法myJni.add( 1 , 3)计算1+3的结果。
4 编译工程
Build–Make Project
5 生成jni的头文件
打开Android Studio的Terminal编辑器,一般在软件底部,如果找不到,可以按快捷键 Alt+F12 打开 (我的AS版本:2.1.1)
切换到classes所在目录:
用到的命令: cd 切换路径, dir 显示文件夹内容列表
cd app\build\intermediates\classes\debug
debug目录下的路径跟包名有关,比如本例中的AndroidManifest.xml中声明的包名为
package= "com.example.hui.myjnitest"
那本例中的”.java”文件生成的.class”就在 debug\com\example\hui\myjnitest\下 (切换到该目录下看看)
2016/06/04 19:37 732 BuildConfig.class
2016/06/04 19:37 1,056 MainActivity.class
2016/06/04 19:37 454 MyJni.class
17 个文件 76,243 字节
2 个目录 144,080,855,040 可用字节
E:\MyGit\MyJniTest\app\build\intermediates\classes\debug\com\example\hui\myjnitest>
我们只用到MyJni.class文件。
好了,了解这些后,我们应该生成头文件了。(生成头文件干嘛?后面你就知道了)
在app\build\intermediates\classes\debug目录下执行
javah -classpath . -jni com.example.hui.myjnitest.MyJni
javah 用于生成头文件(*.h) (我暂时只了解这个用途)
-classpath . 有个圆点。表示设置class路径为当前路径。如果不在debug这个路径,那这个圆点可以换成具体路径
-jni 生成 JNI 样式的标头文件 (默认值)
com.example.hui.myjnitest.MyJni 包名+类名,注意不要带 .java,否则会报错的。
执行完命令,Terminal没有任何提示,在debug目录下,输入dir命令,可以看到有个 .h 文件,名字好长,其实就是把上面包名+类名中间的点换成下划线了。
来看看这个头文件都写了什么
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_example_hui_myjnitest_MyJni */
#ifndef _Included_com_example_hui_myjnitest_MyJni
#define _Included_com_example_hui_myjnitest_MyJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_hui_myjnitest_MyJni
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_hui_myjnitest_MyJni_add
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
这其中的 Java_com_example_hui_myjnitest_MyJni_add 就是我们需要实现的native方法。
其他部分我就不多说了,可以百度一下,google一下。。。C语言的知识。
6 新建一个名为jni的文件夹
在工程目录app\src\main\下,新建一个jni文件夹,把上面步骤的h头文件复制到jni文件夹里。
工程目录更新为:
在jni文件夹中添加一个c语言源代码文件,如myjni.c,并完成其内容。函数名从头文件中复制过来,稍作修改。(知道头文件的作用了吧)
#include "com_example_hui_myjnitest_MyJni.h"
jint JNICALL Java_com_example_hui_myjnitest_MyJni_add
(JNIEnv *env, jobject obj , jint a , jint b)
{
return a+b ;
}
7 再次编译工程
Build – Project
这时 Message 窗口会提示需要设置使用当前NDK。打开gradle.properties, 在最下面粘贴上 android.useDeprecatedNdk=true
Error:Execution failed for task ‘:app:compileDebugNdk’.
Error: NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin. For details, see http://tools.android.com/tech-docs/new-build-system/gradle-experimental. Set “android.useDeprecatedNdk=true” in gradle.properties to continue using the current NDK integration.
8 配置NDK
Android Studio中,菜单 File – Project Structure SDK Location 选择NDK路径
在build.gradle(Module:app)中完成
android{
defaultConfig {
ndk {
moduleName "xxx" // 目标名称,不包括lib .so
ldLibs "xx","xx" // jni代码中引用的库文件,本例没有引用
abiFilters "", "", "", "" // 生成不同平台下的so库
}
}
}
9 再次Build–Rebuild Project
没有报错则生成了so库。这个so库路径在
app\build\intermediates\ndk\debug\lib
10 生成APK,用解压工具查看,apk内部有个lib文件夹,如下图所示。
11 在模拟器中安装并运行。
Toast显示的是正确调用so库运行的结果。
代码地址:https://github.com/BrightLin/MyJniTest
参考播客地址:http://blog.csdn.net/sodino/article/details/41946607 感谢作者 Sodino
提示:
如果编译时报错:需要在jni文件夹中添加一个空文件,名字为util.c
Error:Execution failed for task ‘:app:compileDebugNdk’. > com.android.ide.common.internal.LoggedErrorException: Failed to run command: D:\Mission\adt-bundle-windows\ndk-r10b\ndk-build.cmd NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\Android.mk APP_PLATFORM=android-21 NDK_OUT=C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\obj NDK_LIBS_OUT=C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\lib APP_ABI=armeabi,armeabi-v7a,x86 Error Code: 2 Output: make.exe: * No rule to make target C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\obj/local/armeabi/objs/JniTest/C_\Users\sodinochen\AndroidstudioProjects\JniTest2\app\src\main\jni', needed by
C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\obj/local/armeabi/objs/JniTest/C_\Users\sodinochen\AndroidstudioProjects\JniTest2\app\src\main\jni\main.o’. Stop.