【原创】使用Android NDK开发,初始NDK & JNI

【原创】使用Android NDK开发,初识NDK & JNI

花了一整天时间来爬坑,demo很小但是也遇到了很多问题,写出来希望对还不了解Android NDK & JNI的朋友有一定帮助。

参考资料:
https://www.cnblogs.com/guanmanman/p/6769240.html
https://blog.****.net/xiaozhu0922/article/details/78835144
https://blog.****.net/tluio/article/details/80087567

啥是NDK?啥是JNI?

NDK,相信大家都不陌生,它是Google为便于Android开发提供的一种原生开发集:Native Development Kit,而且也是一个包含API、构建工具、交叉编译、调试器、文档示例等一系列的工具集,可以帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成APK。

与NDK密切相关的另一个词汇则是JNI,它是NDK开发中的枢纽,Java与底层交互绝大多数都是通过它来完成的,那么接下来看看什么是JNI?

JNI:Java Native Interface 也就是java本地接口,它是一个协议,这个协议用来沟通java代码和本地代码(c/c++)。通过这个协议,Java类的某些方法可以使用原生实现,同时让它们可以像普通的Java方法一样被调用和使用,而原生方法也可以使用Java对象,调用和使用Java方法。也就是说,使用JNI这种协议可以实现:java代码调用c/c++代码,而c/c++代码也可以调用java代码。

上面这俩描述要是看不懂,我再用人话解释下: NDK,kit,一个工具;JNI,interface,一个接口,他俩再一起配合使用,可以使java代码和C、C++交互了。

前期工作

创建工程

【原创】使用Android NDK开发,初始NDK & JNI

修改下布局

【原创】使用Android NDK开发,初始NDK & JNI
【原创】使用Android NDK开发,初始NDK & JNI

下载NDK工具

【原创】使用Android NDK开发,初始NDK & JNI
点击SDKManager。
【原创】使用Android NDK开发,初始NDK & JNI
在SDK Tools 里勾选CMake、LLDB、NDK,点击OK。
下好之后,我们去配置一下NDK的路径:
File>Project Structure
【原创】使用Android NDK开发,初始NDK & JNI
点击select:
【原创】使用Android NDK开发,初始NDK & JNI
到这,前期准备工作就完成了。

开始写代码

创建一个JavaToC类

【原创】使用Android NDK开发,初始NDK & JNI
在这里写一个native 方法,方法红了,不用管它,代码可以运行,有强迫症的朋友可以去百度一下怎么去这个红。
我们想要在安卓工程里调用C 的代码,可我们又没有C 的代码,所以接下来我们这么搞:自己生成C的代码,先生成一个头文件。打开终端:
【原创】使用Android NDK开发,初始NDK & JNI
cd进入到你的工程里,一直cd到java文件夹:
【原创】使用Android NDK开发,初始NDK & JNI
使用javah 命令,创建头文件,照我下面这个来
【原创】使用Android NDK开发,初始NDK & JNI
这时,部分小伙伴可能会遇到第一个坑,终端会提示“javah” 不是内部命令或外部命令咋咋地的,遇见这个问题的小伙伴,请移步https://blog.****.net/tluio/article/details/80087567 完美解决!没解决的,我也不知道咋办。
javah 命令回车,成功的,会自动在同包下生成出一个.h文件,即头文件。
【原创】使用Android NDK开发,初始NDK & JNI
我们创建一个jni的包,把这个头文件挪进来。
【原创】使用Android NDK开发,初始NDK & JNI
头文件有了,想执行完整的C代码,我们还需要一个.c文件,跟着我继续往下做:
在新建的这个jni包下,创建一个.c文件:
【原创】使用Android NDK开发,初始NDK & JNI
【原创】使用Android NDK开发,初始NDK & JNI
这个C 文件我取名叫JavaToC ,type是c 不是cpp。

创建好.c文件之后,我们把刚才.h文件里的方法粘贴过来,照我写就行:
复制这里
【原创】使用Android NDK开发,初始NDK & JNI
粘贴到这里:
【原创】使用Android NDK开发,初始NDK & JNI
写好之后,下一步我们要生成so文件了,我们移步build.gradle
在defaultConfig里添加:
【原创】使用Android NDK开发,初始NDK & JNI
到此,网上有的帖子说Build==>Rebuild Project 就可以生成 so 文件了,OK那么我们来试一下:
【原创】使用Android NDK开发,初始NDK & JNI
so文件生成失败了,提示我们使用CMake 或者 ndk-build来干这件事,那么我们就使用CMake,CMake也是一个工具,我们前面在做准备工作的时候,在SDKManeger里已经下好了,那么跟着我继续往下来写,回到build.gradle中:

在build.gradle的defaultConfig节点下加入:
externalNativeBuild {
cmake {
cppFlags “”
abiFilters “arm64-v8a”,“armeabi-v7a”,“x86”,“x86_64”
}
}
在build.gradle的android节点下加入:
// 配置CMakeLists.txt路径
externalNativeBuild {
cmake {
path “CMakeLists.txt” //编译后so文件的名字
}
}
【原创】使用Android NDK开发,初始NDK & JNI
添加完毕后,让我们重新编译一下,sync,好的,又提示我们:
【原创】使用Android NDK开发,初始NDK & JNI
CMakeLists 是so文件编译后的名字,下面我们手动创建一个CMakeLists.txt文件:
【原创】使用Android NDK开发,初始NDK & JNI
【原创】使用Android NDK开发,初始NDK & JNI
点击OK,创建成功,就是这个彩色小三角!
【原创】使用Android NDK开发,初始NDK & JNI
CMakeLists我之前见过,在创建工程的时候,如果勾选了include C++ support的话,就会自动给我们生成好一个CMakeLists,朋友们可以去试一下,创建一个新的工程,勾选include C++ support,完后找到CMakeLists,复制粘贴里面的代码过来。当然你们也可以粘我的:

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
             JavaToC

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/jni/JavaToC.c )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

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 )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
                       JavaToC

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

大功即将告成!
在我们创建的JavaToC类中加载so库,主要是为了在我们调用本地方法之前先编译本地源码。
【原创】使用Android NDK开发,初始NDK & JNI
最后一步!rebuild project,生成so文件:
【原创】使用Android NDK开发,初始NDK & JNI
【原创】使用Android NDK开发,初始NDK & JNI
rebuild之后,so文件创建成功!
赶紧试一下:
【原创】使用Android NDK开发,初始NDK & JNI
运行:
【原创】使用Android NDK开发,初始NDK & JNI
点击Click按钮:
【原创】使用Android NDK开发,初始NDK & JNI

总结

到这就算是小成功了,我们利用一个java文件生成了一个C的头文件,再根据头文件创建了.c文件,再用CMake 生成CMakeLists.txt,从而生成了so 文件,最后在我们的安卓程序里调用了C的方法吐司了一下。里面具体是怎么回事我还没有搞清楚,下回再更新,欢迎大家留言补充。