欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android NDK 開(kāi)發(fā)中 SO 包大小壓縮方法詳解

 更新時(shí)間:2022年09月02日 16:41:03   作者:Android社區(qū)  
這篇文章主要為為大家介紹了Android NDK 開(kāi)發(fā)中 SO 包大小壓縮方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

背景

這周在做Yoga包的壓縮工作。Yoga本身是用BUCK腳本編譯的,而最終編譯出幾個(gè)包大小大總共約為7M,不能滿足項(xiàng)目中對(duì)于APK大小的限制,因此需要對(duì)它進(jìn)行壓縮。

這里先將Yoga編譯腳本用CMAKE重新改寫(xiě),以便可以在android studio中直接使用并輸出一個(gè)AAR的包。后面又對(duì)它進(jìn)行了壓縮,最終將Yoga包的大小壓縮到200多KB。

下面整理了一些可以用于減少NDK開(kāi)發(fā)中Android SO包大小的方法:

1.STL的使用方式

對(duì)于C++的library,引用方式有2種:

  • 靜態(tài)方式(static)
  • 動(dòng)態(tài)方式(shared)

其中,靜態(tài)方式在編譯時(shí)會(huì)將用到的相關(guān)代碼直接復(fù)制到目的文件中;而動(dòng)態(tài)方式則會(huì)將相關(guān)的代碼打成so文件,以便多次引用。由于編譯器在編譯時(shí)并不能知道所有被引用的地方,所以同時(shí)會(huì)打入了很多不相關(guān)的代碼。

所以,如果項(xiàng)目中引用library的函數(shù)較多時(shí),用動(dòng)態(tài)方式可以避免多次拷貝,節(jié)省空間。相反,則直接使用靜態(tài)方式會(huì)更節(jié)省空間。

NDK開(kāi)發(fā)中,可以通過(guò)gradle的設(shè)置來(lái)配置:

defaultConfig{
  externalNativeBuild{
    cmake{
      // gnustl_shared 動(dòng)態(tài)
      arguments "-DANDROID_STL=gnustl_static" 
    }
  }
}

在Yoga中,項(xiàng)目里的stl使用較少時(shí),安卓運(yùn)行時(shí)使用static的方式,而不是shared,所以這里采用static的方式。在采取了這種方式后,包的大小從2.7M縮減到了2M。

2.不使用Exception和RTTI

C++的exception和RTTI功能在NDK中默認(rèn)是關(guān)閉的,但是可以通過(guò)配置打開(kāi)的。

Android.mk:

APP_CPPFLAGS += -fexceptions -frtti

CMake:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -frtti")

Exception和RTTI會(huì)顯著的增加包的體積,所以非必須的時(shí)候,沒(méi)有必要使用。

RTTI

通過(guò)RTTI,能夠通過(guò)基類的指針或引用來(lái)檢索其所指對(duì)象的實(shí)際類型,即運(yùn)行時(shí)獲取對(duì)象的實(shí)際類型。C++通過(guò)下面兩個(gè)操作符提供RTTI。

(1)typeid:返回指針或引用所指對(duì)象的實(shí)際類型。

(2)dynamic_cast:將基類類型的指針或引用安全的轉(zhuǎn)換為派生類型的指針或引用。

在yoga中,RTTI的選項(xiàng)是默認(rèn)打開(kāi)的,而代碼中其實(shí)并沒(méi)有用到相關(guān)的功能,這里可以直接關(guān)閉。

Exception

使用C++的exception會(huì)增加包的大小,而目前JNI對(duì)C++的exception的支持是有bug的,比如下面這段代碼就會(huì)引起程序的crash(對(duì)于低版本的android NDK)。

因此要在程序中引入exception要自己實(shí)現(xiàn)相關(guān)邏輯,yoga就是這么做的,這個(gè)又增加了一些包體大小。對(duì)于開(kāi)發(fā)者來(lái)說(shuō),exception可以幫助快速定位問(wèn)題,而對(duì)于使用者并不是那么重要,這里可以去掉。

try {
    ...
} catch (std::exception& e) {
    env->ThrowNew(env->FindClass("java/lang/Exception"), "Error occured");
}

在yoga中,在關(guān)閉RTTI和Exception功能并把exception相關(guān)的代碼都去掉后,包的大小從2M縮減到的1.8M。

3.使用 gc-sections去除沒(méi)有用到的函數(shù)

去除未使用的代碼顯然可以減少包體的大小,而在NDK的開(kāi)發(fā)中,并不需要手動(dòng)的來(lái)做這一點(diǎn)。可以開(kāi)啟編譯器的gc-sections選項(xiàng),讓編譯器自動(dòng)的幫你做到這一點(diǎn)。

編譯器可以配置自動(dòng)去除未使用的函數(shù)和變量,以下是配置方式:

