Apk瘦身压缩体验
文章目錄
- 資源統(tǒng)一
- 使用jpg格式圖片
- 使用webp格式圖片
- 使用shape背景和selector著色方案
- 在線化素材庫
- lint檢查
- 刪除不必要的so庫
- 去除無用語言資源
- 開啟混淆
- 開啟shrinkResources去除無用資源
- 使用zipAlign
- 使用AndResGuard對資源文件壓縮
- 編譯webp解碼器
- 下載webp源碼放在創(chuàng)建的jni文件夾下
- 引入jar包
- Android.mk配置
- 創(chuàng)建Application.mk
- 編譯解碼器的so庫
- 對比png,jpeg,webp的編解碼速度
資源統(tǒng)一
盡量一個項目使用同一套資源,對于絕大對數(shù)APP來說,只需要取一套設(shè)計圖就足夠了。鑒于現(xiàn)在分辨率的趨勢,建議取720p的資源,放到xhdpi目錄。
相對于多套資源,只使用720P的一套資源,在視覺上差別不大,很多大公司的產(chǎn)品也是如此,但卻能顯著的減少資源占用大小,順便也能減輕設(shè)計師的出圖工作量了。不是xhdpi的目錄都刪除,而是強(qiáng)調(diào)保留一套設(shè)計資源就夠了。
資源圖片引入前先進(jìn)行壓縮,不使用原圖(不要直接使用UI切的原圖)
使用jpg格式圖片
如果對于非透明的大圖,jpg將會比png的大小有顯著的優(yōu)勢,雖然不是絕對的,但是通常會減小到一半都不止。在啟動頁,活動頁等之類的大圖展示區(qū)采用jpg將是非常明智的選擇。
使用webp格式圖片
相對于jpg、png,webp作為一種新的圖片格式,限于android的支持情況暫時還沒用在手機(jī)端廣泛應(yīng)用起來。從Android 4.0+開始原生支持,但是不支持包含透明度,直到Android 4.2.1+才支持顯示含透明度的webp,使用的時候要特別注意。
使用shape背景和selector著色方案
對于一些簡單的圖形或者背景圖片,通過自定義繪制圖形方式來代替引入圖片本身。
對于一些外形相同僅有顏色不同的圖片,可以使用selector文件來實現(xiàn)。
在線化素材庫
如果有很多或者一組圖形需要引入,比如說表情包,可以考慮使用在線引入的方式,省去本地的空間。但同時會增加代碼復(fù)雜度和APP流量消耗。
lint檢查
代碼掃描工具,可幫助您發(fā)現(xiàn)并更正代碼結(jié)構(gòu)質(zhì)量的問題,例如,如果 XML 資源文件包含未使用的命名空間,這樣不僅占用空間,而且還會引起不必要的處理。
官網(wǎng)地址
終端執(zhí)行命令
或者您可以通過依次選擇 Analyze > Inspect Code,手動運行配置的 lint 及其他 IDE 檢查。檢查結(jié)果將顯示在 Inspection Results 窗口中,如果有未使用的資源可以刪除。
刪除不必要的so庫
基本上armable的so也是兼容armable-v7的,armable-v7a的庫會對圖形渲染方面有很大的改進(jìn),如果沒有這方面的要求,可以精簡。有極少數(shù)設(shè)備會Crash,測試通過再使用。x86包下的so在x86型號的手機(jī)是需要的,如果產(chǎn)品沒用這方面的要求也可以精簡。實際工作的配置是可以選擇只保留armable、armable-x86下的so文件。
去除無用語言資源
只保留了中文和英文的語言資源,針對一些支持多語言的SDK。
android {defaultConfig {resConfigs "zh-rCN", "en-rUS"} }開啟混淆
android {defaultConfig {minifyEnabled true} }開啟shrinkResources去除無用資源
會把未使用到的資源文件在打包的時候移除內(nèi)容,但源代碼不會變,但此方案實測對apk的瘦身優(yōu)化不一定會起到正向效果,可以酌情使用。但使用的時候要和上一步開啟混淆同時使用,單獨使用無效。
使用zipAlign
資源對齊處理的工具(對齊到四字節(jié)邊界并以此為單位進(jìn)行訪問),使得資源在被訪問時候更有效率 能夠?qū)Υ虬膽?yīng)用程序進(jìn)行優(yōu)化,是的系統(tǒng)和APP之間的交互更加有效率,但對apk瘦身起到的效果不顯著。
android {buildTypes {release {zipAlignEnabled true}} }使用AndResGuard對資源文件壓縮
以上幾種方式,主要對資源本身進(jìn)行處理和代碼壓縮,但是apk中的資源并沒有進(jìn)行過多的操作。 AndResGuard微信退出的一個幫助你縮小APK大小的工具,但是只針對資源(主要是res)并不涉及編譯過程。他會將原本冗長的資源路徑變短,例如將res/drawable/wechat變?yōu)閞/d/a
官網(wǎng)及引入方式
添加混淆白名單:指定不需要進(jìn)行混淆的資源路徑規(guī)則,主要是一些第三方SDK,因為有些SDK的代碼中引用到對應(yīng)的資源文件,如果對其進(jìn)行混淆,會導(dǎo)致找不到對應(yīng)資源文件,出現(xiàn)crash,所以不能對其資源文件進(jìn)行混淆。白名單鏈接如下;
白名單
引入時也可以選擇自定義gradle的方式
配置完成后使用如下方式打出需要的apk
效果就類似于微信的apk壓縮后的效果。把res文件改成了如下r文件夾的形式,并且壓縮效果不錯。
編譯webp解碼器
手機(jī)廠商自帶的webp解碼器基本上只針對Android 4.3 以上的機(jī)型,4.3以下是有問題的,所以我們可以對webp的源碼進(jìn)行編譯,打包成so庫,使用自己的解碼器。
下載webp源碼放在創(chuàng)建的jni文件夾下
libwebp源碼下載地址
引入jar包
將libwebp.jar引入到工程中
Android.mk配置
創(chuàng)建Application.mk
APP_ABI := armeabi-v7a APP_PLATFORM := android-14編譯解碼器的so庫
在libwebp源碼目錄下打開cmd,使用ndk去對目錄下的文件進(jìn)行編譯,生成so庫
C:\SDK\ndk\21.3.6528147\ndk-build.cmd NDK_PROJECT_PATH=. NDK_APPLICATION_MK=Application.mk APP_BUILD_SCRIPT=Android.mk編譯成功后導(dǎo)入so庫文件
對比png,jpeg,webp的編解碼速度
相關(guān)代碼如下
defaultConfig {applicationId "com.bliss.yang.webpapp"minSdkVersion 19targetSdkVersion 30versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"ndk {abiFilters "armeabi-v7a"//,添加ndk的支持}} companion object{init {System.loadLibrary("webp")}}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// webp 解碼速度 編碼速度var l = System.currentTimeMillis()BitmapFactory.decodeResource(resources, R.drawable.splash_bg_webp)Log.e(TAG, "解碼webp圖片耗時:" + (System.currentTimeMillis() - l))l = System.currentTimeMillis()BitmapFactory.decodeResource(resources, R.drawable.splash_bg_jpeg)Log.e(TAG, "解碼jpeg圖片耗時:" + (System.currentTimeMillis() - l))l = System.currentTimeMillis()var bitmap = BitmapFactory.decodeResource(resources, R.drawable.splash_bg_png)Log.e(TAG, "解碼png圖片耗時:" + (System.currentTimeMillis() - l))//編碼 pngvar bitmap1 = BitmapFactory.decodeResource(resources, R.drawable.splash_bg_png)l = System.currentTimeMillis()compressBitmap(bitmap1, CompressFormat.PNG, Environment.getExternalStorageDirectory().toString() + "/test.png")Log.e(TAG, "------->編碼png圖片耗時:" + (System.currentTimeMillis() - l))//編碼 jpegbitmap1 = BitmapFactory.decodeResource(resources, R.drawable.splash_bg_jpeg)l = System.currentTimeMillis()compressBitmap(bitmap1, CompressFormat.JPEG, Environment.getExternalStorageDirectory().toString() + "/test.jpeg")Log.e(TAG, "------->編碼jpeg圖片耗時:" + (System.currentTimeMillis() - l))//編碼 webpbitmap1 = BitmapFactory.decodeResource(resources, R.drawable.splash_bg_webp)l = System.currentTimeMillis()compressBitmap(bitmap1, CompressFormat.WEBP, Environment.getExternalStorageDirectory().toString() + "/test.webp")Log.e(TAG, "------->編碼webp圖片耗時:" + (System.currentTimeMillis() - l))// 編譯的解碼器l = System.currentTimeMillis()decodeWebp()Log.e(TAG, "libwebp解碼圖片耗時:" + (System.currentTimeMillis() - l))l = System.currentTimeMillis()encodeWebp(bitmap)Log.e(TAG, "libwebp編碼圖片耗時:" + (System.currentTimeMillis() - l))}private fun encodeWebp(bitmap: Bitmap) {//獲取bitmap 寬高val width = bitmap.widthval height = bitmap.height//獲得bitmap中的 ARGB 數(shù)據(jù) nioval buffer: ByteBuffer = ByteBuffer.allocate(bitmap.byteCount)bitmap.copyPixelsToBuffer(buffer)//編碼 獲得 webp格式文件數(shù)據(jù) 4 *widthval bytes: ByteArray = libwebp.WebPEncodeRGBA(buffer.array(), width, height, width * 4, 75F)var fos: FileOutputStream? = nulltry {fos = FileOutputStream(Environment.getExternalStorageDirectory().toString() + "/libwebp.webp")fos.write(bytes)} catch (e: Exception) {e.printStackTrace()} finally {if (null != fos) {try {fos.close()} catch (e: IOException) {e.printStackTrace()}}}}private fun decodeWebp(): Bitmap? {@SuppressLint("ResourceType") val `is`: InputStream = resources.openRawResource(R.drawable.splash_bg_webp)val bytes = stream2Bytes(`is`)//將webp格式的數(shù)據(jù)轉(zhuǎn)成 argbval width = IntArray(1)val height = IntArray(1)try {`is`.close()} catch (e: IOException) {e.printStackTrace()}val argb: ByteArray = libwebp.WebPDecodeARGB(bytes, bytes.size.toLong(), width, height)//將argb byte數(shù)組轉(zhuǎn)成 int數(shù)組val pixels = IntArray(argb.size / 4)ByteBuffer.wrap(argb).asIntBuffer().get(pixels)//獲得bitmapreturn Bitmap.createBitmap(pixels, width[0], height[0], Bitmap.Config.ARGB_8888)}fun stream2Bytes(`is`: InputStream): ByteArray {val bos = ByteArrayOutputStream()val buffer = ByteArray(2048)var len: Inttry {while (`is`.read(buffer).also { len = it } != -1) {bos.write(buffer, 0, len)}} catch (e: IOException) {e.printStackTrace()}return bos.toByteArray()}private fun compressBitmap(bitmap: Bitmap, format: CompressFormat, file: String) {var fos: FileOutputStream? = nulltry {fos = FileOutputStream(file)} catch (e: FileNotFoundException) {e.printStackTrace()}bitmap.compress(format, 75, fos)if (null != fos) {try {fos.close()} catch (e: IOException) {e.printStackTrace()}}}源碼地址
總結(jié)
- 上一篇: 金正昆谈礼仪 - 电话礼仪
- 下一篇: 循环神经网络RNN、LSTM、GRU实现