原文地址:http://www.cnblogs.com/linguanh/ (滑至文章末,直接看解決方法)
問題起因:
前5天,因為項目里面有個類似 仿微信 視頻錄制的功能, 先是上網找了個 開源項目:https://github.com/qdrzwd/VideoRecorder,下載下來,在 debug 非混淆的模式下 測試了幾次,覺得效果還不錯,便在它的基礎上進行了 二次開發。
於當天晚上整合好項目,准備第一次的混淆打包(release模式),然后第一次測試,結果,我意料中的事情發生了,沒錯,就是在混淆后經常發生的各種 蹦,對於這類問題,不吹不黑,我經驗算是豐富的了,一般都是在 proguard-rules 文件(android studio) 下做混淆設置解決的,但我不得不承認,這個是我迄今遇到最棘手的,直到 15:46 才正式解決,怎么說都 4 天了,百度上沒有直接的解決信息,逐擇文以告之。
前序:
先說說這個開源項目的背景吧,它是基於 Vine 的背景下進行開發的,Vine 是外國的一個做短視頻分享起家的公司,也是他們 APP 的名字,后被Twitter收購,目前網上的錄制視頻都是基於android自帶的MediaRecorder類,但是這個類比較雞肋,實話實說,用處不是很大,用來練習下還是可以的。Vine 錄制視頻使用javacv自帶的api, 錄制聲音使用的是android的AudioRecord,同樣,這個開源項目也是基於 二者 開發的。這里順便說下,微信用的庫,微信和微視一樣,用的是自己編譯的 ffmpeg庫,vine使用的是javacv庫。看到這里,你是不是在想,我是怎么知道的?其實很簡單,去網上下載這幾個 APP,反編譯下,看看 libs 下的 .so 就知道了。
網上查資料后發現javacv已經提供了視頻錄制功能,並且包含有視頻編輯等圖像處理功能。在javacv的開源項目鏈接可以下載它:https://code.google.com/p/javacv/
具體問題:
好了,前面吹了這么多,先說下我遇到的奇葩問題先。
從下往上看,到最后提示的是 加載動態鏈接庫 libjniPointer.so 出問題了,這是一處比較重要的信息,還有兩處分別是 javacpp.loader.loadLibrary 、 nativeLibraryDirectories=[/data/app/cn.itguy.recordvideodemo-1/lib/arm, /vendor/lib, /system/lib]] ,它們告訴了我三點, 這個問題是加載 linux 下的動態庫失敗,找不到了,由jar包里面的loader類中的loadLibrary函數觸發,最終找不到的位置是手機系統的的lib庫,system、vendor。
是不是有種想去手機system/lib 里面看看有沒有 jniPointer.so 的沖動?事實不用去看,我前面說過在 debug 下是沒問題的,也就證明了這個庫必然存在,找不到是因為混淆的原因。
通常的做法:
因為這類問題是庫找不到,一般我們回去自己項目里面的 .so 文件夾中找找有木有,這是一種盲目的做法,首先應該看錯誤信息,例如上面的,找不到的路徑的 system/lib 也就和本地項目文件夾無關,事實也是這樣,libjniPointer.so 這個東東,原來項目里面就是沒有的。
然后就是混淆配置文件--proguard-rules:具體的所有操作,網上有很詳細的例子,我這里不廢話,直接針對我遇到的情況開刀
首先點開用到的jar包 為了保證 里面的包不被混編,我們會使用
-keep 和 -dontwarn,然后再補上一句不混淆 native方法->
,依賴注入不混淆->
,還不放心的話,可以狠點再次指出屬性和方法都不混淆->
,看到這里,我們會想,應該行了,一般是行的,可能有同志會指出要保護下 .so 啊,-libraryjars libs/xxx.so 這樣子,其實不用,.so 默認不混淆的,所有上面的做法,一般來說,是行的,但是,對於這個視頻錄制來說,還不行。
真正解決:
在執行了上面的設置后,如果還不行的話,基本上再百度也沒什么用了,因為百度上面沒有,等我發完這篇文章估計就有了。
再回到問題的根源, 它是加載由jar包里面的loader類中的loadLibrary函數觸發,最終找不到的位置是手機系統的的lib庫,system、vendor 下的jniPointer.so,我們怎么辦啊?看源碼,還是得這樣的,下面我貼出我的查找路徑,所有代碼是 這個 視頻錄制 開源項目、javacpp 里面的:
首先打 log 跟蹤到->,在new AVPacket() 里面拋出異常,我們點進去->
,因為allocate()沒有其他操作,我們可以跳過它,在此類里查找 static 靜態塊,它先於構造函數執行->
->
->
->
,看到loadLibrary了->
最終getRunTime,整條線索對應 異常信息,到了這里,這里是系統的 system 類,當 libName = “libjnipointer” 的時候,找不到了。 推測,是系統的某些資源被混淆了。我們的混淆配置中有一句設置:-keepattributes 保護給定的可選屬性,那么我們可以用它保護下項目的注釋、資源文件、資源目錄名、內部類等,例如這樣:
,事實證明,當我添加到 InnerClasses 的時候,問題解決。
解決方法:
在混淆文件中添加 保護下項目的注釋、資源文件、資源目錄名、內部類(主要的解決項)