CMake:

# 去除未使用函數(shù)與變量
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}")
# 設(shè)置去除未使用代碼的鏈接flag
SET_TARGET_PROPERTIES(yoga PROPERTIES LINK_FLAGS "-Wl,--gc-sections")

Android.mk:

LOCAL_CPPFLAGS += -ffunction-sections -fdata-sections
LOCAL_CFLAGS += -ffunction-sections -fdata-sections 
LOCAL_LDFLAGS += -Wl,--gc-sections

4.去除冗余代碼

在NDK中,鏈接器還有一個(gè)選項(xiàng) “-icf = safe”,可以用于去除代碼中的冗余代碼。但是要注意的是,這個(gè)選項(xiàng)也有可能去除定義好的inline函數(shù),這里必須要做好權(quán)衡。

下面是配置方式:

CMake:

SET_TARGET_PROPERTIES(yoga PROPERTIES LINK_FLAGS "-Wl,--gc-sections,--icf=safe")

Android.mk:

LOCAL_LDFLAGS += -Wl,--gc-sections,--icf=safe

5.設(shè)置編譯器的優(yōu)化flag

編譯器有個(gè)優(yōu)化flag可以設(shè)置,分別是-Os(體積最?。?,-O3(性能最優(yōu))等。這里將編譯器的優(yōu)化flag設(shè)置為-Os,以便減少體積。

CMake:

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Os")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}")

Android.mk

LOCAL_CPPFLAGS += -Os
LOCAL_CFLAGS += -Os

在采用了3,4,5這幾種方式后,Yoga包的大小從1.8M減少到了1.7M。這里減少的比較少是因?yàn)閅oga在這方面已經(jīng)做的挺好了,其他的庫(kù)可能會(huì)更有效。

6.設(shè)置編譯器的 Visibility Feature

還有個(gè)減少包體大小的方法,就是設(shè)置編譯器的visibility feature。

Visibility Feature就是用來(lái)控制在哪些函數(shù)可以在符號(hào)表中被輸入,由于C++并不是完全面向?qū)ο蟮?,非類的方法并沒(méi)有public這種修飾符,因此,要用Visibility Feature來(lái)控制哪些函數(shù)可以被外部調(diào)用。

而JNI提供了一個(gè)宏-JNIEXPORT來(lái)控制這點(diǎn)。所以只要對(duì)函數(shù)加上這個(gè)宏,像這樣:

// JNIEXPORT就是控制可見(jiàn)的宏
// JNICALL在NDK這里沒(méi)有什么意義,只是個(gè)標(biāo)識(shí)宏
JNIEXPORT void JNICALL Java_ClassName_MethodName(JNIEnv *env, jobject obj, jstring javaString)

然后在編譯器的FLAGS選項(xiàng)開(kāi)啟 -fvisibility = hidden 就可以。這樣,不僅可以控制函數(shù)的可見(jiàn)性,并且可以減少包體的大小。

CMake:

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}")

7.設(shè)置編譯器的Strip選項(xiàng)

我在把Yoga庫(kù)編譯成AAR包的過(guò)程中發(fā)現(xiàn),它的體積明顯會(huì)大于最后打包進(jìn)APK的大小,這點(diǎn)非常不合理,但是無(wú)法找到原因。

最終搜索到這是谷歌NDK的一個(gè)bug,在打AAR包的過(guò)程中,無(wú)論是debug版本還是release版本,NDK toolchain不會(huì)自動(dòng)的把方便調(diào)試的C++ 符號(hào)表(Symbol Table)中數(shù)據(jù)刪除,而只會(huì)在打APK包的時(shí)候進(jìn)行這一操作。這就導(dǎo)致了打成的AAR包中的SO體積明顯偏大。

找到原因后這個(gè)問(wèn)題就很好解決了,可以手動(dòng)的在鏈接選項(xiàng)中加入 strip參數(shù),配置如下所示:

SET_TARGET_PROPERTIES(yoga PROPERTIES LINK_FLAGS "-Wl,--gc-sections,--icf=safe,-s")

強(qiáng)制進(jìn)行strip操作后,將Yoga包的體積從1.7M成功減少到了282KB。

8.去除C++代碼中的iostream相關(guān)代碼

使用STL中的iostream相關(guān)庫(kù)會(huì)明顯的增加包的體積,而NDK本身是有預(yù)編譯庫(kù)(android/log.h)可以代替這一功能的,在Yoga這里,用log的函數(shù)代替了iostream中的所有函數(shù),如:

//代替所有的iostream庫(kù)里函數(shù)
//cout << obj->toString() << endl;
__android_log_print(ANDROID_LOG_VERBOSE,"Yoga","Node is: %s",obj->toString().c_str());

在做完代替之后,yoga包的體積從282KB減少到了218KB。

總結(jié)

