欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android JNI开发流程

程序员文章站 2022-06-12 20:43:05
...

很多人想学习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());

此时运行程序即可正确显示字符串,如下图所示:

Android JNI开发流程

代码地址(顺手给个Star啊):点击查看源码

作者:lb377463323
出处:http://blog.csdn.net/lb377463323
原文链接:http://blog.csdn.net/lb377463323/article/details/75112049
转载请注明出处!

相关标签: android jni ndk