美文网首页程序员
交叉编译-生成动态库在AndroidStudio中使用

交叉编译-生成动态库在AndroidStudio中使用

作者: 凌烟醉卧 | 来源:发表于2019-10-01 18:58 被阅读0次

    写这篇文章,来记录下如何在Linux平台下使用NDK编译出能够在AndroidStudio中使用的动态库。

    在进入正题之前,最好先熟悉下这几个知识点。

    这篇文章可以学到什么?

    • 交叉编译生成.so文件,拿到AndroidStudio中使用。
    • 静态库和动态库在CMakeLists.txt文件中的配置。
    • 如果打包生成支持多种cpu的so库。

    环境配置:

    • 交叉编译使用的是Ubantu,我这里使用xShell连接的阿里云的服务器,可以查看xShell连接阿里云服务器
    • 交叉编译使用的NDK17(Linux版本的NDK),
    • AndroidStudio版本是3.4。

    下面直接进入正题

    设置NDK中的变量

    export CC=/root/softffmpeg/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc
    
    export AAA="--sysroot=/root/softffmpeg/android-ndk-r17c/platforms/android-21/arch-arm -isystem /root/softffmpeg/android-ndk-r17c/sysroot/usr/include  -isystem /root/softffmpeg/android-ndk-r17c/sysroot/usr/include/arm-linux-androideabi"
    

    使用echo $CC可以查看变量的路径。

    生成动态库

    $CC $AAA -fPIC -shared test.c -o libTest.so
    

    $CC $AAA意为将AAA当做参数传递给CC。

    test.c文件

    int test(){
       return 1;
    }
    

    将生成的libTest.so拿到AndroidStudio中使用

    将listTest.so放在jniLibs目录下


    接下来配置下CMakeLists.txt文件

    cmake_minimum_required(VERSION 3.4.1)
    
    add_library(
            native-lib
            SHARED
            src/main/cpp/native-lib.cpp )
    
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a")
    
    target_link_libraries( 
            native-lib
            Test
            log )
    

    注意:CMake通过生成makefile文件,makefile文件生成.a和.so文件。

    add_library( native-lib SHARED src/main/cpp/native-lib.cpp )
    native-lib:变量名字,这个名字随便起
    SHARED:动态库 ;静态STATIC为
    src/main/cpp/native-lib.cpp:源文件

    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a")
    上面这句话是设置一个变量。
    CMAKE_CXX_FLAGS是c++的参数 会传给编译器,相当于--sysroot=XX。
    CMAKE_C_FLAGS 是c的参数 会传给编译器。
    CMAKE_SOURCE_DIR代表的是这个文件(CMakeLists.txt)的所在的目录。

    上面的set语句和下面的是一样的,效果也是一样的:

    cmake_minimum_required(VERSION 3.4.1)
    add_library(
            native-lib
            SHARED
            src/main/cpp/native-lib.cpp )
    
    #这两句和上面的set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a")是一样的
    add_library(Test SHARED IMPORTED)
    set_target_properties(Test PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libTest.so)
    
    #-lTest
    target_link_libraries( # Specifies the target library.
            native-lib
            Test
            #引入NDK中的库
            log
           )
    

    上面使用的都是我们自己的库,那NDK的库在什么位置呢?
    比如上面我使用了NDK中的log这个库,其实它在系统中的位置如下:

    E:\androidstudio3.4\SDK\ndk-bundle\platforms\android-21\arch-arm\usr\lib
    

    如果需要编译多个平台的so文件,可以使用ANDROID_ABI设置:
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI} ")

    引入静态库和引入动态库的区别?

    引入静态库

    add_library(Test2 STATIC IMPORTED)
    set_target_properties(Test2 PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/cpp/${ANDROID_ABI}/libTest.a)
    target_link_libraries( 
            native-lib
            Test2
            )
    

    IMPORTED:表示我们这一个静态库是以导入的形式添加进来的(预编译静态库)

    MainActivity.java中配置

    static {
       System.loadLibrary("Test");
       System.loadLibrary("native-lib");
    }
    

    native-lib.cpp中使用

    #include <jni.h>
    #include <string>
    
    #include <android/log.h>
    #define LOG_TAG "atguigu"
    #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG ,__VA_ARGS__)
    #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG ,__VA_ARGS__) 
    #define LOGE(...) __android_log_print(ERROR,LOG_TAG ,__VA_ARGS__) 
    
    extern "C"{//这个文件是C++写的,libTest.so中的test方法是C写的
    extern int test();//注意:这里使用extern来调用.so中的方法
    }
    extern "C" JNIEXPORT jstring JNICALL
    Java_com_example_myapplication_MainActivity_stringFromJNI(
            JNIEnv *env,
            jobject /* this */) {
        std::string hello = "Hello from C++";
        LOGI("调用so的test()方法%d\n",test());
        return env->NewStringUTF(hello.c_str());
    }
    

    extern的作用
    1)C++文件中引用C的代码
    2)C++代码中引用其它库中的方法。

    app下的build.gradle配置

    android {
        defaultConfig {
            //指导我们的源文件编译
            externalNativeBuild {
                cmake {
                    cppFlags ""
                    //你希望编译你的c/c++源文件,编译几种cpu(arm,x86等)
                    abiFilters "armeabi-v7a"
                    //abiFilters "arm64-v8a","armeabi-v7a"
                }
            }
            //这里表示打包集中cpu,比如集成了第三方库,第三方库提供了arm的,提供了x86的,可以在此处指导打包arm的,生成出来的apk就包含arm的。
            ndk{
                abiFilters "armeabi-v7a"
                //abiFilters "arm64-v8a","armeabi-v7a"
            }
        }
    
        externalNativeBuild {
            cmake {
                path "CMakeLists.txt"//这里指定CMakeLists.txt文件的路径
                version "3.10.2"
            }
        }
    }
    

    上面最外层的externalNativeBuild中指定了CMakeLists.txt文件的位置,如果我们的CMakeLists.txt文件在别的位置,比如放在了cpp目录下了,那这里的路径应该这样处理?

    path "CMakeLists.txt
    

    改为

    path "src/main/cpp/CMakeLists.txt
    

    并且CMakeLists.txt中的相关目录也要修改,我这里需要将

    set_target_properties(Test PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a/libTest.so)
    

    改为

    //..代表CMakeLists.txt文件所在目录的上一级目录
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}")
    

    如何打包成多cpu的so库?

    比如我现在想打包armeabi-v7a和arm64-v8a的两个库:
    1)在jniLibs目录下再创建一个文件夹,把armeabi-v7a中的so库拷贝到arm64-v8a中。


    2)修改下CMakeLists.txt中文件

    #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi-v7a")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}")
    

    我们将之前的set注释掉,使用ANDROID_ABI来动态的加载libTest.so库。

    3)修改app下的build.gradle文件

    android {
        defaultConfig {
            externalNativeBuild {
                cmake {
                    cppFlags ""
                    abiFilters "arm64-v8a","armeabi-v7a"
                }
                ndk {
                    ldLibs "log"//实现__android_log_print
                }
            }
    
            ndk{
                //这里我们的库支持两种cpu
                abiFilters "arm64-v8a","armeabi-v7a"
            }
        }
    
        externalNativeBuild {
            cmake {
                path "CMakeLists.txt"
                version "3.10.2"
            }
        }
    }
    

    运行之前先clear下,运行成功后,我们查看下面这个路径的apk

    app\build\outputs\apk\debug\app-debug.apk
    

    点击进行查看


    可以看到成功打包了支持两个cpu的so库。

    相关文章

      网友评论

        本文标题:交叉编译-生成动态库在AndroidStudio中使用

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