android开发-NDK-JNI入门教程
1.简介
NDK:
- Native Development Kit ,
Android
的一个工具开发包,NDK是属于Android
的,与java
并无直接关系
作用:
- 快速开发
C
、C++
的动态库,并自动将.so
和应用一起打包成APK
- 通过
NDK
在Android
中 使用JNI
与本地代码(如C、C++)交互
NDK与JNI的关系:
jni是实现最终目的 , 而 ndk 是实现 jni的功能调用。
JNI
是java native interface , java本地接口。如果java代码需要调用底层的c/c++代码,就需要通过jni来实现。android的底层是linux,linux之上是c/c++代码,而我们app是java代码。有些需要高效率的事情,比如音视频编解码,比如3d绘图等就需要用c/c++来实现了。
JNI实现步骤
- 在Java代码中声明Native方法(即需要调用的本地方法)
- 编译上述 Java源文件javac(得到 .class文件)
- 通过 javah 命令对.class文件导出JNI的头文件(.h文件)
- 使用.c文件引入.h头文件 并且实现在 Java代码中声明的Native方法
- 如 Java 需要与 C++ 交互,那么就用C++实现 Java的Native方法
- 配置app模块的 build.gradle (ndk配置产生的.so的名字,不同的系统)
- sync project
- 运行程序
2.步骤详解
以下操作都是在AndroidStudio中进行。
1.首先要准备一个ndk,不要从androidstudio里面直接下载,具体怎么下载配置看另一篇文章:
https://blog.csdn.net/qq_38261174/article/details/83210458 这里讲了如何下载及配置ndk。
然后在androidstudio中配置ndk:
2.androidstudio新建一个工程,不引入c++ Support。
我建的工程名字是 MyJniStudy
包名是 com.liuyan.myjnistudy
3.没有在androidstudio中配置ndk的先按照上面图片配置ndk。
4.在 main 目录下新建一个JNI目录。
上面图片中没有打钩的,别慌,可以在 build.gralde中配置:
apply plugin: 'com.android.application'android { ... defaultConfig { ... } buildTypes { ... }//这里配置 sourceSets { main { jni.srcDirs = ['src/main/jni', 'src/main/jni/'] } }}dependencies { ...}
5. 在java目录下,新建一个包
我的包名是 my
6. 在 my包下面,新建一个java文件,在java文件中写一个 native方法。
我的java文件名字是 NdkJniTest
方法名字是 mytest
package my;public class NdkJniTest { //要先加载so库,才能调用native方法 //so库的名字(NdkJniSoName) //下面步骤会在 build.gralde中配置so库的名字是这个 static { System.loadLibrary("NdkJniSoName"); } public native String mytest();}
方法名字是红色的,不要管,接下来可能方法一直是红色的,不要管,按照步骤来就行了。
7. cmd命令 进入到 my包
执行命令 : javac NdkJniTest.java (编译 .java 产生 .class)
这里我产生了一个错误:
D:\androidstudio-xiangmu\MyJniStudy\app\src\main\java\my>javac NdkJniTest.java
NdkJniTest.java:7: 错误: 编码GBK的不可映射字符
//涓嬮潰姝ラ浼氬湪 build.gralde涓厤缃畇o搴撶殑鍚嶅瓧鏄繖涓?
^
1 个错误
我把注释全删 ,然后重新执行一次命令就好了。
这时会在my包下面多出一个文件
8.cmd命令进入到java 目录,不进入任何包里面
执行命令 javah -jni my.NdkJniTest ( .class产生 .h文件)
这时会在java目录下产生一个 my_NdkJniTest.h 文件。
9.将 .h 文件移动到 jni文件夹下面。
.h 文件内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */#include /* Header for class my_NdkJniTest */#ifndef _Included_my_NdkJniTest#define _Included_my_NdkJniTest#ifdef __cplusplusextern "C" {#endif/* * Class: my_NdkJniTest * Method: mytest * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_my_NdkJniTest_mytest (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif
10.在 jni 文件夹下面新建一个 . C 文件 ,不是 .cpp文件
名字自己取。
我的 c文件名字是 jnitest.c
创建好 .c文件之后,编写这个 .c文件。
.c文件 要引入之前的 .h头文件,并实现头文件其中的方法。
我的 .c文件其中的代码:
#include "my_NdkJniTest.h"JNIEXPORT jstring JNICALL Java_my_NdkJniTest_mytest (JNIEnv *env, jobject obj) { return (*env)->NewStringUTF(env, "This is my Jni test!!!");}
11.到这一步,库文件和 jni接口文件都已经在上面全部准备好了
接下来
配置 gradle.properties 文件,在末尾加入:
android.useDeprecatedNdk=true
配置ndk 修改app模块的 build.gradle文件,如下:
apply plugin: 'com.android.application'android { ... defaultConfig { ... ndk { moduleName "NdkJniSoName" //生成的so名字 abiFilters "armeabi", "armeabi-v7a", "x86"//输出指定三种abi体系结构下的so库。 } } buildTypes { ... } sourceSets { main { jni.srcDirs = ['src/main/jni', 'src/main/jni/'] } }}dependencies { ...}
12.所有准备工作都做好了
sync project
13.开始使用
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); NdkJniTest ndkJniTest = new NdkJniTest(); String s = ndkJniTest.mytest(); //native方法 Log.i("TAG",s); }}
14.结果,打印如下:
15.是不是还有一个疑惑?
我们产生的.so库在哪儿呢?自动生成的哦!!!
想要了解更多的,可以自己研究下 Android.mk文件,其实就是我们在 build.gradle 中配置的 ndk。
3. 过瘾
按照上面的流程,相信你已经成功了!
是不是还不过瘾?好,我们接着来
我们在 NdkJniTest.java 中,再加入一个 native 方法。
package my;public class NdkJniTest { static { System.loadLibrary("NdkJniSoName"); } public native String mytest(); //新加入的方法 public native int AddAB(int a , int b);}
这个native方法将要 实现两数之和 ,并将这个和返回。
重新 生成 .class 文件,重新生成 .h文件。
如果你不想要重新生成,你可以自己直接修改 .h文件,在其中加入:
JNIEXPORT jint JNICALL Java_my_NdkJniTest_AddAB (JNIEnv *, jobject, jint, jint);
如果你的包名和我的不一样,自己根据情况修改上面的代码内容。
所以现在的.h文件所有内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */#include /* Header for class my_NdkJniTest */#ifndef _Included_my_NdkJniTest#define _Included_my_NdkJniTest#ifdef __cplusplusextern "C" {#endif/* * Class: my_NdkJniTest * Method: mytest * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_my_NdkJniTest_mytest (JNIEnv *, jobject);JNIEXPORT jint JNICALL Java_my_NdkJniTest_AddAB (JNIEnv *, jobject, jint, jint);#ifdef __cplusplus}#endif#endif
编写 .c文件,一定要引入 .h头文件。
之前我们实现了头文件的方法,新加入的方法还没有实现,所以我们要去实现新加入的方法。
所以.c文件所有内容如下:
#include "my_NdkJniTest.h"JNIEXPORT jstring JNICALL Java_my_NdkJniTest_mytest (JNIEnv *env, jobject obj) { return (*env)->NewStringUTF(env, "This is my Jni test!!!");}//实现新的方法JNIEXPORT jint JNICALL Java_my_NdkJniTest_AddAB (JNIEnv *env, jobject obj, jint a, jint b) { return (a+b);}
sync project
开始使用新方法,并查看结果:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); NdkJniTest ndkJniTest = new NdkJniTest(); String s = ndkJniTest.mytest(); //native方法 Log.i("TAG",s); //测试新方法 int result = ndkJniTest.AddAB(2018,10); Log.i("TAG",""+result); }}
到此为止了!!!
更多相关文章
- Android(安卓)View 绘制刷新流程分析
- Android(安卓)使用Scroller实现绚丽的ListView左右滑动删除Item
- Android学习中遇到的优秀文章的总结(持续更新)
- Android(安卓)项目组件化之创建module,生成aar,引入aar
- 【Android】AsyncTask源码分析
- Android(安卓)Webview组件使用总结
- Android之预览PDF文件
- Android(安卓)studio如何指定使用自己生成的keystore调试
- Android(安卓)中解决Viewpage调用notifyDataSetChanged()时界面