APK是什么?

首要看一下APK包的构成,Android的APK包和Windows运用程序装置包是不同的, Apk装置包本质上是一个zip紧缩文件, 解压之后咱们可以看到其中包含的内容:

【Android】浅谈APP的瘦身之路

  • META-INF目录:包含两个签名文件(CERT.SF和CERT.RSA),以及一个manifest文件(MANIFEST.MF)。
  • assets目录:包含工程中的asset目录下的文件,可以运用AssetManager获取。
  • res目录:包含那些没有被编译到resources.arsc的资源。
  • lib目录:包含适用于不同处理器的第三方依靠库,这里边可以有多个子目录,比方armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, 以及mips。
  • resources.arsc文件:存储编译好的资源,包含项目工程中的res/values目录里的xml文件,它们都被编译成二进制格式,也包含一些途径,指向那些没有被编译的资源,比方layout文件和图片。
  • classes.dex文件:项目中的java类都被编译到该dex文件,这个文件可以被Android的Dalvik/ART虚拟机解析。
  • AndroidManifest.xml:二进制格式的manifest文件,这个文件是必须的。

这些文件是Android体系运转一个运用程序时会用到的数据和代码,下面介绍体系怎么装置一个APK包

APK装置过程

一般的装置有四种办法:

1.体系运用装置:开机时加载体系的APK和运用,没有装置界面。

2.网络下载运用装置:通过各种market运用完成,没有装置界面。

3.ADB东西装置:即通过Android的SDK开发tools里边的adb.exe程序装置,没有装置界面。

4.通过第三方运用来装置:通过SD卡里的APK文件装置(比方双击APK文件触发),有装置界面,体系默许已经装置了一个装置卸载运用的程序。

装置运用程序最常用的办法便是在PC上运转命令adb install 加APK的文件途径,回车等待Android设备装置完成,装置成功命令行会显示Success。过程如下:

默许情况下APK会被拷贝到/data/app目录下,这个目录用户是有拜访权限的;adb install 最终会发送shell:pm命令,向体系的PackageManagerService(PMS)进程发送音讯,通知其装置apk包; apk在装置的时候,会将app的依靠的第三方动态链接库拷贝到/data/app/包名-XXX==/lib/渠道名的目录中。 然后,在/data/data/目录下创建运用程序的数据目录(以运用的包名命名),存放运用的相关数据,如数据库、xml文件、cache、二进制的so动态库等等。 解析apk的AndroidManifinest.xml文件并写入/data/system/packages.xml中,包含apk的name、codePath、flags、ts、version、uesrid等信息。解析完apk后将更新信息写入这个文件并保存,下次开机直接从里边读取相关信息添加到内存相关列表中。当有apk晋级,装置或删去时会更新这个文件。

APK为什么要减肥?

装置包要减肥的主要原因便是考虑运用的下载转化率和留存率. 运用太大了, 用户或许就不下载了, 特别是移动网络或许流量收费的情况下. 再者, 由于手机空间问题, 用户有时候或许需求挑选卸载一些运用, 就会先盯上那些占空间大的, 所以运用巨细也会也影响留存率.

Google Play 运用商场强制要求超越 100MB 的运用只能运用 APK 扩展文件办法 上传。假如想防止运用扩展文件,而且想要运用程序的下载巨细大于100 MB,则应该运用 Android App Bundles 上传运用程序,此刻运用程序最多可提供150 MB的紧缩下载巨细。

途径协作商的要求,当咱们的 App 做大之后,或许需求跟各个手机厂商协作预装,只要达到相应的要求后才允许你的 App 预装到手机上。

APK体积庞大后对APP性能也会发生影响。App装置时刻: 虽然Android 7.0 之后有了混合编译,牵强可以接收,可是其签名校验时刻也会变长。运转时内存: Resource 资源、Library 以及 Dex 类加载都会占用运用的一部分内存。ROM 空间: 由于闪存的这种工作办法,必须擦除改写的闪存部分比新数据实际需求的大得多。即最终或许导致实际写入的物理资料量是写入资料量的多倍。

APK包巨细指标?

文件尺度下载尺度装置尺度

APK包巨细缩小办法?

一、资源减重

在项目中往往会有一些由于产品的改变或许其他原因而发生一些无用资源,咱们apk减肥的第一步便是要移除这些无用资源。

1.优化assets和res中的资源文件

这个目录一般都是图片资源占空间比较多,特别当App为了适配多种分辨率而存放了多套图时,这时候就会导致res目录打下会非常大。

【Android】浅谈APP的瘦身之路

以PNG资源为例,PNG 图片相关于 JPEG 图片来说,它是一种无损的图像存储格式,同时多了一条透明度A通道,所以一般情况下,PNG 图片要比 JPEG 图片要大;将PNG图片通过 AndroidStudio 转化东西将PNG/PSD图片转为 SVG图片。或许Indexed_color 和 Color_quantization办法对图片进行紧缩。