在做完這一系列工作后,最終成功的壓縮了Yoga包的體積,從幾M到最后輸出一個(gè)218KB的AAR包提供使用。以上幾種方法并不局限于Yoga包的縮減。在NDK開(kāi)發(fā)中,要縮減SO包的體積都可以按照這幾種方式嘗試一下。

以上就是Android NDK 開(kāi)發(fā)中 SO 包大小壓縮方法詳解的詳細(xì)內(nèi)容,更多關(guān)于Android NDK開(kāi)發(fā)SO包壓縮的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android自定義View繪制貝塞爾曲線實(shí)現(xiàn)流程

    Android自定義View繪制貝塞爾曲線實(shí)現(xiàn)流程

    貝塞爾曲線的本質(zhì)是通過(guò)數(shù)學(xué)計(jì)算的公式來(lái)繪制平滑的曲線,分為一階,二階,三階及多階。但是這里不講數(shù)學(xué)公式和驗(yàn)證,那些偉大的數(shù)學(xué)家已經(jīng)證明過(guò)了,所以就只講講Android開(kāi)發(fā)中的運(yùn)用吧
    2022-11-11
  • Android集成GreenDao數(shù)據(jù)庫(kù)的操作步驟

    Android集成GreenDao數(shù)據(jù)庫(kù)的操作步驟

    這篇文章主要介紹了Android集成GreenDao數(shù)據(jù)庫(kù),使用數(shù)據(jù)庫(kù)存儲(chǔ)時(shí)候,一般都會(huì)使用一些第三方ORM框架,比如GreenDao,本文分幾步給大家介紹Android集成GreenDao數(shù)據(jù)庫(kù)的方法,需要的朋友可以參考下
    2022-10-10
  • android 之listview 優(yōu)化方法

    android 之listview 優(yōu)化方法

    這篇文章主要介紹了android 之listview 優(yōu)化方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-06-06
  • android 6.0 權(quán)限授權(quán)方法

    android 6.0 權(quán)限授權(quán)方法

    今天小編就為大家分享一篇android 6.0 權(quán)限授權(quán)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • 安卓圖片反復(fù)壓縮后為什么普遍會(huì)變綠而不是其它顏色?

    安卓圖片反復(fù)壓縮后為什么普遍會(huì)變綠而不是其它顏色?

    今天小編就為大家分享一篇關(guān)于安卓圖片反復(fù)壓縮后為什么普遍會(huì)變綠而不是其它顏色?,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-12-12
  • 深度剖析Android Binder IPC機(jī)制

    深度剖析Android Binder IPC機(jī)制

    Android系統(tǒng)的成功離不開(kāi)其強(qiáng)大的IPC(Inter-Process Communication)機(jī)制,其中最引人注目的就是Binder,本文將深入探討B(tài)inder的技術(shù)原理,解釋其工作方式以及相關(guān)的關(guān)鍵概念
    2023-10-10
  • 內(nèi)存泄露導(dǎo)致Android?中setVisibility()?失效原理

    內(nèi)存泄露導(dǎo)致Android?中setVisibility()?失效原理

    這篇文章主要介紹了內(nèi)存泄露導(dǎo)致Android?中setVisibility()?失效原理,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下
    2022-07-07
  • Android App中實(shí)現(xiàn)相冊(cè)瀑布流展示的實(shí)例分享

    Android App中實(shí)現(xiàn)相冊(cè)瀑布流展示的實(shí)例分享

    這篇文章主要介紹了Android App中實(shí)現(xiàn)相冊(cè)瀑布流展示的實(shí)例分享,例子中利用到了緩存LruCache類的相關(guān)算法來(lái)解決大量加載問(wèn)題,需要的朋友可以參考下
    2016-04-04
  • 實(shí)例詳解Android Selector和Shape的用法

    實(shí)例詳解Android Selector和Shape的用法

    shape和selector是Android UI設(shè)計(jì)中經(jīng)常用到的,比如我們要自定義一個(gè)圓角Button,點(diǎn)擊Button有些效果的變化,就要用到shape和selector,通過(guò)本文結(jié)合代碼實(shí)例給大家詳解Android Selector和Shape的用法,感興趣的朋友一起學(xué)習(xí)吧
    2016-01-01
  • Android源碼學(xué)習(xí)之觀察者模式應(yīng)用及優(yōu)點(diǎn)介紹

    Android源碼學(xué)習(xí)之觀察者模式應(yīng)用及優(yōu)點(diǎn)介紹

    定義對(duì)象間一種一對(duì)多的依賴關(guān)系,使得當(dāng)一個(gè)對(duì)象改變狀態(tài),則所有依賴于它的對(duì)象都會(huì)得到通知并被自動(dòng)更新等等,需要了解的朋友可以參考下
    2013-01-01

最新評(píng)論