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

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,506评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,693评论 2 59
  • 本人为初学者,文章写得不好,如有错误,请大力怼我 或者看这里 如何使用jni进行开发 本文主要针对Android环...
    AlbertHumbert阅读 4,643评论 2 12
  • 一个人不寂寞,想一个人才寂寞 思念是一种病,我以病入膏肓 你从我的世界悄然退场,我黯然神伤 凝视你离去的方向踽踽独...
    vicky星儿阅读 93评论 0 0