资源紧缩:AndResGuard缩小APK巨细的东西,他的原理类似Java Proguard,可是只针对资源。他会将本来冗长的资源途径变短,例如将res/drawable/xxxxxxx 变为 r/d/x。

还可以做:只保存一套图运用webp替换png非重要图片动态加载运用lint删去无用资源翻开shrinkResources

【Android】浅谈APP的瘦身之路

2.lib目录优化

三方库处理 在开发过程中,咱们由于事务需求,或许会引用很多第三方库,而这些库或许有着相同功用。比方Picasso和Glide,这两个库都是图片加载的功用。假如没有特殊要求的话,根据场景的挑选任选其一即可。

当然,本着减肥的准则,咱们一般挑选一个包体积更小的较好。 关于第三方库的办理,一般主张封装一个统一办理三方库的基础库,这样有利于后期库的维护 假如咱们需求某个库的某小部分功用,那么主张便是仅引进所需部分功用代码即可,假如咱们运用了Picasso,可是咱们只需求它支撑webp功用,那么这时候咱们就只引进webp功用就好了 准则便是 能小则小,不必最好。

远程so

so库便是由ndk编译出来的动态库,app运转在不同的手机中,而so库是由c\c++编译的,不是跨渠道的,所以不同渠道(不同CPU)需求运用不同的so库。

ABI 是运用程序二进制接口简称(Application Binary Interface),界说了二进制文件(特别是.so文件)怎么运转在相应的体系渠道上,从运用的指令集,内存对齐到可用的体系函数库。在Android 体系上,每一个CPU架构对应一个ABI,即:armeabi,armeabi-v7a,arm64-v8a,x86,x86_64,mips,mips64。

ABI Supported Instruction Set(s)
armeabi ARMV5TE and later,Thumb-1
armeabi-v7a armeabi,Thumb-2,VFPv3-D16,Other,optional
arm64-v8a AArch-64
x86 x86(IA-32),MMX,SSE/2/3,SSSE3
x86_64 x86-64,MMX,SSE/2/3,SSE3,SSE4.1,SSE4.2,POPCNT
mips MIPS32r1 and later
mips64 MIPS64r6
  • 将所有的so文件都放到armeabi文件夹下,通过获取用户的CPU架构进行动态加载
  • 可以将需求的so文件上传到服务器,然后先获取用户的CPU架构,再从服务器上下载对应的so文件进行加载
  • 通过插件化插拔的办法进行下发
3.旧的事务逻辑代码下线

跟着版别的迭代,部分功用或许已被去掉,可是其代码还存在项目中。僵尸代码过多也会导致Dex文件过大。移除无用代码以及无用功用,有助于减少代码量,直接表现便是Dex的体积会变小。

调试代码独自办理,做到阻隔,禁止release包运用,如下:

【Android】浅谈APP的瘦身之路

dependencies{
......
  
 debugImplementationproject(path:':DaiMao-testUnit')//debug引进
 releaseImplementationproject(path:':DaiMao-testUnit')//release引进
 
......
}

二、项目混杂

Java字节码中包含了很多源代码信息,如变量名、办法名,而且通过这些称号来拜访变量和办法,这些 符号带有许多语义信息,很简单被反编译成 Java 源代码。为了防止这种现象,咱们可以运用 Java 混杂器对 Java 字节码进行混杂。

混杂可以检测并移除未运用到的类、办法、字段以及指令、冗余代码,并可以对字节码进行深度优化。最终,它还会将类中的字段、办法、类的称号改成简短无意义的名字。除此之外增加代码被反编译的难度,一定程度上确保代码的安全。

#---------------------------------基本指令区----------------------------------
# 代码混杂紧缩比,在0~7之间
-optimizationpasses5
# 混合时不运用巨细写混合,混合后的类名为小写
-dontusemixedcaseclassnames
# 指定不去忽略非公共库的类
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
# 不做预校验,preverify是proguard的四个步骤之一,Android不需求preverify,去掉这一步可以加速混杂速度。
-dontpreverify
-verbose
# 防止混杂泛型
-keepattributesSignature
-printmappingproguardMapping.txt
# 保存Annotation不混杂
-keepattributes*Annotation*,InnerClasses
#google推荐算法
-optimizations!code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
# 防止混杂Annotation、内部类、泛型、匿名类
-keepattributes*Annotation*,InnerClasses,Signature,EnclosingMethod
# 重命名抛出反常时的文件称号
-renamesourcefileattributeSourceFile
# 抛出反常时保存代码行号
-keepattributesSourceFile,LineNumberTable
#---------------------------------默许保存区---------------------------------
# 处理support包
-dontnoteandroid.support.**
-dontwarnandroid.support.**
# 保存承继的
-keeppublicclass*extendsandroid.support.v4.**
-keeppublicclass*extendsandroid.support.v7.**
-keeppublicclass*extendsandroid.support.annotation.**
# 保存R下面的资源
-keepclass**.R$*{*;}
# 保存四大组件,自界说的Application等这些类不被混杂
-keeppublicclass*extendsandroid.app.Activity
-keeppublicclass*extendsandroid.app.Application
-keeppublicclass*extendsandroid.app.Service
-keeppublicclass*extendsandroid.content.BroadcastReceiver
-keeppublicclass*extendsandroid.content.ContentProvider
-keeppublicclass*extendsandroid.preference.Preference
-keeppublicclass*extendsandroid.app.backup.BackupAgentHelper
-keeppublicclass*extendsandroid.view.View
# 保存在Activity中的办法参数是view的办法,
# 这样以来咱们在layout中写的onClick就不会被影响
-keepclassmembersclass*extendsandroid.app.Activity{
 publicvoid*(android.view.View);
}
# 关于带有回调函数的onXXEvent、**On*Listener的,不能被混杂
-keepclassmembersclass*{
 void*(**On*Event);
 void*(**On*Listener);
}

