美文网首页
Android:通过CMake方式生成动态库so文件

Android:通过CMake方式生成动态库so文件

作者: 绿茵场上的码者 | 来源:发表于2019-05-30 14:32 被阅读0次

    1、前言

    Java JNI的本意是Java Native Interface(Java本地接口),它是为了方便Java调用CC++等本地代码所封装的一层接口。通过Java JNI,用户可以调用用CC++所编写的本地代码
    NDKAndroid所提供的一个工具集合,通过NDK可以在Android中更加方便地通过JNI来访问本地代码。

    2、优势

    • 提高代码的安全性。由于so库反编译比较困难,因为NDK提高了Android程序的安全性。
    • 可以很方便地使用目前已有的C/C++开源库
    • 便于平台间的移植。
    • 提高程序在某些特定情形下的执行效率,但是并不能提升Android程序性能

    注:JNINDK开发所用到的动态库的格式是以.so为后缀的文件,JNINDK主要用于底层和嵌入式开发,在Android应用层开发中使用比较少。

    3、JNI开发流程

    3.1、在Android Studio配置NDK环境

    打开SDKManager-tools下载NDK插件,下载后到SDK Location里面检查里面的NDK路径

    3.2、在Java中声明native方法**

    创建一个类,叫做JNITest.java

    package com.qinkl;
    public class JNITest{
        static{
            System.loadLibrary("jni-test");
        }
    
        public static void main(String args[]){
            JNITest jniTest = new JNITest();
            System.out.println(jniTest.jniGet());
            jniTest.jniSet("hello world");
        }
    
        public native String jniGet();
        public native void jniSet(String str);
    
    }
    

    可以看到上面的代码中,声明了两个native方法:jniGetjniSet,这两个就是需要在JNI中实现的方法。在JniTest的头部有一个加载动态库的静态代码块,其中jni-testso库的标识,so库完整的名称为libjni-test.so,这是加载so库的规范

    3.3、编译Java源文件得到class文件,然后通过javah命令导出JNI的头文件

    进入cmd,(cd /d 任意目录),选择进入项目文件目录,具体的命令为:

    javac com/qinkl/JNITest.java
    javah com.qinkl.JNITest
    

    JDK10以前用:javah com.qinkl.JNITest
    JDK10以后是没有提供javah的,要用javac代替javah命令:进入cmd,切换到文件所在的根目录,执行命令javac -encoding utf8 -h . JNITest.java

    在当前目录下,会产生com_qinkl_JNITest.h的头文件,它是上述命令生成的,内容如下:

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_qinkl_JNITest */
    
    #ifndef _Included_com_qinkl_JNITest
    #define _Included_com_qinkl_JNITest
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     com_qinkl_JNITest
     * Method:    jniGet
     * Signature: ()Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_com_qinkl_JNITest_jniGet
      (JNIEnv *, jobject);
    
    /*
     * Class:     com_qinkl_JNITest
     * Method:    jniSet
     * Signature: (Ljava/lang/String;)V
     */
    JNIEXPORT void JNICALL Java_com_qinkl_JNITest_jniSet
      (JNIEnv *, jobject, jstring);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    

    JNIEnv*:表示一个指向JNI环境的指针,可以通过它来访问JNI提供的接口方法
    jobect:表示Java对象中的this
    JNIEXPORTJNICALL:他们是JNI中所定义的宏,可以在JNI.h这个头文件中查找到

    3.4、实现JNI方法

    JNI方法是指Java中声明的native方法,这里可以选择用c++c来实现。
    首先,在工程的主目录下创建一个名为jni的子目录,把之前生成的头文件com_qinkl_JNITest复制进来,接着创建test.cpp文件,内容如下:

    #include "com_qinkl_JNITest.h"
    
    JNIEXPORT jstring JNICALL Java_com_qinkl_JNITest_jniGet(JNIEnv *env,jobject thiz){
        printf("invoke get in c++\n");
        return (*env)->NewStringUTF(env,"Hello from JNI!");
    }
    
    JNIEXPORT void JNICALL Java_com_qinkl_JNITest_jniSet(JNIEnv *env,jobject thiz,jstring string){
        printf("invoke set in c++\n");
        char* str = (char*)(*env)->GetStringUTFChars(env,string,NULL);
        printf("%s\n",str);
        (*env)->ReleaseStringUTFChars(env,string,str);
    }
    

    3.5、生成so库

    gradle3.0以前生成的方式是,在根目录gradle.properties下面加上:

    android.useDeprecatedNdk=true
    

    然后在项目build.gradledefaultConfig节点下,添加代码:

    ndk{
        moduleName "jni-test"//指定生成的so文件名
        abiFilters "armeabi","armeabi-v7a","x86"//CPU的类型
    }
    

    这两步就可以运行生成so库了
    但如果在gradle 3.0之后,已经不支持这样的生成方式了,会报错,内容如下

    Error: Flag android.useDeprecatedNdk is no longer supported and will be removed in the next version of Android Studio. 
    

    3.6、在gradle3.0以上的构建方式

    • 首先先到SDKManager->SDK Tools,下载CMakeLLDB
    • 在项目build.gradledefaultConfig节点下,添加代码
    externalNativeBuild{
        cmake{
            cppFlags ""
            //生成多个版本的so库
            abiFilters 'armeabi-v7a','arm64-v8a'
        }
    }
    
    • 在项目build.gradleandroid节点下,添加代码
    externalNativeBuild{
        cmake{
            path "CMakeLists.txt"//编译后so文件的名字
        }
    }
    
    • 在项目app目录下新建CMakeLists.txt文件,内容如下
    # 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文件名称.
        jni-test
        # Sets the library as a shared library.
        SHARED
        # 设置这个so文件为共享.
        # Provides a relative path to your source file(s).
        # 设置这个so文件为共享.
        src/main/jni/test.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.
        # 制定目标库.
        jni-test
        # Links the target library to the log library
        # included in the NDK.
        ${log-lib} )
    
    • 点击build构建一下,可能会出现一下问题
    executing external native build for cmake
    

    解决办法:将gradle的版本3.1.2改成3.2.1,我这边就解决了,再点击build构建一下,就可以在app/build/cmake/debug/obj路径下看到生成的so库了,大功告成!

    4、案例下载

    点击这里

    相关文章

      网友评论

          本文标题:Android:通过CMake方式生成动态库so文件

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