很多人想学习JNI和NDK,但又不敢学习,觉得这一块内容太难,其实难的不是JNI和NDK,而是C/C++语言,JNI和NDK只是个工具,很容易学习的。
学习JNI之前,首先得先知道JNI、NDK、Java和C/C++之间的关系。在Android开发中,有时为了性能和安全性(反编译),需要使用C/C++语言,但是Android APP层用的是Java语言,怎么才能让这两种语言进行交流呢,因为他们的编码方式是不一样的,这是就需要JNI了。JNI可以被看作是代理模式,Java使用JVM加载并调用JNI来间接调用C/C++代码,也就是Java让JNI代其与C/C++沟通。NDK呢其实主要就是用来将C/C++代码打包编译成.so库的。
搭建NDK环境我就不讲了,其实就是添加NDK的路径而已,下面开始讲解一个简单的jni例子,从native层获取字符串。
第一步,编写native方法
比如在MainActivity中声明如下:
public native String stringFromJNI();
第二步,根据此native方法编写C文件
如果不知道jni中的函数声明怎么写,可以利用javah工具帮我们
在终端中切换到java目录下,然后执行javah命令
F:\Apps\jniDemo\JNIDemo>cd app/src/main/java/
F:\Apps\jniDemo\JNIDemo\app\src\main\java>javah com.lb6905.jnidemo.MainActivity
之后会在java目录下自动生成一个com_lb6905_jnidemo_MainActivity.h文件,如下,stringFromJNI方法名会变成Java_com_lb6905_jnidemo_MainActivity_stringFromJNI。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_lb6905_jnidemo_MainActivity */
#ifndef _Included_com_lb6905_jnidemo_MainActivity
#define _Included_com_lb6905_jnidemo_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_lb6905_jnidemo_MainActivity
* Method: stringFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_lb6905_jnidemo_MainActivity_stringFromJNI
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
这里我们可以注意到jni的取名规则,一般都是包名 + 类名,jni方法只是在前面加上了Java_,并把包名和类名之间的.换成了_
有了这个.h文件文件后,在main目录下新建jni文件夹,把此.h文件拷到jni目录下,我重命名成了hello-jni.c文件(文件名可随意取),看着方便。我们把Java_com_lb6905_jnidemo_MainActivity_stringFromJNI实现一下,如下,C/C++中是没有Java的String类型的,所有我们需要把C/C++的UTF8编码的字符串转换成jstring,这时就需要用到jni的NewStringUTF方法了,至于jni的方法可以在jni.h文件(android-ndk-r13b\platforms\android-24\arch-arm\usr\include\jni.h)中查询。之后JVM会将jstring转成Java的String类型的。
JNIEXPORT jstring Java_com_lb6905_jnidemo_MainActivity_stringFromJNI( JNIEnv* env,
jobject thiz )
{
return (*env)->NewStringUTF(env, "Hello from JNI !");
}
第三步,使用NDK打包成.so库
到这里,jni文件已编写完成,在Android中想使用native方法,需要通过.so库来实现,所有我们需要将jni和C代码打包成.so文件。
这里需要两步:
1,编写Android.mk文件,此文件用来告知NDK打包.so库的规则
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
include $(BUILD_SHARED_LIBRARY)
至于每个变量的定义方式可以参考谷歌文档(https://developer.android.google.cn/ndk/guides/android_mk.html),那里讲的很清楚,还是中文的。
2,使用ndk-build打包.so库
在终端中切换到jni目录下,执行ndk-build命令,如下,我们可以看到这里讲所以平台对应的.so库都打包了,如果我们想只打包特定平台的.so库,还需要编写Application.mk文件,详细参考(https://developer.android.google.cn/ndk/guides/application_mk.html)
F:\Apps\jniDemo\JNIDemo>cd app/src/main/jni
F:\Apps\jniDemo\JNIDemo\app\src\main\jni>ndk-build
[arm64-v8a] Compile : hello-jni <= hello-jni.c
[arm64-v8a] SharedLibrary : libhello-jni.so
[arm64-v8a] Install : libhello-jni.so => libs/arm64-v8a/libhello-jni.so
[x86_64] Compile : hello-jni <= hello-jni.c
[x86_64] SharedLibrary : libhello-jni.so
[x86_64] Install : libhello-jni.so => libs/x86_64/libhello-jni.so
[mips64] Compile : hello-jni <= hello-jni.c
[mips64] SharedLibrary : libhello-jni.so
[mips64] Install : libhello-jni.so => libs/mips64/libhello-jni.so
[armeabi-v7a] Compile thumb : hello-jni <= hello-jni.c
[armeabi-v7a] SharedLibrary : libhello-jni.so
[armeabi-v7a] Install : libhello-jni.so => libs/armeabi-v7a/libhello-jni.so
[armeabi] Compile thumb : hello-jni <= hello-jni.c
[armeabi] SharedLibrary : libhello-jni.so
[armeabi] Install : libhello-jni.so => libs/armeabi/libhello-jni.so
[x86] Compile : hello-jni <= hello-jni.c
[x86] SharedLibrary : libhello-jni.so
[x86] Install : libhello-jni.so => libs/x86/libhello-jni.so
[mips] Compile : hello-jni <= hello-jni.c
[mips] SharedLibrary : libhello-jni.so
[mips] Install : libhello-jni.so => libs/mips/libhello-jni.so
第四步,在Android中使用.so库
现在我们在main/libs目录下会有所有平台的.so文件,因为Android Studio默认的.so库目录是src/main/jniLibs,所以在不更改.so文件目录的情况下,只需要在build.gradle文件中指向main/libs即可,添加如下代码。
android {
......
defaultConfig {
......
sourceSets.main {
jniLibs.srcDir 'src/main/libs'
jni.srcDirs = []
}
}
下面在MainActivity中加载.so库,需要添加如下代码
static {
System.loadLibrary("hello-jni");
}
现在我们可以调用stringFromJNI的native方法了,比如将返回值显示到TextView上,使用如下代码:
mTextView.setText(stringFromJNI());
此时运行程序即可正确显示字符串,如下图所示:
代码地址(顺手给个Star啊):点击查看源码
作者:lb377463323
出处:http://blog.csdn.net/lb377463323
原文链接:http://blog.csdn.net/lb377463323/article/details/75112049
转载请注明出处!