0.前言
根據(jù)不成文的規(guī)則溉仑,這里還是要廢話幾句的挖函。
看標(biāo)題,我特意加上了“有效果”三字浊竟,沒錯(cuò)怨喘,今天我來給大伙填坑了。
因公司業(yè)務(wù)需求振定,與第三方商家合作必怜,需要調(diào)用商家提供的so庫,對(duì)此知識(shí)一無所知的我踏上了一條踩坑之路后频。
1.導(dǎo)入so庫
廢話不多說梳庆,直接切入正題。
網(wǎng)上的方法卑惜,只要新建jni包然后將so庫放入其中即可使用膏执。但是,本文說的是編譯露久,如果你要編譯一個(gè)so庫更米,這個(gè)庫又需要引用本地so庫,這個(gè)方法是不行的
2.編譯so庫
編譯so庫的方法主要分為mk編譯和cmake編譯這兩種方式抱环。
(1)mk方式編譯獨(dú)立的so庫
先來看看mk編譯的方式壳快。
首先要在gradle.properties文件里面添加
android.useDeprecatedNdk=true
然后在build之中添加代碼:
android{
` ` `
sourceSets {
main{
jni.srcDirs=[]; //禁用as自動(dòng)生成mk
jniLibs.srcDirs = ['src/main/jniLibs']//設(shè)置目標(biāo)的so存放路徑
}
}
//設(shè)置編譯任務(wù)纸巷,編譯ndkBuild
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn 'ndkBuild'
}
` ` `
}
task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') {
//ndk路徑镇草,一定要加上這部分
commandLine "C:\\Users\\Administrator\\AppData\\Local\\Android\\Sdk\\ndk-bundle\\ndk-build.cmd",
'NDK_PROJECT_PATH=build/intermediates/ndk',
'NDK_LIBS_OUT=src/main/jniLibs',
'APP_BUILD_SCRIPT=src/main/jni/Android.mk',
'NDK_APPLICATION_MK=src/main/jni/Application.mk'
}
然后新建一個(gè)jni目錄(相信大伙都懂,這里就不羅嗦怎么建立)瘤旨,在jni目錄下添加Android.mk和Application.mk這兩個(gè)文件梯啤,編寫代碼如下
- Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := dp
LOCAL_SRC_FILES := dp.cpp
LOCAL_LDLIBS :=-llog
include $(BUILD_SHARED_LIBRARY)
dp即自己編寫的cpp文件,位于jni目錄下存哲,名字可以隨意取因宇,但要對(duì)得上。
然在Application.mk文件里寫明要編譯的平臺(tái)祟偷。比如
#模擬器是 x86_64 的
APP_ABI := x86_64
這里的x86_64指編譯出x86_64平臺(tái)的so庫察滑,如果選擇all,即編譯出所有的平臺(tái)的so庫修肠。
然后運(yùn)行一下贺辰,即可編譯出對(duì)應(yīng)的libdp.so庫文件,放在['src/main/jniLibs']目錄下。
這里特別指出重點(diǎn):
task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') {
//ndk路徑饲化,一定要加上這部分
commandLine "C:\\Users\\Administrator\\AppData\\Local\\Android\\Sdk\\ndk-bundle\\ndk-build.cmd",
'NDK_PROJECT_PATH=build/intermediates/ndk',
'NDK_LIBS_OUT=src/main/jniLibs',
'APP_BUILD_SCRIPT=src/main/jni/Android.mk',
'NDK_APPLICATION_MK=src/main/jni/Application.mk'
}
這段代碼一定要加上去莽鸭,不如編譯不過。博主曾在網(wǎng)上搜到過片段代碼與此相似吃靠,奈何其博主沒有詳細(xì)說明也沒有完整說明硫眨,無法理解。此處給出完整例子希望有所幫助巢块。(至于dp.cpp的代碼礁阁,先不給出,這不是本文重點(diǎn))
(2)mk方式引入第三方so庫
為了便于說明族奢,這里給第三方庫取個(gè)名氮兵,就叫sb庫好了,完整名是libsb.so
歹鱼。
導(dǎo)入第三方so庫泣栈,必須要注意他支持的平臺(tái),需要與你的Application.mk中設(shè)置的相對(duì)應(yīng)(如果對(duì)不上弥姻,改為相對(duì)應(yīng)的)南片,否則會(huì)編譯不通過的(重點(diǎn),小編在這問題上被坑得死死的庭敦,希望讀者能吸取小編的教訓(xùn))疼进。
接下來講重點(diǎn):
- 1.將libsb.so庫放在jni目錄下,然后重新編寫Android.mk文件代碼:
LOCAL_PATH := $(call my-dir)
#引入第三方 so
include $(CLEAR_VARS)
LOCAL_MODULE := sb
LOCAL_SRC_FILES := sb.so
LOCAL_EXPORT_C_INCLUDES := include
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := dp
LOCAL_SRC_FILES := dp.cpp
LOCAL_LDLIBS :=-llog
#引入第三方編譯模塊
LOCAL_SHARED_LIBRARIES := \sb
include $(BUILD_SHARED_LIBRARY)
這樣一來(只有這樣)秧廉,你就可以在dp.cpp代碼里面編寫調(diào)用libsb.so庫里面的方法了伞广。如果你只是單純把它放到j(luò)niLibs目錄下,那是不行的疼电。
- 2.你還是得老老實(shí)實(shí)的把它放到j(luò)niLibs目錄下嚼锄,因?yàn)槟愕臉I(yè)務(wù)代碼里面要用到它必然需要把它導(dǎo)進(jìn)去,需要用到哪些庫就放到j(luò)niLibs目錄下蔽豺,別忘了在這些so的上一層加上他的平臺(tái)文件包区丑。
如此一來,再run一下就沒問題了修陡。
(3)cmake方式編譯
使用cmake方式編譯沧侥,那就真的是太即便簡便太簡單了。我這么說讀者可能會(huì)不服氣魄鸦,眼見為實(shí)吧宴杀。
- 1.編寫cpp文件
正常操作,編寫cpp文件拾因,相信這一步不是問題旺罢,這里就取名為mb.cpp吧斯棒,此處就放在main/cpp/目錄下好了。然后再編寫cmake的代碼(語法在你新建支持c++文件的項(xiàng)目時(shí)產(chǎn)生的cmakeLists.txt中有介紹)
這里直接給出代碼:
add_library(mb SHARED src/main/cpp/mb.cpp)
target_link_libraries( # Specifies the target library
mb
${log-lib} )
先用add_library將mb添加進(jìn)去主经,再在target_link_libraries里關(guān)聯(lián)即可荣暮。
如果你的mb.cpp文件里面include了其他頭文件,你還需加上
include_directories(scr/main/cpp/include/ )
聲明頭文件的部分罩驻,否則會(huì)編譯失敗找不到頭文件的(坑)穗酥。
注意:習(xí)慣使用mk編譯的童鞋這里注意了,使用cmake的方式編譯是不會(huì)生成so文件的(坑)惠遏,所以你不用去找libmb.so文件了=-=砾跃。
但是你還是要在你的代碼里面導(dǎo)入庫時(shí)把它倒進(jìn)去:
System.loadLibrary("mb");
(4)cmake引入第三方so庫
這里就給第三方so取名為nb好了(具體的當(dāng)然要與讀者的相同啦),完整名字libnb.so节吮。
不廢話抽高,上代碼:
#導(dǎo)入第三方so包
#IMPORTED 指只是把 so 導(dǎo)入到項(xiàng)目中
add_library( nb
SHARED
IMPORTED )
#指明 so 庫的路徑
set_target_properties( # Specifies the target library.
nb
# Specifies the parameter you want to define.
PROPERTIES IMPORTED_LOCATION
# Provides the path to the library you want to import.
${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libvvw.so )
#頭文件路徑
include_directories(scr/main/cpp/include/ )
add_library(mb SHARED src/main/cpp/mb.cpp)
target_link_libraries( # Specifies the target library.
mb
#關(guān)聯(lián)第三方 so
nb
${log-lib} )
ANDROID_ABI指平臺(tái),同樣透绩,你需要將你的第三方so庫放入到可識(shí)別的路徑文件下(這里放在jniLibs目錄下)翘骂,上一層需加上平臺(tái)的文件夾,比如/jniLibs/x86/libnb.so這樣子帚豪,如果你這里選ANDROID_ABI碳竟,則會(huì)默認(rèn)編譯你在build中設(shè)定的平臺(tái),比如在build中加入:
android {
compileSdkVersion 26
buildToolsVersion "26.0.2"
defaultConfig {
applicationId "com.zhengsr.jnidemo_camke"
minSdkVersion 19
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags "-frtti -fexceptions"
}
}
ndk{
abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a'//重點(diǎn)
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
重點(diǎn)看:
ndk{
abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a'//重點(diǎn)
}
那他就會(huì)默認(rèn)編譯 'x86', 'x86_64', 'armeabi', 'armeabi-v7a', 'arm64-v8a'平臺(tái)的第三方so狸臣,如果你在這里使用了ANDROID_ABI莹桅,而你的jniLibs路徑下恰好沒有以上全部平臺(tái)的libnb.so庫,則會(huì)編譯失敗烛亦,這時(shí)你就需要將ANDROID_ABI改為你所擁有的平臺(tái)或補(bǔ)充相應(yīng)的so文件诈泼。
說到底,這一步煤禽,路徑很重要铐达,必須配置正確,否則會(huì)有一堆問題呜师,然后網(wǎng)上一堆無關(guān)說法娶桦、解決方法等你去踩坑.
通過以上配置贾节,就可以愉快的引用你的so庫了汁汗,想怎么用就怎么用,不怕報(bào)在cpp文件里面找不到庫里面的方法(某方法未聲明)的錯(cuò)誤
總結(jié)還是得要有的
3.總結(jié)
不管是mk編譯方法還是cmake編譯方法栗涂,都是可取的方法知牌。但是相較而言,cmake編譯的方法明顯勝于mk編譯的方法斤程,不用生成so庫文件即可使用角寸。
二者的使用方法上有共通之處菩混,需要注意的是路徑一定要配置正確,
由于cpp的文件書寫以及utils的代碼相關(guān)部分本文并不給出扁藕,需要讀者自行搜索了沮峡。希望此文對(duì)你有所幫助。
如有問題和錯(cuò)誤的地方亿柑,歡迎留言提出邢疙!
原創(chuàng)文章,轉(zhuǎn)載請(qǐng)附上