Android JNI开发流程

很多人想学习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 
/* 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
转载请注明出处!

你可能感兴趣的:(Android,android,jni,ndk)