(原創:http://www.cnblogs.com/linguanh)
目錄:
前序
一,問題描述
二,為何會如此"無情"?
三,目前存在該問題的知名SDK
四,解決方案,1 對 N
前序:
嫌無聊的請跳過。上次發博文是同年8月,時光荏苒,空閑時間少,現在都接近年關了,其實這4個月學了很多,接觸了IM(非第三方),學習了 golang 并采用它成功完成仿朋友圈頁面的服務端api,等等..等等..,由于這個問題的确是業界超級"毒瘤",趁午休時間,盡我程式員的"幹貨"思維,少廢話,盡通俗。
碼字發文。
一,問題描述
先簡單闡述下幾個概念,這些不是重點。
1,Android 編譯時候的 api 版本,指的是你要生成的這個 apk 所依賴的 sdk 版本,例如 api 23 即是 Android 6.0 ;
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 中都會獨立生成一份,全部引用就解決此類問題,例如
3- 如果需要 libgnustl_shared.so 的,留郵箱,我發一份你。
至此,基本講完了,下期 開源二次開發的IM 服務端系統。
、
