NDK学习(一)向现有安卓项目加入jni

ε祈祈猫儿з 提交于 2020-02-11 20:06:07

做安卓半年了,用的都是java,目前老大分配了一个任务,在现有项目上添加图片的滤镜功能,使用滤镜一般计算量比较大,需要用到NDK,也就是C相关的知识,虽然之前学过C/C++,但还没用到过项目中,尤其还要用到安卓中,这一篇算是为NDK学习开个头。

如何使得现有项目支持NDK呢?
首先就是要下载NDK相关的工具链,然后加入C文件。
1、首先我们在MainActivity中假如一个getNdkText的native方法,并且在onCreate方法中调用

public native String getNdkText();

然后就要在c文件中实现这个方法了
2、工程目录切到project,在app模块下新建一个cpp文件夹,用于放置C文件
在这里插入图片描述
3、然后在cpp文件夹下面新建自己的c文件,命名为hello.cpp
hello.cpp内容如下:

#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_cn_hzw_ndk_1learn_MainActivity_getNdkText(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

在这里就实现了getNdkText方法了,但是为什么名称发生了改变?这是因为要使得MainActivity中的getNdkText方法要唯一的对应到上面c中的函数,具体的对应方法就是Java+类包名+方法名,其中包名不是以.分割的,而是下划线_,另外如果原始包名中如果有下划线的话,那么就会把下划线转化成数字,我写的这个例子原始包名为cn.hzw.ndk_learn.MainActivity
一般来说可以使用代码提示自动生成c函数,但是在第一次加入时是没有提示的,需要手动写。

4、加入CMakeLists.txt文件,在模块目录下面,这里就是在app目录下加入这个文件

cmake_minimum_required(VERSION 3.4.1)

add_library( # Sets the name of the library.设置库的名称
             hello

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).包含需要编译的文件
             src/main/cpp/hello.cpp )

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

target_link_libraries( # Specifies the target library.
                       hello

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

5、在build.gradle文件中加入支持ndk编译的选项,在android的defaultConfig节区下面加入

externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }

在android节区下面加入

externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }

6、加载so库,因为编译出来的so库是不会随JVM自动加入的,而是要我们手动加载,并且是在调用native方法之前加载,因此就在MainActivity最前面的static语句块中载入:

static {
        System.loadLibrary("hello");
}

到此,就应该可以运行起来了。

最后解释一下jni函数的声明含义

extern "C" JNIEXPORT jstring JNICALL

extern “C”的含义是这个函数使用C编译器的方式去编译,因为C和C++在对函数编译时会采取不一样的方式,使用C编译器编译,得到最终的so库中的函数表中的名称就是源代码中的名称,但是因为C++支持重载,因此C++在链接函数的时候使用的是函数签名,最终so库中的名称就会和源代码中的不一样,那么外部在链接的时候,可能就会出现找不到的情况,因此使用这么一个字符串,使得C和C++可以使用同一套代码。

JNIEXPORT 和 JNICALL都是给编译器看的,前者和生成动态库有关,后者和调用约定有关系,因为在不同的平台函数参数的顺序可能会不一样,具体可参考https://blog.csdn.net/shulianghan/article/details/104072587
这篇文章。

jstring是c层对应的java层字符串的一个类型表示,具体后面再讲。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!