Android 一天搞定 NDK和JNI编程 3分钟学会写NDK实例
1)安装和下载Cygwin,下载 AndroidNDK
2)在ndk项目中JNI接口的设计
3)使用C/C++实现本地方法
4)JNI生成动态链接库.so文件
5)将动态链接库复制到Java工程,在java工程中调用,运行java工程即可
studio下的NDK操作流程:
NDK开发思路:1、下载NDK
2、创建一个native方法,
3、通过javah生成头文件,
4、改cpp类的代码,
5、编辑c语言 ,
6、添加application.mk文件,
7、运行
按照上述的思路开始一步一步执行
studio配置环境:
Android Studio
2.2
又重新加入了jni
的支持,并且使用的是CMake
,所以现在写jni
不需要配置mk
文件了。
eclipse找那个的 所以就不像以前那样还要安装什么cygwin。
SDK、JDK、NDK的区别
SDK | 软件开发工具包;英语全称:Software Development Kit |
---|---|
JDK | Java语言的软件开发工具包;英语全称:Java Development Kit |
NDK | 原生软件开发工具包;英语全称:Native Development Kit;被Google称为NDK |
由此可见,其实不管什么XDK
,都可以叫SDK
,可能为了有很好的区分,便有了JDK
、NDK
,所以我们有的时候常说的SDK
并不是特指安卓开发工具包
,
而只是我们都是同行,交流的时候都知道指的是什么,其实你们会发现,我们常常接三方平台的时候,那些工具包也是叫SDK
,但可能我们在交流的时候就会加个前缀,比如:微信分享SDK、支付宝SDK、xxSDK。
如果出现如下错误: NDK integration is deprecated in the current plugin. gradle.properties中添加如下配置
使用AndroidStudio开发前我们也要做点额外工作,我们需要在项目根目录下local.properties中添加编译NDK的路径:ndk.dir=/Users/liangqi/android-ndk
//javah -d jni -classpath E:\NdkDemo\app\build\intermediates\classes\debug ndkdemo.peng.cx.com.myapplication.JNIUtils public class JNIUtils { static { System.loadLibrary("huazict"); } public native String getString(); }
输入命令:javah -d jni -classpath 自己编译后的class文件的绝对路径
.h文件生成的目录是更加你的javah指令。
javah -d jni -classpath E:\NdkTest\app\build\intermediates\classes\debug ndkdemo.peng.cx.com.myapplication.JniDemo(注意debug后的空格)
找到类所在的路径:
输入指令之后就自动生成了.h的文件.
把生成的jni目录拷贝到程序的根目录:
<五> 拷贝JNI在正确的目录下 我们在工程中创建一个文件夹jni
,此目录与工程中的java
目录同级,并把生成的*.h
文件放置到jni
文件夹中。
<六>写C语言文件
现在我们来写一个test的C文件huazict.c同.h文件一样放到jni文件夹下,代码如下:
C包含里面的头文件.h文件,就是上面生成的.h文件
#include "ndkdemo_peng_cx_com_myapplication_JNIUtils.h" JNIEXPORT jstring JNICALL Java_ndkdemo_peng_cx_com_myapplication_JNIUtils_getString (JNIEnv *env , jobject obj) { return (*env)->NewStringUTF(env, "这是我测试的jni"); }
最后在构建文件中的默认配置中加上:
ndk { moduleName "huazict" //生成的so名字 abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种abi体系结构下的so库。 }
.so文件的区别
armeabi、armeabi-v7a和x86都表示CPU的类型。一般的手机或平板都是用arm的cpu。
不同的cpu的特性不一样,armeabi就是针对普通的或旧的
arm v5 cpu,armeabi-v7a是针对有浮点运算或高级扩展功能的arm v7 cpu。
mips、armeabi、armeabi-v7a和x86到底是什么
armeabi:默认选项,将创建以基于
ARM* v5TE 的设备为目标的库。 具有这种目标的浮点运算使用软件浮点运算。 使用此 ABI (二进制接口)
创建的二进制代码将可以在所有 ARM*设备上运行。所以armeabi通用性很强。但是速度慢
armeabi-v7a:创建支持基于
ARM* v7 的设备的库,并将使用硬件 FPU 指令。armeabi-v7a是针对有浮点运算或高级扩展功能的arm v7 cpu。
x86:支持基于硬件的浮点运算的
总结:
不同的CPU不用的芯片,armeabi是最基本的,有不同的运算指令,如浮点运算,所以需要生成不同的.so文件
如果项目只包含了 armeabi,那么在所有android设备都可以运行;如果项目只包含了 armeabi-v7a,除armeabi架构的设备外都可以运行;
如果项目只包含了 x86,那么armeabi架构和armeabi-v7a的Android设备是无法运行的;
如果同时包含了 armeabi, armeabi-v7a和x86,
所有设备都可以运行,程序在运行的时候去加载不同平台对应的so,这是较为完美的一种解决方案,同时也会导致包变大。
<七>编译生成SO库
Android Studio
的菜单build
> Rebuild Project
clean 一下,然后rebuild一下,就会生成.so文件,在build/intermediates.ndk下
不是eclipse还要配置android.mk这么文件吗?android studio不用,
你还真猜对了,不过不是说不用就代表他没有,只不过这个配置过程不过你来做,你要做的就是配置上图的代码。那android.mk
<八>建立一个jniLib目录,把so库拷贝进来
运行程序,可以看到效果
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView tv = (TextView) findViewById(R.id.sample_text); tv.setText(new JNIUtils().getString()); } }
总结一下流程:
编写静态方法(用java声明)-->编译生成class文件--->编译生成h文件---->编写C文件(用C/C++实现)
---->配置NDK---->配置so库---->在Activity调用(Java调用C/C++)。
自己总结的流程:
1.下载NDK
2.配置NDK
3.写JAVA类调用C
4.把JAVA生成Class
5.生成.h文件
6.写C语言
7.配置so库并编译so库
=================================================================================
知识拓展
C语言和.h文件解析
.h文件详细解析:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class ndkdemo_peng_cx_com_myapplication_JNIUtils */ #ifndef _Included_ndkdemo_peng_cx_com_myapplication_JNIUtils #define _Included_ndkdemo_peng_cx_com_myapplication_JNIUtils #ifdef __cplusplus extern "C" { #endif /* * Class: ndkdemo_peng_cx_com_myapplication_JNIUtils * Method: getString * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_ndkdemo_peng_cx_com_myapplication_JNIUtils_getString (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
其实 ,我们不用javah
命令
, 也能写出头文件
命名都是有规律 :函数名称规则:Java_完整类名_方法名
, 包名的.号 , 以`_`表示
JNI数据类型对应java的标准数据类型
Java Type | Native Type | Description |
---|---|---|
boolean | jboolean | unsigned 8 bits |
byte | jbyte | signed 8 bits |
char | jchar | unsigned 16 bits |
short | jshort | signed 16 bits |
int | jint | signed 32 bits |
long | jlong | signed 64 bits |
float | jfloat | 32 bits |
double | jdouble | 64 bits |
void | void | not applicable |
作者:逝我
链接:http://www.jianshu.com/p/cba836f6a08c
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
java文件如何编译生成Class文件,先到了.java的目录羡慕,然后用命令 javac 类名 javac JNIUtils.java
javah -jni com.example.binbin.testjni.myJNI
生成头文件;Javah ndktest.peng.cx.com.myapplication.JNIUtils
创建NDK项目(方式二)
- 为什么我生成的C函数参数是(JNIEnv , jobject)而不是(JNIEnv , jclass);
答:这和java代码中对Native函数的声明有关,声明为static,这里的参数就是jclass,即代表该函数所在的类(如HelloJNI.getFromString(),这是jclass接收的是HelloJNI.class)。如果没有声明static,这里的参数就是jobject,表示调用者的实体对象,(如HelloJNI t=new HelloJNI();t.getFromCString(),这里jobject就代表就是t这个对象)。 - 报java.lang.UnsatisfiedLinkError错误;
答:此类错误一般后面有详细的解释,此处列出常见的错误:- could’t find “xxx.so” 此类错误表示找不到对应的so文件,请查看自己的gradle配置是否有ndk标签,函数实现文件要用标准的后缀.cpp或.c,不然gradle编译ndk的时候不会加入编译的。
- Native method not found 此类问题表明你在JAVA中声明的函数没有对应的Native实现,如果已经实现了,请确保执行了System.loadLibary(“xxx.so”);一般来说用javah生成的函数定义是不会出现签名不一致的情况的。
- Can’t load 64-bit .so on 32-bit platform 也就是说不能在32位环境中载入64位的so。一般来说把so移到armeabi-v8a 文件夹下即可。该文件夹存放的是arm64位so文件。
gradlew clean