NDK JNI--学习笔记
JNI 是java语言提供的Java和C/C++相互沟通的机制,Java可以通过JNI调用本地的C/C++代码,本地的C/C++的代码也可以调用java代码。
JNI 是本地编程接口,Java和C/C++互相通过的接口。Java通过C/C++使用本地的代码的一个关键性原因在于C/C++代码的高效性。
NDK 是一系列工具的集合。它提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。
这些工具对开发者的帮助是巨大的。它集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。它可以自动地将so和Java应用一起打包,极大地减轻了开发人员的打包工作。
Java通过JNI机制和C/C++沟通的具体步骤
1、编写包含native本地方法的java类
2、通过javah工具生成C/C++语言的头文件
3、使用C/C++语言实现头文件
4、使用交叉编译工具对C/C++本地代码进行编译,最后通过链接生成*.so可执行的C/C++库
5、实际执行Java代码去和本地的C/C++代码互相沟通
JNI是Java调用Native机制,是Java语言自己的特性全称为Java Native Interface,类似的还有微软.Net Framework上的p/invoke,可以让C#或Visual Basic.Net可以调用C/C++的API,所以说JNI和Android没有关系,在PC上开发Java的应用,如果运行在Windows平台使用JNI是是经常的,比如说读写Windows的注册表。而NDK是Google公司推出的帮助Android开发者通过C/C++本地语言编写应用的开发包,包含了C/C++的头文件、库文件、说明文档和示例代码,我们可以理解为Windows Platform SDK一样,是纯C/C++编写的,但是Android并不支持纯C/C++编写的应用,同时NDK提供的库和函数功能很有限,仅仅处理些算法效率敏感的问题,所以推荐初学者学好Java后再学习JNI。
简单点说,用C语言生成一个库文件,在java中调用这个库文件的函数。JNI的过程比较复杂,生成.so需要大量操作,而NDK就是简化了这个过程。
---------------------------------------------------------------------------------------------------------------------------------------
1. 首先创建MainActivity,添加native方法:
2. 在main目录下创建一个jni目录,用来放头文件,可以用下面的方法:
3. 编译头文件
在Terminal中进入到项目的根目录,对于Android Studio来说,切换到app\src\main就行,按照网上的说法,直接执行下面的命令:
javah -d jni com.jackie.hellondk.MainActivity(-d jni 指定头文件生成在jni目录下,如果没有第二步创建jni文件夹,也会自动创建)
很明显,找不到MainActivity的.class字节码文件,然后按照官网的方法,指定MainActivity.class的路径,在Android Studio中,所有的.class文件都生成在app\build\intermediates\classes\debug下面,所以,执行下面的命令:
javah -classpath ..\..\build\intermediates\classes\debug -d jni com.jackie.hellondk.MainActivity
这是什么原因呢?这是由于我们上面的MainActivity继承了AppCompatActivity,这个类是在Android v7的兼容包下,所以还需要v7的兼容包加入到classpath中:
javah -classpath D:\DevTools\studio_sdk\extras\android\support\v7\appcompat\libs\android-support-v7-appcompat.jar;..\..\build\intermediates\classes\debug -d jni com.jackie.hellondk.MainActivity
同样,也需要将v4的兼容包加入到classpath中:
javah -classpath D:\DevTools\studio_sdk\extras\android\support\v4\android-support-v4.jar;D:\DevTools\studio_sdk\extras\android\support\v7\appcompat\libs\android-support-v7-appcompat.jar;..\..\build\intermediates\classes\debug -d jni com.jackie.hellondk.MainActivity
无语了,真是处处都是坑啊,继续把android.jar添加到classpath中呗!
javah -classpath D:\DevTools\studio_sdk\platforms\android-23\android.jar;D:\DevTools\studio_sdk\extras\android\support\v4\android-support-v4.jar;D:\DevTools\studio_sdk\extras\android\support\v7\appcompat\libs\android-support-v7-appcompat.jar;..\..\build\intermediates\classes\debug -d jni com.jackie.hellondk.MainActivity终于成功生成了头文件,如下:
4. 实现native方法
在jni目录新建一个hello.c文件,实现头文件中的方法:
5. 编写Android.mk文件
6. 编译动态链接库so,注意要切换到jni所在的目录,执行ndk-build之前,还需要配置环境变量。
7. 引用
编译会出现下面的错误:
在gradle.properties添加下面一句:
android.useDeprecatedNdk=true
继续编译,错误如下:
配置如下:
编译后,安装完成后,还是会出现java.lang.UnsatisfiedLinkError couldn't find libhello.so的错误。
修改build.gradle配置,在defaultConfig里面新增一下代码:
ndk { moduleName"hello"abiFilters"armeabi","armeabi-v7a","x86"}
注意,这里的moduleName一定要和System.loadLibrary以及Android.mk中定义的名称一致。
最后一次运行,终于看到了久违的界面啊!走了很多弯路,真是处处都是坑,做个记录,希望对你们有所帮助。最后说一点,上面用ndk-build来编译生成动态链接库libhello.so,然后在Java中通过loadLibrary来加载。在实际开发过程中,完全可以省去ndk-build这一步,开发好jni程序后,直接运行程序,Android Studio会自动帮我们编译动态链接库,亲测成功!