详细哪些地方给你减肥了,请看以下文件

dump.txt
DescribestheinternalstructureofalltheclassfilesintheAPK.
mapping.txt
Providesatranslationbetweentheoriginalandobfuscatedclass,method,andfieldnames.
seeds.txt
Liststheclassesandmembersthatwerenotobfuscated.
usage.txt
ListsthecodethatwasremovedfromtheAPK.

三、项目插件化

假如运用了插件化后,需求对整个项目进行重构,这种方案相比起改改Dex和图片,整个工程量极大,可是收益也会很高。

【Android】浅谈APP的瘦身之路

publicvoidenterShadow(ContextreactContext,StringparamKey,StringjsonParams,PluginInfoBeanpluginInfoBean) {
        //    /data/user/0/ 运用包名/files
   HostUiLayerProvider.setParams(paramKey,jsonParams);
   // 得到sp数据中的值
   Stringstart_pluginVersion=KvSpUtil.Companion.getSIntance().decodeString(pluginInfoBean.pluginVersionKey);
   StringpluginName=pluginInfoBean.pluginName;
   StringpluginDir=FILE_DIR_PLUGIN;
   Filepluginfile=newFile(pluginDir);
   if(TextUtils.isEmpty(start_pluginVersion)) {//本地不存在乐屋装修包
     if(pluginfile.exists()) {//防止下载一半封闭app
       pluginfile.delete();
     }
     checkPluginFiles(reactContext,pluginInfoBean);
   }else{
     if(start_pluginVersion.equals(pluginInfoBean.pluginVersion)) {//本地存在乐屋装修包且不需更新
       checkPluginFiles(reactContext,pluginInfoBean);
     }else{//本地存在乐屋装修包但需求更新
       if(pluginfile.exists()) {
         pluginfile.delete();
       }
       checkPluginFiles(reactContext,pluginInfoBean);
     }
   }
   if(!isLoaclManager) {
     //manager
     StringmanagerName=DW_PLUGIN_MANAGER_NAME;
     StringmanagerDir=FILE_DIR_MANAGER;
     Filemanagerfile=newFile(managerDir);
     // 得到sp数据中的值
     Stringstart_managerVersion=KvSpUtil.Companion.getSIntance().decodeString("start_managerVersion");
     if(TextUtils.isEmpty(start_managerVersion)) {//本地不存在乐屋装修包
       if(managerfile.exists()) {
         managerfile.delete();
       }
       checkManagerFiles(reactContext,pluginInfoBean.managerVersion,pluginInfoBean.managerUrl);
     }else{
       if(start_managerVersion.equals(pluginInfoBean.managerVersion)) {//本地存在乐屋装修包且不需更新
         checkManagerFiles(reactContext,pluginInfoBean.managerVersion,pluginInfoBean.managerUrl);
       }else{//本地存在乐屋装修包但需求更新
         if(managerfile.exists()) {
           managerfile.delete();
         }
         checkManagerFiles(reactContext,pluginInfoBean.managerVersion,pluginInfoBean.managerUrl);
       }
     }
   }
 }

小注:APK剖析东西

ApkTool反编译东西剖析 APK

运用 android-classshark 进行 APK 剖析

redex优化

XZ Utils 进行 Dex 紧缩

运用Simian东西扫描重复代码

总结

【Android】浅谈APP的瘦身之路

最终成果:

【Android】浅谈APP的瘦身之路

下一步方案

项目的糜烂代码管理继续进行!

可继续的常态化管理模式,减肥相关的技能探究。包体积监控应该作为发布流程的一个环节,最好是做到渠道化、流程化,否则很难继续,也许通过几个版别包体积又涨上来了。接下来会进行发版版别比对,当时版别与上一个版别的包巨细做对比,超越500KB需求批阅,临时批阅需求给出后续优化方案等等。