上次開發的相機外掛廠家提供的是一個AAR庫,匯入就可以使用,可以直接用原生UI控制元件渲染介面,也可以用外掛內方法回撥的資料傳遞到Flutter層進行展示。
今天公司又提供了另一臺相機的sdk,本來想著已經學會了aar外掛開發,這次又提供了一套jar和so檔案,又給我出難題。根據百度得知,jar檔案封裝了方法供開發者呼叫,而so檔案是和底層打交道的函式庫,所以和手機的CPU架構有關,一般分為armeabi
,armeabi-v7a
,arm64-v8a
等其他型別的架構。最原始的版本就是32位
的armeabi
,而現在主流的手機一般都採用64位
的arm64-v8a
,。
所以一般來說要對不同架構的手機提供不同的sdk包,當然也可以只使用armeabi
,因為後兩種架構是可以向下相容的。所以一部分第三方服務商雖然提供的SDK包含各種架構的,也會順帶一句:只引入armeabi
也能正常執行,還能減小包的體積。
然而我們這個相機廠商只提供
armeabi
架構的包
由於測試機是v8a的架構,而且flutter經過多個版本更新後,不能直接flutter run的時候指定平臺架構為32位的,則無法呼叫到so檔案
,所以有不小的麻煩。這先按下不表。
一、匯入jar包和so檔案
匯入jar包
首先還是在plugin/android
的目錄新建一個libs
資料夾,然後將jar檔案放進去就好
然後在依賴裡一個一個匯入就好。
dependencies {
implementation files('libs/hyfisheyepano.jar')
implementation files('libs/mid-core-sdk-4.0.7.jar')
implementation files('libs/org.apache.http.legacy.jar')
implementation files('libs/wup-1.0.0.E-SNAPSHOT.jar')
implementation files('libs/Xg_sdk_4.0.3_20180720_1441.jar')
implementation files('libs/nv_sdk_v1.0.0.jar')
}
複製程式碼
匯入so檔案
so檔案包在編輯器中只需要放入指定位置,就可以被找到,在Android studio中需要放在src/main/jniLibs
中,所以直接將檔案包一下子複製進去。
如果有多種架構的so檔案,同樣也是把包整體複製進來。
執行一下
這個時候,程式是可以正常跑起來了,不涉及到外掛功能的時候執行正常,一旦需要呼叫一下外掛的方法的時候,程式會直接奔潰退出,檢視控制檯,提示出Can't link the lib
。
這裡的提示語句是sdk裡使用try catch返回出來的,本意就是coundn't find "xxx.so"
,無法在程式裡面找到所需要的檔案,按照方法匯入了,怎麼又無法連結到,接著往下看。
二、載入so檔案
上面我們執行了一下,發現是jar包裡的方法需求呼叫so檔案,但是怎麼都呼叫不到,這又是什麼問題?
驗證安裝包
首先想到的是驗證一下檔案是否引入成功,如果檔案引入成功,可以直接檢視apk
裡檢視到引入的檔案。所以首先找到安裝包的位置
當flutter執行於debug模式的時候,程式會先生成一個app-debug.apk
的檔案,儲存在專案目錄中build/app/outputs/flutter-apk
的這個位置,當然這裡同時也儲存了release
版的安裝包。
在Android studio的選單欄中,找到build
-> Analyze Apk
這個功能,開啟我們的app-debug.apk
。
可以發現裡面包含了多種架構的jniLibs
,而我們的手機是arm64-v8a
,程式非常符合邏輯的就去對應的架構中去尋找需要檔案,而我們的檔案只有armeabi
的版本,找不到檔案,外掛方法無法呼叫,程式就報錯崩潰
根據開頭提到的:armeabi-v7a
和arm64-v8a
這兩種架構是可以向下相容的,聰明的你肯定想到可以把其他架構的檔案包刪掉就好,只有保留有內容的。
解決方法
解決方法就一句話,在build.gradle
檔案中加上以下這句話:
android{
defaultConfig{
ndk {
abiFilters 'armeabi'
}
}
}
複製程式碼
在這裡是通過 abiFilters 來指定我們需要的 ABI,也就是指定需要的CPU架構,加上上面語句後,在通過Analyze Apk
的功能,可以看到:
安裝包裡只剩armeabi
了,這樣的話,基本的3中架構呼叫檔案的時候,都會呼叫到這個包裡的檔案,程式就不會崩潰了。
這裡不得不插一句,因為我在開發外掛,在得知這個解決方法的時候,我將這個語句放入了
android/build.gradle
,沒有任何效果,而是應該寫在example/android/build.gradle
的這個檔案裡,這裡才是安裝包生成時候所依賴的引數和配置。這個小問題卡了近我3個小時。
三、還是跑不起來?
在指定了ABI之後,程式不會崩潰了,因為程式直接跑不起來了。
話不多說,還是直接看控制檯的報錯,發現缺少了最核心的檔案-libflutter.so
。而在開始未指定ABI的時候,v8a的檔案包裡是有這個檔案的,現在這個檔案隨它一起蒸發了。
依然先開啟百度,輸入這個關鍵詞,有無數的答案。萬語千言就一個原因,在flutter構建apk的時候,需要指定對應的平臺,才會生成對應架構的libflutter.so
檔案。
在舊版本的flutter中,可以直接執行flutter run --target-platform android-arm
命名來指定平臺,新版又去掉了這個功能。只有釋出release
的時候才能指定平臺,但不可能每修改一點,需要除錯了就打包一份吧。
網上還有很多其他方法,試了都不太管用。其實有個最不優雅但很實用的方法,直接再別的包裡拿一個libflutter.so
檔案,放進armeabi
包裡,執行成功。
- 1、複製一份未指定ABI前的
app-debug.apk
。 - 2、修改檔案字尾
apk
到zip
並解壓 - 3、將資料夾裡
libs\arm64-v8a
裡的libflutter.so
拿出來 - 4、放進外掛裡
armeabi
裡 - 5、重新執行,沒報錯了。
四、總結
研究怎麼匯入aar
的時候,3行程式碼花了3天,研究怎麼匯入jar&so
的時候,Ctrl C + Ctrl V幾個檔案又花了3天,果然是萬事開頭難。
大部分時間都是對一個語句的反覆嘗試,多次懷疑自己哪的單詞是不是拼錯了,結果還是對解決方法的原理理解不到位。最開始只知道so放到指定位置就可以了,請求不到的時候報錯也不知道什麼意思,然後發現,不同的CPU架構會請求不同的包,就開始找指定ABI的方法。方法很簡單啊,就一句話,但放錯了位置,沒任何效果,半天開不了竅。終於在最後把每一塊理解到位了,才解決了問題。