最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

背景

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

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

下面整理了一些可以用于減少NDK開發(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開發(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ò)配置打開的。

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)打開的,而代碼中其實(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ì)于開發(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的開發(fā)中,并不需要手動(dòng)的來(lái)做這一點(diǎn)。可以開啟編譯器的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就是控制可見的宏
// JNICALL在NDK這里沒(méi)有什么意義,只是個(gè)標(biāo)識(shí)宏
JNIEXPORT void JNICALL Java_ClassName_MethodName(JNIEnv *env, jobject obj, jstring javaString)

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

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開發(fā)中,要縮減SO包的體積都可以按照這幾種方式嘗試一下。

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

相關(guān)文章

最新評(píng)論

紫阳县| 巨野县| 双流县| 大余县| 湘乡市| 宁夏| 湘潭县| 思南县| 额济纳旗| 黔江区| 北辰区| 潞西市| 三台县| 高尔夫| 肥东县| 博兴县| 祁阳县| 通城县| 鹤壁市| 安庆市| 突泉县| 山西省| 崇文区| 当雄县| 永城市| 宣武区| 海林市| 乐平市| 隆化县| 林芝县| 宝清县| 太仆寺旗| 霍山县| 木里| 时尚| 佳木斯市| 呼玛县| 界首市| 镇坪县| 佛坪县| 根河市|