美文网首页
[Android骚操作] NDK编译多架构SO引用

[Android骚操作] NDK编译多架构SO引用

作者: var_rain | 来源:发表于2018-10-09 17:32 被阅读0次
    1530.jpg

    前言

    在我们开发当中,总有那么一些奇葩的需求要用奇葩的骚操作来实现,好了,话不多说,直接进入主题

    打开方式

    简单来说,当我们需要使用多个架构的SO库编译出多个SO的时候,怎么去配置NDK的编译脚本呢?
    我个人比较喜欢使用原始的Android.mk编译,本文也以Android.mk为例,好像CMake更简单一些,不过,我不喜欢

    Gradle中配置NDK自动编译

    我们使用的是Android.mk文件,那么怎么让Gradle识别并自动编译呢?其实很简单,只需要在项目的gradle.build文件的 android 节点下添加这么一段

    externalNativeBuild {
            ndkBuild {
                path 'src/main/cpp/Android.mk'
            }
        }
    

    其中的 path 表示你的Android.mk所在的绝对路径,这样一来,就实现了NDK自动编译了

    在Android.mk中配置引用多个SO

    一般情况,我们在进行NDK开发的时候,目录结构大概是这样的

    2018-10-09 17-18-36 的屏幕截图.png
    cpp 目录下包含C/C++的源文件
    jniLibs 目录下是引用的对应架构的库文件

    当你的项目需要编译引用多个SO来的时候,怎么去配置呢?其实也很简单的,这里以Android中使用FFmpeg为例

    • 打开你的Android.mk文件,设置工作目录

    正常情况一个合格的Android.mk文件应该是这样子的

    ## 选择需要编译的目标架构,如果全部架构都需要则把 APP_ABI 的值设置为 all 即可
    APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
    
    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    LOCAL_MODULE := native-lib
    LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
    LOCAL_SRC_FILES := native-lib.c native-lib.h
    include $(BUILD_SHARED_LIBRARY)
    
    • 声名SO库的名称及位置

    我们在 LOCAL_PATH := $(call my-dir) 后边添加声名,添加完成过后大概是这个样子的

    APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
    
    LOCAL_PATH := $(call my-dir)
    
    ## libavcodec.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := avcodec
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/armeabi-v7a/libavcodec.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libavdevice.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := avdevice
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/armeabi-v7a/libavdevice.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libavfilter.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := avfilter
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/armeabi-v7a/libavfilter.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libavformat.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := avformat
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/armeabi-v7a/libavformat.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libavutil.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := avutil
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/armeabi-v7a/libavutil.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libswresample.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := swresample
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/armeabi-v7a/libswresample.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libswscale.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := swscale
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/armeabi-v7a/libswscale.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    include $(CLEAR_VARS)
    LOCAL_MODULE := native-lib
    LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
    LOCAL_SRC_FILES := native-lib.c native-lib.h
    include $(BUILD_SHARED_LIBRARY)
    

    这里需要注意的是,声明的SO库后边的 include 应该为 $(PREBUILT_SHARED_LIBRARY)
    而你编译的SO库后边的 include 应该为 $(BUILD_SHARED_LIBRARY)

    • 引用SO库

    最后,在你的编译脚本中加入 LOCAL_SHARED_LIBRARIES := avcodec avdevice avfilter avformat avutil swresample swscale 这后边的值需要是你之前声名的 LOCAL_MODULE 的名称,多个引用中间用空格隔开,添加引用过后整个Android.mk大概就是这个样子的

    APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
    
    LOCAL_PATH := $(call my-dir)
    
    ## libavcodec.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := avcodec
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/armeabi-v7a/libavcodec.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libavdevice.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := avdevice
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/armeabi-v7a/libavdevice.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libavfilter.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := avfilter
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/armeabi-v7a/libavfilter.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libavformat.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := avformat
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/armeabi-v7a/libavformat.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libavutil.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := avutil
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/armeabi-v7a/libavutil.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libswresample.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := swresample
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/armeabi-v7a/libswresample.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libswscale.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := swscale
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/armeabi-v7a/libswscale.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    include $(CLEAR_VARS)
    LOCAL_MODULE := native-lib
    LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
    LOCAL_SHARED_LIBRARIES := avcodec avdevice avfilter avformat avutil swresample swscale
    LOCAL_SRC_FILES := native-lib.c native-lib.h
    include $(BUILD_SHARED_LIBRARY)
    

    这样,你就实现了多个SO的引用

    多架构SO引用编译

    相信你也发现了,上边引用的SO全部是 armeabi-v7a 的,那我们需要引用 arm64-v8a 或者 x86x86_64 的架构时,怎么操作呢?把 armeabi-v7a 换成 x86 或者其他的架构在编译一次?当然不可能,因为这太繁琐了,其实,Android.mk有一些全局变量,而大多数是编译的时候使用的,我们并不知道,那我怎么知道Android.mk当前编译的是那个架构的呢?答案只有一个,那就是 TARGET_ARCH_ABI ,当编译的架构为 armeabi-v7a 的时候,TARGET_ARCH_ABI 的值为 armeabi-v7a ,那么这个时候,我们只需要引用 TARGET_ARCH_ABI 的值替换目录名称就行了,修改后的整个Android.mk是这样的

    APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
    
    LOCAL_PATH := $(call my-dir)
    
    ## libavcodec.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := avcodec
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/$(TARGET_ARCH_ABI)/libavcodec.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libavdevice.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := avdevice
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/$(TARGET_ARCH_ABI)/libavdevice.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libavfilter.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := avfilter
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/$(TARGET_ARCH_ABI)/libavfilter.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libavformat.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := avformat
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/$(TARGET_ARCH_ABI)/libavformat.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libavutil.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := avutil
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/$(TARGET_ARCH_ABI)/libavutil.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libswresample.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := swresample
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/$(TARGET_ARCH_ABI)/libswresample.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    ## libswscale.so
    include $(CLEAR_VARS)
    LOCAL_MODULE := swscale
    LOCAL_SRC_FILES := $(LOCAL_PATH)/../jniLibs/$(TARGET_ARCH_ABI)/libswscale.so
    include $(PREBUILT_SHARED_LIBRARY)
    
    include $(CLEAR_VARS)
    LOCAL_MODULE := native-lib
    LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
    LOCAL_SHARED_LIBRARIES := avcodec avdevice avfilter avformat avutil swresample swscale
    LOCAL_SRC_FILES := native-lib.c native-lib.h
    include $(BUILD_SHARED_LIBRARY)
    

    当然,这还不够,Android.mk算是完事了,但是呢?你会发现在编译的时候会有冲突,因为编译器不知道你的SO文件应该放在那里,所以呢,需要我们告诉他,那怎么告诉他呢?其实也很简单,就是在 build.gradle 文件的 android 节点中加入这一段

    packagingOptions {
    
            pickFirst 'lib/armeabi-v7a/libavcodec.so'
            pickFirst 'lib/armeabi-v7a/libavdevice.so'
            pickFirst 'lib/armeabi-v7a/libavfilter.so'
            pickFirst 'lib/armeabi-v7a/libavformat.so'
            pickFirst 'lib/armeabi-v7a/libavutil.so'
            pickFirst 'lib/armeabi-v7a/libswresample.so'
            pickFirst 'lib/armeabi-v7a/libswscale.so'
            
            pickFirst 'lib/x86/libavcodec.so'
            pickFirst 'lib/x86/libavdevice.so'
            pickFirst 'lib/x86/libavfilter.so'
            pickFirst 'lib/x86/libavformat.so'
            pickFirst 'lib/x86/libavutil.so'
            pickFirst 'lib/x86/libswresample.so'
            pickFirst 'lib/x86/libswscale.so'
            
            pickFirst 'lib/arm64-v8a/libavcodec.so'
            pickFirst 'lib/arm64-v8a/libavdevice.so'
            pickFirst 'lib/arm64-v8a/libavfilter.so'
            pickFirst 'lib/arm64-v8a/libavformat.so'
            pickFirst 'lib/arm64-v8a/libavutil.so'
            pickFirst 'lib/arm64-v8a/libswresample.so'
            pickFirst 'lib/arm64-v8a/libswscale.so'
            
            pickFirst 'lib/x86_64/libavcodec.so'
            pickFirst 'lib/x86_64/libavdevice.so'
            pickFirst 'lib/x86_64/libavfilter.so'
            pickFirst 'lib/x86_64/libavformat.so'
            pickFirst 'lib/x86_64/libavutil.so'
            pickFirst 'lib/x86_64/libswresample.so'
            pickFirst 'lib/x86_64/libswscale.so'
        }
    

    这表示,打包后的APK文件中的lib目录下边有几种架构的SO库,并且每个架构的文件夹里边有多少SO,名称分别为什么

    架构打包过滤

    如果在打包的时候,不想把 x86,x86_64 这类打包到APK中,这时应该怎么操作呢?当然你可能想的是,不编译就行了,当然这是最简单粗暴的解决办法,那么如何优雅的处理这类问题呢?当然是使用Gradle来进行打包过滤啦,只需要在 build.gradle 文件中的 defaultConfig 节点下添加这么一段话就行了

    ndk {
                abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
            }
    

    abiFilters表示打包时需要打包哪些架构的SO到APK中,如果不需要的架构,直接注释掉或者删掉,如果有其他的架构,自己添加上去即可,方便快捷,优雅不单调,简洁而大方

    Android骚操作--简单又实用的开发黑科技

    2bda9ad633dfc754fbc51ff474bdfb0e_b.jpg

    相关文章

      网友评论

          本文标题:[Android骚操作] NDK编译多架构SO引用

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