[Android騷操作] NDK編譯多架構(gòu)SO引用

1530.jpg

前言

在我們開發(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ā)黑科技

2bda9ad633dfc754fbc51ff474bdfb0e_b.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,872評論 25 709
  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 13,934評論 2 59
  • 本人為初學者,文章寫得不好,如有錯誤,請大力懟我 或者看這里 如何使用jni進行開發(fā) 本文主要針對Android環(huán)...
    AlbertHumbert閱讀 4,834評論 2 12
  • 一個人不寂寞,想一個人才寂寞 思念是一種病,我以病入膏肓 你從我的世界悄然退場,我黯然神傷 凝視你離去的方向踽踽獨...
    vicky星兒閱讀 108評論 0 0

友情鏈接更多精彩內(nèi)容