天天看點

Android 5.0 到 Android 6.0 + 的深坑之一 之 .so 動态庫的适配

(原創: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 服務端系統。

Android 5.0 到 Android 6.0 + 的深坑之一 之 .so 動态庫的适配

繼續閱讀