[TOC]
首先說下apk體積減小的必要性
減小apk的安裝時間,增加用戶留存,減少CDN流量費用
再這之前,先來看看apk的結構,以微信apk為例,用Android studio打開
可以看到APK由以下主要部分組成:
文件/目錄 | 描述 |
---|---|
lib | 存放so文件喉钢,可能會有armeabi、armeabi-v7a杖剪、arm64-v8a服赎、x86榜跌、x86_64什黑、mips起惕,大多數(shù)情況下只需要支持armabi與x86的架構即可涡贱,如果非必需,可以考慮拿掉x86的部分 |
assets | 應用的資源文件 |
r | 這是經(jīng)過proguard的資源文件 |
class*.dex | classes文件是Java Class惹想,被DEX編譯后可供Dalvik/ART虛擬機所理解的文件格式 |
META_INF | 它包含了APK中所有文件的簽名摘要等信息,其中MANIFEST.MF的內(nèi)容是對apk每個文件進行摘要然后base64一下,進行存儲问词。 CERT.SF內(nèi)容是對MANIFEST.MF中的每條內(nèi)容進行進行摘要然后base64一下。 CERT.RSA就是一個pkcs7格式 der編碼的證書文件,可以通過openssl獲取內(nèi)容,這里會把CERT.SF文件用私鑰計算出簽名然后寫入CERT.RSA, META_INF具體內(nèi)容可以參考[Android簽名機制(http://blog.csdn.net/jiangwei0910410003/article/details/50402000) |
resources.arsc | 編譯后的二進制資源 |
AndroidManifest.xml | Android的清單文件 |
在介紹怎么做之前嘀粱,先來大概介紹一下App的資源是怎么被打進APK包里的激挪。Android構建工具鏈使用AAPT工具來對資源進行處理,來看下圖(圖片來源于Build Workflow):
[圖片上傳中...(image.png-293ad-1519886265335-0)]
下面來說下具體減少apk size的方法
一. 減少資源的數(shù)量和大小,主要有一下幾個方面
1.使用drawable(xml中使用<shape>標簽生成drawable對象)替換靜態(tài)圖片資源,減少apk的大小
??比如
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<stroke
android:width="2dp"
android:color="#2dcaff" />
<gradient
android:startColor="@color/transparent"
android:endColor="@color/transparent"/>
</shape>
??使用Drawable有什么好處锋叨?
???很方便得到一個矩形垄分,圓,橢圓娃磺,圓環(huán)薄湿,很容易維護和修改
???很方便實現(xiàn)圓角,漸變(線性漸變偷卧,徑向漸變豺瘤,掃描漸變)
???代替圖片作為 View 的背景,減少 apk 的體積(減少 apk 體積最明顯最有效的步驟就是去掉圖片)
???大圖片耗內(nèi)存听诸,使用 Drawable 節(jié)省內(nèi)存坐求,Android 本身對 Drawable 做了很好的優(yōu)化(內(nèi)存優(yōu)化需要考慮)
??什么情況下選擇使用Drawable,而不是使用一張圖蛇更,反之呢瞻赶?
???理論上能用 Drawable 的地方就用 Drawable
???如果能夠通過 shape 標簽就能定義的幾何圖形就能滿足需求,就不用圖片來表示
???漸變類型的背景也盡量使用 shape 來實現(xiàn)
???不規(guī)則的派任,復雜的圖形還是只能使用圖片砸逊,比如要一個表示手機的圖標,一個人的頭像
???有些特殊拉升效果需要使用 .9.png 圖片(盡可能的小吧掌逛,越小越省內(nèi)存)
2.刪除未使用的資源
??使用lint工具檢測未使用的資源,
??刪除未使用的資源:主要通過shrinkresource編譯時優(yōu)化無用資源师逸,我以前以為這個關鍵字是刪??除無用資源,其實并非這樣豆混,它只是把無用資源替為一個小的虛擬的文件(背景全黑)篓像,并非刪除它,
??設置了shrinkresource的編譯的日志如下
??Skipped unused resource res/drawable-xhdpi-v4/ic_launcher.png: 4366 bytes (replaced with small dummy file of size 67 bytes)
2.圖片優(yōu)化
為了支持Android設備DPI的多樣化([l|m|tv|h|x|xx|xxx]dpi)以及用戶對高質(zhì)量UI的期待动知,美團App中使用了大量的圖片,在Android下支持很多格式的圖片员辩,例如:PNG盒粮、JPG 、WebP奠滑,那我們該怎么選擇不同類型的圖片格式呢丹皱? 在Google I/O 2016
中提到了針對圖片格式的選擇,來看下圖(圖片來源于Image compression for Android developers):
通過上圖可以看出一個大概圖片格式選擇的方法宋税。如果能用VectorDrawable
來表示的話優(yōu)先使用VectorDrawable摊崭,如果支持WebP
則優(yōu)先用WebP,而PNG
主要用在展示透明或者簡單的圖片杰赛,而其它場景可以使用JPG
格式呢簸。針對每種圖片格式也有各類的優(yōu)化手段和優(yōu)化工具
??使用矢量圖片
???可以使用矢量圖形來創(chuàng)建獨立于分辨率的圖標和其他可伸縮圖片。使用矢量圖片能夠有效的減少App中圖片所占用的大小乏屯,矢量圖形在Android中表示為VectorDrawable對象根时。 使用VectorDrawable對象,100字節(jié)的文件可以生成屏幕大小的清晰圖像瓶珊,但系統(tǒng)渲染每個VectorDrawable對象需要大量的時間啸箫,較大的圖像需要更長的時間才能出現(xiàn)在屏幕上。 因此只有在顯示小圖像時才考慮使用矢量圖形伞芹。有關使用VectorDrawable的更多信息,請參閱 Working with Drawables蝉娜。也可參考 Android使用矢量圖-簡述
??使用WebP
???如果App的minSdkVersion
高于14(Android 4.0+
)的話唱较,可以選用WebP格式,因為WebP在同畫質(zhì)下體積更姓俅ā(WebP支持透明度南缓,壓縮比比JPEG更高但顯示效果卻不輸于JPEG,官方評測quality參數(shù)等于75均衡最佳)荧呐, 可以通過PNG到WebP轉換工具來進行轉換汉形。當然Android從4.0才開始WebP的原生支持,但是不支持包含透明度倍阐,直到Android 4.2.1+
才支持顯示含透明度的WebP概疆,在筆者使用中是判斷當前App的minSdkVersion
以及圖片文件的類型(是否為透明)來選用是否適用WebP。見下面的代碼片段:
boolean isPNGWebpConvertSupported() {
if (!isWebpConvertEnable()) {
return false
}
// Android 4.0+
return GradleUtils.getAndroidExtension(project).defaultConfig.minSdkVersion.apiLevel >= 14
// 4.0
}
boolean isTransparencyPNGWebpConvertSupported() {
if (!isWebpConvertEnable()) {
return false
}
// Lossless, Transparency, Android 4.2.1+
return GradleUtils.getAndroidExtension(project).defaultConfig.minSdkVersion.apiLevel >= 18
// 4.3
}
def convert() {
String resPath = "${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/res/merged/${variant.dirName}"
def resDir = new File("${resPath}")
resDir.eachDirMatch(~/drawable[a-z0-9-]*/) { dir ->
FileTree tree = project.fileTree(dir: dir)
tree.filter { File file ->
return (isJPGWebpConvertSupported() && (file.name.endsWith(SdkConstants.DOT_JPG) || file.name.endsWith(SdkConstants.DOT_JPEG))) || (isPNGWebpConvertSupported() && file.name.endsWith(SdkConstants.DOT_PNG) && !file.name.endsWith(SdkConstants.DOT_9PNG))
}.each { File file ->
def shouldConvert = true
if (file.name.endsWith(SdkConstants.DOT_PNG)) {
if (!isTransparencyPNGWebpConvertSupported()) {
shouldConvert = !Imaging.getImageInfo(file).isTransparent()
}
}
if (shouldConvert) {
WebpUtils.encode(project, webpFactorQuality, file.absolutePath, webp)
}
}
}
}
??減少動畫幀
???再不影響用戶體驗的情況下,可適當?shù)臏p少幀率,進而就減少了幀數(shù),減少資源
二.減少原生和Java代碼
?減少本地二進制文件的大小
??如果你的應用使用原生代碼和Android NDK峰搪,你還可以通過優(yōu)化代碼來減小應用的大小岔冀。兩個有用的技術是刪除調(diào)試符號和提取原生庫。
???. 刪除調(diào)試符號
???? 如果你的應用程序正在開發(fā)中并仍需要調(diào)試概耻,則使用調(diào)試符號很有意義使套。 使用Android NDK中提供的arm-eabi-strip
工具從本機庫中刪除不必要的調(diào)試符號罐呼。 之后,你可以編譯你的發(fā)行版侦高。
???. 避免提取原生庫
??? ?將.so
文件存儲在APK中未壓縮的文件嫉柴,并在應用清單的<application> </application>元素中將android:extractNativeLibs
標記設置為false。 這將防止 PackageManager在安裝過程中將.so
文件從APK復制到文件系統(tǒng)奉呛,并且將具有使你的應用程序的delta更新更小的額外好處计螺。
??混淆(java代碼):
???這里不細說混淆了,可以參考這邊文章,我覺得寫的很好Android混淆,個人再補充點混淆規(guī)則之刪除日志
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int w(...);
public static int d(...);
public static int v(...);
public static int i(...);
}
上面的代碼加到混淆配置文件里面,可以刪除log日志,但是前提是optimize開啟, 不要加-dontoptimize, 之前筆者一直加了上面的代碼,但是日志依然有,究其原因時因為getDefaultProguardFile('proguard-android.txt'),
這是Android默認的混淆文件,這個文件里面有-dontoptimize,所以一直失敗,可以使用proguard-android-optimize.txt這個配置文件
三.resources.arsc的優(yōu)化
resources.arsc是編譯之后的二進制資源,主要維護者資源名字,資源id,資源路徑,資源類型,以及字符串資源的值等等,所以直接參考騰訊的方案安裝包立減1M--微信Android資源混淆打包工具,其主要原理就是將名字和路徑變短,類似于下面的
R.string.name -> R.string.a
res/drawable/icon -> r/s/a
(參考文檔)https://tech.meituan.com/android-shrink-overall-solution.html