前言
在我們開發(fā)當(dāng)中命贴,總有那么一些奇葩的需求要用奇葩的騷操作來實現(xiàn)梢莽,好了囤捻,話不多說臼朗,直接進(jìn)入主題
打開方式
簡單來說,當(dāng)我們需要使用多個架構(gòu)的SO庫編譯出多個SO的時候蝎土,怎么去配置NDK的編譯腳本呢视哑?
我個人比較喜歡使用原始的Android.mk編譯,本文也以Android.mk為例誊涯,好像CMake更簡單一些挡毅,不過,我不喜歡
Gradle中配置NDK自動編譯
我們使用的是Android.mk文件暴构,那么怎么讓Gradle識別并自動編譯呢跪呈?其實很簡單,只需要在項目的gradle.build文件的 android 節(jié)點下添加這么一段
externalNativeBuild {
ndkBuild {
path 'src/main/cpp/Android.mk'
}
}
其中的 path 表示你的Android.mk所在的絕對路徑取逾,這樣一來耗绿,就實現(xiàn)了NDK自動編譯了
在Android.mk中配置引用多個SO
一般情況,我們在進(jìn)行NDK開發(fā)的時候菌赖,目錄結(jié)構(gòu)大概是這樣的
cpp 目錄下包含C/C++的源文件
jniLibs 目錄下是引用的對應(yīng)架構(gòu)的庫文件
當(dāng)你的項目需要編譯引用多個SO來的時候缭乘,怎么去配置呢?其實也很簡單的琉用,這里以Android中使用FFmpeg為例
-
打開你的Android.mk文件堕绩,設(shè)置工作目錄
正常情況一個合格的Android.mk文件應(yīng)該是這樣子的
## 選擇需要編譯的目標(biāo)架構(gòu),如果全部架構(gòu)都需要則把 APP_ABI 的值設(shè)置為 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 應(yīng)該為 $(PREBUILT_SHARED_LIBRARY)
而你編譯的SO庫后邊的 include 應(yīng)該為 $(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)
這樣,你就實現(xiàn)了多個SO的引用
多架構(gòu)SO引用編譯
相信你也發(fā)現(xiàn)了浅浮,上邊引用的SO全部是 armeabi-v7a 的沫浆,那我們需要引用 arm64-v8a 或者 x86 ,x86_64 的架構(gòu)時滚秩,怎么操作呢专执?把 armeabi-v7a 換成 x86 或者其他的架構(gòu)在編譯一次?當(dāng)然不可能郁油,因為這太繁瑣了本股,其實攀痊,Android.mk有一些全局變量,而大多數(shù)是編譯的時候使用的拄显,我們并不知道苟径,那我怎么知道Android.mk當(dāng)前編譯的是那個架構(gòu)的呢?答案只有一個躬审,那就是 TARGET_ARCH_ABI 棘街,當(dāng)編譯的架構(gòu)為 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)
當(dāng)然炒刁,這還不夠恩沽,Android.mk算是完事了,但是呢翔始?你會發(fā)現(xiàn)在編譯的時候會有沖突罗心,因為編譯器不知道你的SO文件應(yīng)該放在那里,所以呢城瞎,需要我們告訴他渤闷,那怎么告訴他呢?其實也很簡單脖镀,就是在 build.gradle 文件的 android 節(jié)點中加入這一段
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目錄下邊有幾種架構(gòu)的SO庫,并且每個架構(gòu)的文件夾里邊有多少SO蜒灰,名稱分別為什么
架構(gòu)打包過濾
如果在打包的時候弦蹂,不想把 x86,x86_64 這類打包到APK中强窖,這時應(yīng)該怎么操作呢凸椿?當(dāng)然你可能想的是,不編譯就行了翅溺,當(dāng)然這是最簡單粗暴的解決辦法脑漫,那么如何優(yōu)雅的處理這類問題呢?當(dāng)然是使用Gradle來進(jìn)行打包過濾啦咙崎,只需要在 build.gradle 文件中的 defaultConfig 節(jié)點下添加這么一段話就行了
ndk {
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
}
abiFilters表示打包時需要打包哪些架構(gòu)的SO到APK中优幸,如果不需要的架構(gòu),直接注釋掉或者刪掉褪猛,如果有其他的架構(gòu)劈伴,自己添加上去即可,方便快捷,優(yōu)雅不單調(diào)跛璧,簡潔而大方