技术标签: java 性能优化 android android studio
该篇文章主要来介绍如何减少APK体积,以帮助用户更快地下载App,并加速安装/更新过程。
要查看APK文件中都包含哪些内容,有两种方式。第一种通过Android Studio的Analyze APK功能查看,该工具不仅可以还原XML类型代码的原始内容和各类资源文件,而且连Dex文件也能还原,比起第二种方式手动解压查看要简单直观得多;第二种则是直接将APK文件解压。则两种方式解压同一个app都是一样的文件结构,如下所示。
apk的内容结构根据App功能和打包时的具体操作差异,文件结构可能会有所区别,但大体相当。
无论采用上述哪种方式,我们都可以发现,这个APK文件内部主要是由5个文件和文件夹组成。
java -jar AXMLPrinter2.jar AndroidManifest.xml
需要特别注意的是,该工具不仅能还原AndroidManifest.xml,还能还原layout中的XML布局文件以及drawable中的XML代码文件,它几乎对所有XML文件都适用。
最后需要特别说明的是,由于不同APK在打包方式上有少许差异,文件结构可能与上述内容有所区别,但大多数只限于文件名或目录名的不同,其作用和结构仍保持一致。
如果我们想要尽可能让程序在尽可能多的设备上正常收到推送消息,通常的做法是集成各个厂商独立的推送库。但这也带来了负面作用——各库之间是否有冲突、多个推送服务同时运行,拖慢app的运行速度、apk的体积增大。
面对这种现象,理想的做法是相应版本只集成相应厂商的推送服务,比如发布到华为应用市场的APK只集成华为的推送服务,发布到小米应用市场的APK只集成小米的推送服务。最后,在做一个集成通用推送服务的版本,比如极光推送,用来发布到通用的应用市场上,比如应用宝、百度应用市场等。
减少APK体积的一种方法是分渠道打包,其主要思想是只保留该渠道所用的资源,去除其他渠道专用的资源。这里的资源包括程序代码、资源文件以及第三方库。下面我们介绍下多渠道打包的重要知识点。
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
signingConfig signingConfigs.debug
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
下面列举该节点下比较常用的配置参数。
参数名称 | 说明 |
---|---|
Debuggable | 定义该版本是否为可调试 |
minifyEnabled | 定义是否需要自动移除没有用的java代码 |
multiDexEnabled | 定义是否可以拆分多个dex包 |
signingConfig | 指定签名配置文件 |
versionNameSuffix | 定义VersionName的后缀 |
zipAlignEnabled | 定义是否启用zipAlign优化APk |
shrinkResources | 当其值为true时,编译相应版本时会省略没有用到的素材 |
除了上述完全自定义版本参数的方法外,还可以继承某个版本,对某些参数进行自定义,类似java中的Override。举个例子,现要定义名为custom的版本,当我们想集成Debug版本时,可以按如下方式配置:
custom.initWith(Debug)
custom{
zipAlignEnabled true
}
这样配置后,编译出的custom版本就是启用了zipAlign优化后的Debug版本,其他未在此配置的参数则与Debug版本的配置参数保持一致。
productFlavors {
huawei {
}
xiaomi {
}
}
下面列举该节点下比较常用的配置参数。
参数名称 | 说明 |
---|---|
ApplicationId | 完整的程序包名 |
ApplicationIdSuffix | 包名的后缀 |
versionName | 定义了该渠道版本的VersionName的值 |
versionCode | 定义了该渠道版本的VersionCode的值 |
dimension | 指维度,所有的Flavor都必须包含dimension定义 |
接下来,我们用一个实例来演示如何分渠道打包 APK。 项目需求如下:
为了统计用户对地图类 App的喜好,需要分不同渠道进行分发。渠道A 集成百度地图,在X应用市场上架:渠道B集成高德地图,在Y应用市场上架。
productFlavors{
baidu{
dimension "default"
}
amap{
dimension "default"
}
}
buildTypes 不做处理,Sync 成功后,打开 Build Variants 视图,可以看到一共可产出 4 个版本。
然后,打开Project 视图,并以Project 方式查看项目文件。依次展开 AndroidMultiApkDemo->app->src,对于新建的工程,src目录中通常包含 main、 test 和 androidTest 三个子日录。我们在src 中新建amap 和 baidu 两个新目录:将main 目录下的内容完整地复制到这两个新目录中。
之所以创建 amap 和 baidu 两个目录,目的在于分别编写两个渠道版本的代码。在编译时,系统将根据 productFlavors 中的版本名称与 src中的目录名自动匹配,从而将不同渠道的代码分开处理。而这两个目录中的内容与main 中的代码是继承关系,这就解释了为何我们无论选择哪种 BuildVariants, main 目录下的内容都为启用状态,且图标样式不变:
接下来,按照百度地图和高德地图的官方文档分别进行集成。完成后,在Build Variants 中选择相应的版本,再执行 Build APK 即可编译出对应版本。如果读者偏爱使用命令行,也可在工程根目录下执行
./gradlew assemble [渠道版本名]
例如: ./gradlew assembleamapDebug
即可生产出集成高德地图的 Debug版本。至此,项目需求已经可以完整地实现,可以分发了。似还有哪里不对劲,因为这样做会集成高德和百度地图的so库和jar包,我们需要将其分开来。
dependencies{
baiduImplementation files ('libs_baidu/BaidulBS Android.jar')
amapImplementation files ('libs_amap/...')
}
由于修改后的 jnilLibs 目录在编译时 可以自动识别,因此无须显示指定。
最后,分别再次编译这两个版本,可以看到APRK文件有明显的缩小。
针对某些开发场景,应用多渠道打包技术已经可以减少APk的体积了。但这还不够,我们还可以从资源文件入手,进一步地缩小APK的文件大小。
排除了上述例外情况后,就可以放心地对原有图片进行转换了。Android Studio中提供了快速方便地转换工具,可以对单个文件进行转换,也可以对整个目录下的图片进行转换。并且可以根据情况做出如下排除:
aaptOptions{
cruncherEnabled = flase
}
复用相似的图片
这个想必就不必多说了吧。对于一些图片可以通过旋转等变换得到所需图片的就不需要美术再出一张图片了。
应对多尺寸的9Patch处理方案
这里罗列一些注意事项:
众所周知,矢量图是经过矢量计算绘制出来的图形,它的特点是无论怎样放大和缩小,图像都不会失真。随之而来的另一个优点是节省空间。分辨率为100x100的矢量图和分辨率为1000x1000的图片都可以使用同一个文件,而无论是JPG、PNG还是WebP格式,对于相同内容的图片而言,文件尺寸的分辨率往往成正相关的关系。另外,虽然矢量图绘制路径可由开发人员完全自定义,但是实际上很少有人去改动它,因为它不是很直观,一般的人还真是改不了。
如果你正在开发的App确实有很多图片显示需求,而且都是GIF图,无法应用WebP转换,也无法压缩,那该怎么办?典型的例子就是聊天软件中的动画表情,它们大部分是GIF动图,而且数量十分庞大。解决方案就是资源后加载。
首先,在APK内部以ZIP格式压缩一些常用的动画表情,放到assets目录中,随APK分发。然后,在适当的时机完成其他动画表情的下载。这样一来,既保证了用户体验,又达到了给APK瘦身的目的。
代码混淆可以帮助我们很好地隐藏代码,防止被反编译。同时,还可以达到缩减发布安装包大小的目的。在早期版本(3.4版本前)的Android Studio中,代码混淆时由ProGuard来完成的。现在,Android官方采用R8编译器来处理代码混淆,且与ProGuard规则配置文件相兼容。
在使用 Android Gradle Pluein 3.4.0 及以上版本迸行编译时,R8编译器将会参与到构建流程中。当然,就提是我们启用了它,默认情况下是没有启用的。启用后,R8编译器将自动执行下列任务:
前文中己经讲过,默认情况下代码混淆并不生效,需要开发者手动启用它。这是因为如果默认启用,每次编评都会耗费大量的时间。另一方面,如果开发者并没有制定哪些类需要排除,极易触发运行崩溃。
启用代码混淆的方法在之前已经大致介绍过,主要是编辑Project中的build.gradle 文件,示例如下:
buildTypes {
release {
// Caution! In production, you need to generate your own keystore file.
// see https://reactnative.dev/docs/signed-apk-android.
signingConfig signingConfigs.debug
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
上述代码对release版本进行了混淆方式定义。
(1)在Proguard配置文件中 (通常是module 中的proguard-rules.pro)使用-keep来添加例外。比如,要对名为 MainActivity java 的类原样保留,通常的写法为:
-keep public class Mainactivity
(2)在要添加例外的类中通过@Keep 注解的万式漆加例外。当@Keep 注解作用与类时,整个类的内容特被保留:当@Keep 注解作用于 某个方法或变量时,相应的方法或变量将被保留。该方法只有迁移到 Androidx 后可用,否则请按照方式(1)来添加例外。
提醒读者:如果一个 App在混淆后反复崩溃,但无论从代码逻辑还是资源文件都查不出问题,那么可以关闭混淆后打包试试。
如果在没有启用混淆的情况 下程序可以正常运行,问题就出在混淆的步骤之中。通常的做法是看崩溃发生在哪个类,然后尝试将其添加到例外项中最后再次尝试运行。以上操作可以解决大部分由混淆带来的异常问题。
文章浏览阅读2.5k次。通常我们在应用中对mysql执行了insert操作后,需要获取插入记录的自增主键。本文将介绍java环境下的4种方法获取insert后的记录主键auto_increment的值:通过JDBC2.0提供的insertRow()方式 通过JDBC3.0提供的getGeneratedKeys()方式 通过SQL select LAST_INSERT_ID()函数 通过SQL @@IDENTITY 变量1.通过JDBC2.0提供的insertRow()方式自jdbc2.0以来,可以通过下面的方式执._mybatis auto_increment
文章浏览阅读6.4k次,点赞6次,收藏11次。此函数可以理解为形参,用于定义过程,在执行的时候再赋具体的值。不必指定初始值,可在运行时,通过 Session.run 的函数的 feed_dict 参数指定。这也是其命名的原因所在,仅仅作为一种占位符。tf.placeholder( dtype, shape=None, name=None)参数:dtype:数据类型。常用的是tf.float32,tf.float64等数值类型shape:数据形状。默认是None,就是一维值,也可以多维,比如:[None,3],表示_python placeholder
文章浏览阅读3.4k次。文章目录重启wifi概率性无法打开发现问题问题分析解决方法重启wifi概率性无法打开发现问题 最近在调试A100项目,建立在RK平台上的一个医疗随行包+智能音箱;在调试的过程中发现了一个bug:通过reboot命令重启的时候会概率性的出现WIFI打不开的情况;问题分析 根据查看kernel log,发现在sdio去探测设备的过程中,sdio报错了,导致无法探测到设备,以致于驱动..._rk重新上电概率打不开wifi蓝牙
文章浏览阅读4.2k次。文件系统的类型简介Linux支持多种文件系统类型,包括ext2、ext3、vfat、jffs、romfs和nfs等,为了对各类文件系统进行统一管理,Linux引入了虚拟文件系统VFS(Virtual File System),为各类文件系统提供一个统一的应用编程接口。根据存储设备的硬件特性、系统需求,不同的文件系统类型有不同的应用场合。在嵌入式Linux应用中,主要的存储设备为_系统用认识媒介类型是文件的什么
文章浏览阅读1.5k次。魅族新的手机型号为魅蓝U20发布了,,售价特公布了为千元级别手机,那么meizuU20手机配置如何呢?我们看看吧,,屏幕尺寸为5.5英寸,分辨率为1920*1080高清,系统是基于安卓的Flyme5系统,兼容安卓系统的APK格式文件安装和使用。处理器为HelioP101.8GHz(八核心)摄像头为1300万像素,前置为500万,。可运行内存为2GB,机身存储空间为16GB这个手机目前售价为..._1920*1080手机可以root的
文章浏览阅读112次。附件全国计算机等级考试调整方案2015年,考试中心组织召开了第六届全国计算机等级考试(NCRE)考委会会议,会议完成NCRE考委会换届选举,并确定了下一步改革目标。在新的历史时期,NCRE将在保持自身特色、稳定发展的基础上进一步考试改革。从2018年3月开始,将实施2018版考试大纲,并按新体系开考各个考试级别。具体调整内容如下:一、考试级别及科目1.一级新增“网络安全素质教育”科目(代码:17)..._二级mysql报名
文章浏览阅读9.7k次。1、简介检测android应用程序安全漏洞,可以用于已打包但是未加固的app或者源代码。https://github.com/linkedin/qark2、安装要求Tested on Python 2.7.13 and 3.6 Tested on OSX, Linux, and Windows现有win10安装pip install qark安装成功后可以使用一下命令查看qark --help安装反编译工具_jadx:https:._qark
文章浏览阅读1.1w次,点赞7次,收藏18次。相关文章: 校验码——码距 校验码——海明码及码距 校验码——CRC循环冗余校验码 一、码距二、奇偶校验码 奇偶校验码是一种增加二进制传输系统最小距离的简单和广泛采用的方法。例如,单个的奇偶校验将使码的最小距离由一增加到二。 一个二进制码字,如果它的码元有奇数个1,就称为具有奇性。例如,码字“10110101”有五个1,因此,这个码字具有奇性。同样,偶性码字具有偶数个1。注意奇性检测等效于所有码元的模二加,..._奇偶校验题目
文章浏览阅读4k次,点赞9次,收藏10次。25.请编写一个函数fun,它的功能是:比较两个字符串的长度,(不得调用C语言提供的求字符串长度的函数),函数返回较长的字符串。若两个字符串长度相同,则返回第一个字符串。例如,输入:beijing shanghai(为回车键),函数将返回shanghai。#include <stdio.h>char *fun(char *s1,char *s2){//考察传递字符串 char *p=s1; char *q=s2; int m=0; int n=0; while(*p){ _3、(串比较):编写一个函数fun,功能是对两个字符串进行比较;在主函数中输入两个字 符串,调用fun函数完成串比较,在主函数中输出这两个字符串的比较结果。要求用指针完成fun函数,不得使用strcmp库函数。
文章浏览阅读4.5k次。这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入欢迎使用Ma..._pycharm r日志详情
文章浏览阅读176次。该类从名字看就是IO工具类。同样类声明为final,构造为private,方法都是static。这些是工具类的标配呀!源码:/** * Provides I/O operations * * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) * @since 1.0.0 */public final _universal_utils
文章浏览阅读549次。本帖只展示部分演示站 需了解更多请移步注册http://console.open.onebound.cn/console/?i=Rookie代购业务近年兴起的一种购物模式,是帮国外客户购买中国商品。主要通过外贸代购模式,把淘宝、天猫等电商平台的全站商品通过API接入到你的网站上,瞬间就可以架设一个有数亿产品的大型网上商城,而且可以把这些中文的商品全部自动翻译成各国语言,能让国外客户看懂,直接在网站上下单,然后网站运营方代为购买再邮寄给客户,收取商品差价以及代购费和运费,利润可观,市场巨大。目前跨境