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

写这篇文章,来记录下如何在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库。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,110评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,443评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,474评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,881评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,902评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,698评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,418评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,332评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,796评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,968评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,110评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,792评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,455评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,003评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,130评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,348评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,047评论 2 355

推荐阅读更多精彩内容