(原創:http://www.cnblogs.com/linguanh)html
目錄:程序員
前序golang
一,問題描述編程
二,爲什麼會如此"無情"?api
三,目前存在該問題的知名SDK函數
四,解決方案,1 對 N工具
前序:學習
嫌無聊的請跳過。上次發博文是同年8月,時光荏苒,空閒時間少,如今都接近年關了,其實這4個月學了不少,接觸了IM(非第三方),學習了 golang 並採用它成功完成仿朋友圈頁面的服務端api,等等..等等..,因爲這個問題的確是業界超級"毒瘤",趁午休時間,盡我程序員的"乾貨"思惟,少廢話,盡通俗。spa
碼字發文。操作系統
一,問題描述
先簡單闡述下幾個概念,這些不是重點。
1,Android 編譯時候的 api 版本,指的是你要生成的這個 apk 所依賴的 sdk 版本,例如 api 23 便是 Android 6.0 ;
2,.so 動態連接庫,爲 Linux 下的庫文件,Windows 是 .dll,Android 是基於 Linux 內核的,因此使用的是 .so,在安卓上面,通常由 C/C++ 語言進行 Jni 編程後,採用 NDK 工具編譯後所生成的,能夠參考下我以前的一篇教程博文:http://www.cnblogs.com/linguanh/p/4624768.html
3,.so的做用,主要是提供系統底層函數,供應用層使用。不用它行不?能夠,在Android已經提供了的狀況下,你不須要再本身添加,例如一個 View 的繪製,裏面都有不少 Native 關鍵詞的函數,這個就是底層函數,Android api 對應的是它已經提供了。那麼若是,你老闆要去實現,語音,圖像,視頻處理等系統沒有的功能,你就只能本身寫 .so 來供調用了。
問題來了:
發生的環境:此類問題通常發生在 Android 6.0 及其以上的系統,具體也存在於其餘的 api 版本,主要集中在 api >=23;
具體表現是:同一個 APP 在 api <=22 的 sdk 狀況下編譯,能夠運行正常,不存在閃退或者 .so 庫加載失敗的狀況,當你採用 api >=23 的sdk 編譯的時候,安裝到 Android 6.0 及其以上的手機的時候,大範圍出現崩潰 或者 .so 庫加載失敗,而在 6.0 如下的手機卻正常;
Catch的信息:dlopen failed: cannot locate symbol "XXXX" xxxx.so, XX 是泛配,此類崩潰信息,你徹底能夠對號入我"座"。
二,爲什麼如此無情?
若是隻爲解決問題,能夠不看這部分。
如今我用一句話說白它,就是:不一樣連接方式時,dlopen會打開指定的系統中(手機中)或提供的動態庫,並使用 dlsym 獲取符號地址,也就是說,若是,在此時的手機中若是找不到,那麼就會出問題,通常和 API 有關係。
人爲因素就是,編譯這個 .so 庫的人,他在編譯的時候沒考慮到下面這些狀況,致使提供給別人用的時候,或者本身用的時候在高 API 版本手機出現問題。
感興趣的就接着看下面詳解吧!上面問題描述的第二點提到 .so 是運行在 Linux 環境下的,並且在 Android 裏面通常由 NDK 編譯,編譯的時候,咱們能夠指明一種文件叫作 Application.mk,裏面有一行 APP_STL := XXX 指明庫的連接方式,默認是靜態,STL的取值:
1)system,默認的值,最危險方式,直接和手機系統版本掛鉤,採用手機最小版本的.so庫連接
2)gabi++_static
3)gabi++_shared
4)stlport_static
5)stlport_shared
6)gnustl_static
7)gnustl_shared
若是不特別定義的話,「system」運行時庫是默認的值。除此以外,凡是後面帶「_static」的,表示其是一個靜態連接的運行時庫(運行時庫的代碼包含在編譯後的程序中);而凡是後面帶「_shared」的,表示其是一個動態連接的運行時庫(運行時庫在程序運行時被動態加載進來)。若是去除動態或靜態連接的因素,則除了默認的「system」運行時庫以外,還有所謂的「gabi++」運行時庫、「stlport」運行時庫和「gunstl」運行時庫。若是想支持C++異常的話,必需要使用gunstl運行時庫。
主要是兩種,靜態連接,動態連接:
動態連接,是指在生成可執行文件時不將全部程序用到的函數連接到一個文件,由於有許多函數在操做系統帶的dll文件中,當程序運行時直接從操做系統中找。
靜態連接,是把全部用到的函數所有連接到 .so 文件中;
重點來了,上面說到了,靜態連接是會把所須要的都搞到exe中,其實否則,這個說法是早期的了,對於如今的 Android 發展來講,爲了使程序方便擴展,具有通用性,已經採用插件形式來連接動態庫,編譯時的靜態和動態連接僅僅是程度問題。插件加載形式有:
1)dlopen
2)dlsym
3)dlclose
dlopen打開指定的系統中(手機中)動態庫。並使用 dlsym 獲取符號地址,也就是說,若是,在此時的手機中若是找不到,那麼就會出問題,通常和 API 有關係。
三,目前存在該問題的知名SDK
根據我所瞭解到的,存在這類問題的 SDK 有,百度地圖、環信、高德地圖、語音庫 speex, 不知道修復沒有,這些 SDK 一但在你的 APK 編譯版中中設置 API >=23 就會出現各類問題,閃退或者拋出異常。
四,解決方案,1 對 N
主要有兩種:
1- 委曲求全,指標不治本,把你的 APK target API 先下降到 23如下,若不行再把 編譯時 API 下降到 23 如下,還出問題就繼續下降,這意味着,你不少 Android Sdk 的新控件用不了;
2- 在 Application.mk 中修改 APP_STL,從新編譯 .so ,若是,我說若是你沒有源碼,那麼悲劇了,要麼等他們解決,要麼採用第一種,建議嘗試,APP_STL := gnustl_shared,
這種方式,對於所須要的外部動態連接函數、符號,在 NDK 13b 中都會獨立生成一份,所有引用就解決此類問題,例如
1 private void load() { 2 try { 3 System.loadLibrary("gnustl_shared"); // 也能夠不寫這一句,可是要保證 gnustl_shared.so 放置在 libs 裏面 4 System.loadLibrary("speex"); 5 } catch (Throwable e) { 6 Log.d("zzzzz","加載語音庫異常 :"+e.toString()); 7 } 8 }
3- 若是須要 libgnustl_shared.so 的,留郵箱,我發一份你。
至此,基本講完了,下期 開源二次開發的IM 服務端系統。