
前言
在我們開發(fā)當中,總有那么一些奇葩的需求要用奇葩的騷操作來實現(xiàn),好了,話不多說,直接進入主題
打開方式
簡單來說,當我們需要使用多個架構(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
一般情況,我們在進行NDK開發(fā)的時候,目錄結(jié)構(gòu)大概是這樣的
2018-10-09 17-18-36 的屏幕截圖.png
cpp 目錄下包含C/C++的源文件
jniLibs 目錄下是引用的對應(yīng)架構(gòu)的庫文件
當你的項目需要編譯引用多個SO來的時候,怎么去配置呢?其實也很簡單的,這里以Android中使用FFmpeg為例
-
打開你的Android.mk文件,設(shè)置工作目錄
正常情況一個合格的Android.mk文件應(yīng)該是這樣子的
## 選擇需要編譯的目標架構(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)在編譯一次?當然不可能,因為這太繁瑣了,其實,Android.mk有一些全局變量,而大多數(shù)是編譯的時候使用的,我們并不知道,那我怎么知道Android.mk當前編譯的是那個架構(gòu)的呢?答案只有一個,那就是 TARGET_ARCH_ABI ,當編譯的架構(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)
當然,這還不夠,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)該怎么操作呢?當然你可能想的是,不編譯就行了,當然這是最簡單粗暴的解決辦法,那么如何優(yōu)雅的處理這類問題呢?當然是使用Gradle來進行打包過濾啦,只需要在 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),簡潔而大方
Android騷操作--簡單又實用的開發(fā)黑科技

