美文网首页android大牛聚集之地Android-NDK/JNI
AndroidStudio JNI详细教程+Gradle3.0以

AndroidStudio JNI详细教程+Gradle3.0以

作者: 小五666 | 来源:发表于2017-12-03 18:21 被阅读727次

    “Android开发者社区”微信群期待各位加入,我们一起“抠腚”,一起进步,一起探讨技术……
    微信ID:393795397
    掘金主页:Android开发者社区

    Demo下载地址:点击下载Demo

    1.首先什么是JNI呢?

    JNI——(Java Native Interface),他是java平台的特性,不是安卓系统提供的。他定义了一些JNI函数,来让开发者可以通过调用这些函数来实现java代码调用C/C++代码。

    2.如何使用JNI呢?

    我们先将写好的C/C++代码编译成对应平台的动态库(windows是.dll文件,linux是.so文件)。

    下面我们来举个栗子:使用AndroidStudio来实现JNI

    3.要实现JNI先下载NDK,那么NDK又是什么呢?(面试宝典来了,赶紧掏出小本本)

    • NDK是一系列工具的集合
    • NDK提供了一份稳定、功能有限的API头文件声明
    • NDK的发布,使“Java+C”的开发方式终于转正,成为官方支持的开发方式
    • NDK将使Android平台支持C开发的开端
      好,那接下来我们来下载NDK,有俩种方式:
    • Google官方下载NDK:点击下载
    • 通过SDKManager来下载NDK:


      image.png

    4.下来我们new一个新工程:这个工程只包含一个MainActivity

    image.png

    5.我们来检查一下NDK下载好了没有,怎么检查呢?如下:

    • 检查SDK Location里面的NDK路径:


      image.png
    • 检查local.properties文件里面有没有NDK路径:


      image.png

    6.下来我们要编写JNI接口啦,如下:

    JNI接口需要用native关键字修饰,我们会看到方法名报红,没关系,我们继续


    image.png

    7.我们先build一下工程,检查myJNIUtils.java编译后有没有生成class文件,在这个位置下:

    AndroidJNITest/app/build/intermediates/classes/debug/com/kissdream/androidjnitest/myJNIUtils.class

    image.png

    8.使用javah生成.h头文件,具体如下:

    • 打开Terminal,输入命令进入到debug目录下,命令如下:
      cd/Users/apple/Desktop/AndroidJNITest/app/build/intermediates/classes/debug
    • 然后使用javah+包名+文件路径来生成头文件,命令如下:
      javah com.kissdream.androidjnitest.myJNIUtils
      image.png
    • 检查头文件有没有生成:
      我们发现这个路径下多了个.h文件AndroidJNITest/app/build/intermediates/classes/debug/com/kissdream
      哈哈,没错这个就是我们要生成的头文件
      image.png

    9.生成了.h文件还不行,只是声明了方法,我们还需要去实现它,那么如何去实现他呢,如下:

    -我们在main下新建一个jni文件夹,如图:


    image.png
    image.png

    .h文件内容如下:

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_kissdream_androidjnitest_myJNIUtils */
    
    #ifndef _Included_com_kissdream_androidjnitest_myJNIUtils
    #define _Included_com_kissdream_androidjnitest_myJNIUtils
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     com_kissdream_androidjnitest_myJNIUtils
     * Method:    getName
     * Signature: ()Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_com_kissdream_androidjnitest_myJNIUtils_getName
      (JNIEnv *, jobject);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    
    
    • 把生成的.h文件拷贝到jni文件夹下
    • 在jni文件夹下,新建一个.c(c语言)或者.cpp(c++)的文件,来实现.h文件里声明的方法:
      把.h文件里面声明的方法拷贝到新建的c++文件里面,然后在文件里面引入.h文件:
      引入.h文件#include "com_kissdream_androidjnitest_myJNIUtils.h"
    #include "com_kissdream_androidjnitest_myJNIUtils.h"
    JNIEXPORT jstring JNICALL Java_com_kissdream_androidjnitest_myJNIUtils_getName
            (JNIEnv * env, jobject obj){
    //如果是用C语言格式就用这种方式
    //    return (*env)->NewStringUTF(env,"Kiss dream");
        //如果是用C语言格式就用这种方式
    return env->NewStringUTF((char *)"Kiss dream");
    }
    
    image.png

    到这里我们的方法就实现完毕了

    10.方法我们实现了,但是我们如何调用呢,不要着急,Follow me:

    • 首先引入动态库:
    public class myJNIUtils {
        static {
            //名字注意,需要跟你的build.gradle ndk节点下面的名字一样
            System.loadLibrary("NameProvider");
        }
        //JNI接口需要用native关键字修饰
        public native String getName();
    }
    

    NameProvider就是你要生成d的.so文件的文件名

    • 下面我们来调用它


      image.png

    11.最重要的一步来了,生成so文件:

    这个小编也不会,于是就去百度了下,得到结果:

    • 在根目录gradle.properties下面加上:
      android.useDeprecatedNdk=true意思就是允许使用低版本的NDK
    • 在module下面的build.gradle下面加上ndk节点如下图:
    ndk {
            moduleName "NameProvider"
        }
    

    NameProvider注意这个名字要跟你引入动态库的名字一样
    需要这俩步就可以运行生成so文件了
    然儿,并没有想象的那么顺利,报错了,我顿时心中飞过一万只草泥玛,上log:

    Error:Execution failed for task ':app:compileDebugNdk'.
    > Error: Flag android.useDeprecatedNdk is no longer supported and will be removed in the next version of Android Studio.  Please switch to a supported build system.
      Consider using CMake or ndk-build integration. For more information, go to:
       https://d.android.com/r/studio-ui/add-native-code.html#ndkCompile
       To get started, you can use the sample ndk-build script the Android
       plugin generated for you at:
       /Users/apple/Desktop/AndroidJNITest/app/build/intermediates/ndk/debug/Android.mk
      Alternatively, you can use the experimental plugin:
       https://developer.android.com/r/tools/experimental-plugin.html
      To continue using the deprecated NDK compile for another 60 days, set 
      android.deprecatedNdkCompileLease=1512283120054 in gradle.properties
    

    百思不得其姐啊,百度的答案大家都是这样做啊,为什么人家可以我的就不行呢,我的代码和他的一模一样啊
    为什么人家可以我的就不行呢,我的代码和他的一模一样啊这句话作为程序员的我们很熟悉!难到我要放弃吗?no no no,作为程序员的我怎么能轻言放弃呢!每个人都有这样的经历,蓝瘦过、香菇过,到最后我们都找到我们的错误
    来我们仔细看下Log,大概意思就是说:

    • android.useDeprecatedNdk不再支持了

    • 让使用CMake or ndk-build

    • 然后还有链接
      考虑使用CMake或ndk构建集成。要了解更多信息,请访问:
      https://d.android.com/r/studio-ui/add-native-code.html#ndkCompile
      首先,您可以使用Android的ndk构建脚本示例插件为您生成:
      /Users/apple/Desktop/AndroidJNITest/app/build/intermediates/ndk/debug/Android.mk
      或者,你可以使用实验插件:
      https://developer.android.com/r/tools/experimental-plugin.html
      继续使用已弃用的NDK编译60天,设置
      在gradle.properties
      android.deprecatedNdkCompileLease = 1512283120054(这个测试不起作用)
      经过各种查资料,发现原来在gradle3.0以上以前这种方法不在支持
      学习过程就不详细描述了,直接上结果:

    • 先通过SDKManager下载:CMake和LLDB


      image.png
    • 在build.gradle的defaultConfig节点下加入:

    // 使用Cmake工具
            externalNativeBuild {
                cmake {
                    cppFlags ""
                    //生成多个版本的so文件
                    abiFilters 'arm64-v8a','armeabi-v7a','x86','x86_64'
                }
            }
    
    • 在build.gradle的android节点下加入:
    // 配置CMakeLists.txt路径
        externalNativeBuild {
            cmake {
                path "CMakeLists.txt"   // 设置所要编写的c源码位置,以及编译后so文件的名字
            }
        }
    
    image.png
    • 添加CMakeLists.txt文件到build.gradle文件同级目录下,具体内容如下:
    # 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.
    #CMakeLists.txt
    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.
                # 设置so文件名称.
                 NameProvider
    
                 # Sets the library as a shared library.
                 SHARED
                 # 设置这个so文件为共享.
    
                 # Provides a relative path to your source file(s).
                 # 设置这个so文件为共享.
                 src/main/jni/getName.cpp)
    
    # 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.
                           # 制定目标库.
                           NameProvider
    
                           # Links the target library to the log library
                           # included in the NDK.
                           ${log-lib} )
    
    image.png
    至此,我们所有的流程都做完了,下面来检查一下我们的成果,见证奇迹的时候到了:
    image.png
    可以看到我们已经成功生成so文件,再来上个效果图:
    image.png
    下载地址:点击下载Demo

    当然,实现JNI有更简单的方法,可以一键集成,省去了上面的好多步骤,但是作为一名开发者,我们要知道开发工具为我们做了什么……不要做一名只会吃技术快餐的程序员,要了解背后的原理,只是会使用,不了解原理的程序员不是一名好厨师……

    相关文章

      网友评论

      • 49dc8bf039f2:写的很详细,想问一下CMakeLists.txt这个文件都是自己手动建的吗?还有自己测了下里面的文件名称和目标库名称可以和静态代码块中的名称不一样,但是那个分享的必须要写吗?
        小五666:@leisure1107 有更简单的办法一键集成,创建工程的时候可以选中support c/c++
      • Versol:写的真好,给了我很大的帮助
      • 我是谁之从心开始:写的很好,希望作者以后多发👍 👍 👍

      本文标题:AndroidStudio JNI详细教程+Gradle3.0以

      本文链接:https://www.haomeiwen.com/subject/syeabxtx.html