Android面试整理(8)-性能优化

性能优化

参考链接:Android 性能监测工具,优化内存、卡顿、耗电、APK 大小的方法
参考博客:http://mtancode.com/

1、 稳定(内存溢出、崩溃)
2、 流畅(卡顿)
3、 耗损(耗电、流量)
4、 安装包(APK 瘦身)

影响稳定性的原因很多,比如内存使用不合理、代码异常场景考虑不周全、代码逻辑不合理
等,都会对应用的稳定性造成影响。其中最常见的两个场景是:Crash 和 ANR,这两个错误
将会使得程序无法使用。所以做好 Crash 全局监控,处理闪退同时把崩溃信息、异常信息收
集记录起来,以便后续

1.启动 app 黑白屏优化

app 启动时间计算:adb shell am start -w packagename/activity
在这里插入图片描述
WaitTime 就是总的耗时,包括前一个应用 Activity pause 的时间和新应用启动的时间;

ThisTime 表示一连串启动 Activity 的最后一个 Activity 的启动耗时;

TotalTime 表示新应用启动的耗时,包括新进程的启动和 Activity 的启动,但不包括前一个应用 Activity pause 的耗时。也就是说,开发者一般只要关心 TotalTime 即可,这个时间才是自己应用真正启动的耗时。

(1)窗口优化

Application 的启动优化

Application#attachBaseContext()
Application 启动会经过 attachBaseContext()–>onCreate();这时大家从attachBaseContext 的生命周期联想到什么?没错就是 MultiDex 分包机制。想必大家都会发现,自从我们方法数超出了 65535 处理了分包之后,启动白屏/黑屏的问题就出现了,分包机制是导致冷启动缓慢的重要原因,而现在部分应用采用插件化的方式来避免 MultiDex带来的白屏问题,这虽然是一种方法,但是开发成本实在高,对于不少应用来说是不必要的。

我们来聊一下 MultiDex 优化,首先 MultiDex 可分成运行时和编译时两个部分:
编译期:将 App 中的 class 以某种策略拆分在多个 dex 中,为了减少第一个 dex 也就主 dex中包含的 class 数;
运行期: App 启动时,虚拟机只加载主 dex 中的 class。app 启动以后,使用Multidex.install,通过反射机制修改 ClassLoader 中的 dexElements 来加载其他 dex;

从网上的多篇实践分析中,他们主要采用的是异步方式。因为 App 起始会先加载主 dex 包,那么我们可以自主去处理分包的工作,我们将启动页和首页需要的库、组件等主要 class分在主 dex 中,从而达到精分主 dex 包的大小,具体的操作写法,大家可以参考网上 MultiDex启动优化文章,但是大家要注意在主 dex 的分包过程中,主 dex 经过我们一系列的优化操作减少了主 dex 的大小,因此也增大了 NoClassDefFoundError 的异常的可能,此时会导致我们的应用启动失败的风险,所以在优化后我们一定做好测试工作。

2.稳定——内存优化

内存抖动(代码注意事项):
内存抖动是由于短时间内有大量对象进出新生区导致的,它伴随着频繁的 GC,gc 会大量占用 ui 线程和 cpu 资源,会导致 app 整体卡顿。

避免发生内存抖动的几点建议:
(1)尽量避免在循环体内创建对象,应该把对象创建移到循环体外。
(2)注意自定义 View 的 onDraw()方法会被频繁调用,所以在这里面不应该频繁的创建对象。
(3)当需要大量使用 Bitmap 的时候,试着把它们缓存在数组或容器中实现复用。
(4)对于能够复用的对象,同理可以使用对象池将它们缓存起来。
(1)Memory Monitor 工具:
它是 Android Studio 自带的一个内存监视工具,它可以很好地帮助我们进行内存实时分析。
通过点击 Android Studio 右下角的 Memory Monitor 标签,打开工具可以看见较浅蓝色代表
free 的内存,而深色的部分代表使用的内存从内存变换的走势图变换,可以判断关于内存
的使用状态,例如当内存持续增高时,可能发生内存泄漏;当内存突然减少时,可能发生GC等。
(2)LeakCanary 工具:

这个工具是 Square 公司在 Github 开源的。 首先,笔者仔细查看了Leakcanary官方的github仓库,最重要的便是对Leakcanary是如何起作用的(即原理)这一问题进行了阐述,我自己把它翻译成了易于理解
的文字,主要分为如下 7 个步骤:

  • 1、RefWatcher.watch()创建了一个 KeyedWeakReference 用于去观察对象。
  • 2、然后,在后台线程中,它会检测引用是否被清除了,并且是否没有触发 GC。
  • 3、如果引用仍然没有被清除,那么它将会把堆栈信息保存在文件系统中的.hprof 文件里。
  • 4、HeapAnalyzerService 被开启在一个独立的进程中,并且 HeapAnalyzer 使用了 HAHA开源库解析了指定时刻的堆栈快照文件 heap dump。
  • 5 、 从 heap dump 中 , HeapAnalyzer 根 据 一 个 独 特 的 引 用 key 找 到 了
    KeyedWeakReference,并且定位了泄露的引用。
  • 6、HeapAnalyzer 为了确定是否有泄露,计算了到 GC Roots 的最短强引用路径,然后建立了导致泄露的链式引用。
  • 7、这个结果被传回到 app 进程中的 DisplayLeakService,然后一个泄露通知便展现出来了。

官方的原理简单来解释就是这样的:在一个 Activity 执行完 onDestroy()之后,将它放入 WeakReference 中,然后将这个 WeakReference 类型的 Activity 对象与ReferenceQueque 关联。这时再从 eferenceQueque 中查看是否有没有该对象,如果没有,执行 gc,再次查看,还是没有的话则判断发生内存泄露了。最后用
HAHA 这个开源库去分析 dump 之后的 heap 内存。

(3)Android Lint 工具:

3.流畅——卡顿优化

View 的绘制帧数保持 60fps 最佳,这要求没帧绘制时间不超过 16ms,如果不能在 16ms 内完
成界面的渲染,那么就会出现卡顿的现象。

  • UI 线程中做了耗时操作,导致 UI 线程卡顿
  • 布局层次嵌套过多,过于复杂,无法在 16ms 内完成渲染
  • 同一时间动画执行的次数过多,导致 CPU 和 GPU 负载过重
  • overDraw,导致像素在同一帧的时间内被绘制多次
  • view 频繁的触发 measure、layout,导致 measure、layout 类似耗时过多和整个 view频繁重新渲染
  • 频繁触发 GC,使得 16ms 无法完成绘制
  • ANR
(1)布局优化

在 Android 种系统对 View 进行测量、布局和绘制时,都是通过对 View 数的遍历来进行操作的。如果一个 View 数的高度太高就会严重影响测量、布局和绘制的速度。Google 也在其 API 文档中建议 View 高度不宜哦过 10 层。现在版本种 Google 使用 RelativeLayout替代 LineraLayout 作为默认根布局,目的就是降低 LineraLayout 嵌套产生布局树的高度,从而提高 UI 渲染的效率。

  • 布局复用,使用标签重用 layout;
  • 提高显示速度,使用延迟 View 加载;
  • 减少层级,使用标签替换父级布局;
  • 注意使用 wrap_content,会增加 measure 计算成本;
  • 删除控件中无用属性;
(2)启动优化

应用一般都有闪屏页 SplashActivity,优化闪屏页的 UI 布局,可以通过 Profile GPU Rendering 检测丢帧情况。

(3)优化工具

CPU Profile

4.节省——耗电优化

在 Android5.0 以前,关于应用电量消耗的测试即麻烦又不准确,而 5.0 之后 Google专门引入了一个获取设备上电量消耗信息的 API—— Battery Historian。BatteryHistorian 是一款由 Google 提供的 Android 系统电量分析工具,直观地展示出手机的电量消耗过程,通过输入电量分析文件,显示消耗情况

最后提供一些可供参考耗电优化的方法:

(1)计算优化。算法、for 循环优化、Switch…case 替代 if…else、避开浮点运算。

浮点运算:计算机里整数和小数形式就是按普通格式进行存储,例如 1024、3.1415926 等等,这个没什么特点,但是这样的数精度不高,表达也不够全面,为了能够有一种数的通用表示法,就发明了浮点数。浮点数的表示形式有点像科学计数法(.×10^),它的表示形式是 0.×10^,在计算机中的形式为 .* e ±**),其中前面的星号代表定点小数,也就是整数部分为 0 的纯小数,后面的指数部分是定点整数。利用这样的形式就能表示出任意一个整数和小数,例如 1024 就能表示成 0.1024×10^4,也就是 .1024e+004,3.1415926 就能表示成 0.31415926×10^1,也就是 .31415926e+001,这就是浮点数。浮点数进行的运算就是浮点运算。浮点运算比常规运算更复杂,因此计算机进行浮点运算速度要比进行常规运算慢得多。

(2)避免 Wake Lock 使用不当。

Wake Lock 是一种锁的机制,主要是相对系统的休眠而言的,,只要有人拿着这个锁,系统就无法进入休眠意思就是我的程序给 CPU 加了这个锁那系统就不会休眠了,这样做的目的是为了全力配合我们程序的运行。有的情况如果不这么做就会出现一些问题,比如微信等及时通讯的心跳包会在熄屏不久后停止网络访问等问题。所以微信里面是有大量使用到了Wake_Lock 锁。系统为了节省电量,CPU 在没有任务忙的时候就会自动进入休眠。有任务需要唤醒 CPU 高效执行的时候,就会给 CPU 加 Wake_Lock 锁。大家经常犯的错误,我们很容易去唤醒 CPU 来工作,但是很容易忘记释放 Wake_Lock。

(3)使用 Job Scheduler 管理后台任务。

在 Android 5.0 API 21 中,google 提供了一个叫做 JobScheduler API 的组件,来处理当某个时间点或者当满足某个特定的条件时执行一个任务的场景,例如当用户在夜间休息时或设备接通电源适配器连接 WiFi 启动下载更新的任务。这样可以在减少资源消耗的同时提升应用的效率。

5.安装包——APK 瘦身

(1)安装包的组成结构

在这里插入图片描述

assets 文件夹。存放一些配置文件、资源文件,assets 不会自动生成对应的 ID,而是通过AssetManager 类的接口获取。
res。res 是 resource 的缩写,这个目录存放资源文件,会自动生成对应的 ID 并映射到 .R文件中,访问直接使用资源 ID。
META-INF。保存应用的签名信息,签名信息可以验证 APK 文件的完整性。
AndroidManifest.xml。这个文件用来描述 Android 应用的配置信息,一些组件的注册信息、可使用权限等。
classes.dex。Dalvik 字节码程序,让 Dalvik 虚拟机可执行,一般情况下,Android 应用在打包时通过 Android SDK 中的 dx 工具将 Java 字节码转换为 Dalvik 字节码。
resources.arsc。记录着资源文件和资源 ID 之间的映射关系,用来根据资源 ID 寻找资源。

(2)减少安装包大小
  • 代码混淆。使用 IDE 自带的 proGuard 代码混淆器工具 ,它包括压缩、优化、混淆等功能。
  • 资源优化。比如使用 Android Lint 删除冗余资源,资源文件最少化等。
  • 图片优化。比如利用 PNG 优化工具 对图片做压缩处理。推荐目前最先进的压缩工具Googlek 开源库 zopfli。如果应用在 4.0 版本以上,推荐使用 WebP 图片格式。
  • 避免重复或无用功能的第三方库。例如,百度地图接入基础地图即可、讯飞语音无需接入离线、图片库 Glide\Picasso 等。
  • 插件化开发。比如功能模块放在服务器上,按需下载,可以减少安装包大小。
  • 可以使用微信开源资源文件混淆工具——AndResGuard 。一般可以压缩 apk 的 1M 左右大。

6.冷启动与热启动

app 冷启动: 当应用启动时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用, 这个启动方式就叫做冷启动(后台不存在该应用进程)。冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始化 Application 类,再创建和初始化 MainActivity 类(包括一系列的测量、布局、绘制),最后显示在界面上。
app 热启动: 当应用已经被打开, 但是被按下返回键、Home 键等按键时回到桌面或者是其他程序的时候,再重新打开该 app 时, 这个方式叫做热启动(后台已经存在该应用进程)。热启动因为会从已有的进程中来启动,所以热启动就不会走 Application 这步了,而是直接走 MainActivity(包括一系列的测量、布局、绘制),所以热启动的过程只需要创建和初始化一个 MainActivity 就行了,而不必创建和初始化 Application

冷启动的流程
当点击 app 的启动图标时,安卓系统会从 Zygote 进程中 fork 创建出一个新的进程分配给该应用,之后会依次创建和初始化 Application 类、创建 MainActivity 类、加载主题样式 Theme中的 windowBackground 等属性设置给 MainActivity 以及配置 Activity 层级上的一些属性、再 inflate 布局、当 onCreate/onStart/onResume 方法都走完了后最后才进行 contentView的 measure/layout/draw 显示在界面上冷启动的生命周期简要流程:

Application 构造方法 –> attachBaseContext()–>onCreate –>Activity 构造方法 –>onCreate() –> 配置主体中的背景等操作 –>onStart() –> onResume() –> 测量、布局、绘制显示

冷启动的优化主要是视觉上的优化,解决白屏问题,提高用户体验,所以通过上面冷启动的过程。能做的优化如下:
1、减少 onCreate()方法的工作量
2、不要让 Application 参与业务的操作
3、不要在 Application 进行耗时操作
4、不要以静态变量的方式在 Application 保存数据
5、减少布局的复杂度和层级
6、减少主线程耗时

7.内存泄漏的场景和解决办法

Android性能优化:手把手带你全面了解 内存泄露 & 解决方案

8. Bitmap 优化

Android中一张图片占据的内存大小是如何计算

9.LRU 的原理

为减少流量消耗,可采用缓存策略。常用的缓存算法是 LRU(Least Recently Used):当缓存满时, 会优先淘汰那些近期最少使用的缓存对象。主要是两种方式:
(1) LruCache(内存缓存):
LruCache 类是一个线程安全的泛型类:内部采用一个 LinkedHashMap 以强引用的方式存储外界的缓存对象,并提供 get 和 put 方法来完成缓存的获取和添加操作,当缓存满时会移除较早使用的缓存对象,再添加新的缓存对象。
(2)DiskLruCache(磁盘缓存):
通过将缓存对象写入文件系统从
而实现缓存效果

10.webview 优化

加载一个 webview 经历的过程如下:

  1. webview 的初始化
  2. 从服务器下载 html,css,js,image 等文件
  3. 解析 html,构建 dom 树,布局绘制
  4. 显示一个 webview 页面

优化点:

1.将 html,css,js,image 等资源文件放到本地,设计增量更新策略,对需要更新的部分进行 webpack 和 gzip 压缩和 cdn 加速处理,提神拉取速度。并且在进行网络链接时可以让前端请求的域名跟 API 域名保持一致,减少 DNS 解析时间。
2.通过 setBlockNetworkImage(boolean)来设置预先加载非图片部分的内容,延迟图片加载,在 onPageStart 屏蔽图片加载,在 onPageFinished 开启图片加载

11.如何避免 OOM?

使用更加轻量的数据结构:

如使用 ArrayMap/SparseArray 替代 HashMap,HashMap 更耗内存,因为它需要额外的实例对象来记录 Mapping 操作,SparseArray 更加高效,因为它避免了 Key Value 的自动装箱,和装箱后的解箱操作

便面枚举的使用,可以用静态常量或者注解@IntDef 替代
Bitmap 优化:
(1)尺寸压缩:通过 InSampleSize 设置合适的缩放
(2)颜色质量:设置合适的 format,ARGB_6666/RBG_545/ARGB_4444/ALPHA_6,存在很大差异
(3)inBitmap :使用 inBitmap 属性可以告知 Bitmap 解码器去尝试使用已经存在的内存区域,新解码的 Bitmap 会尝试去使用之前那张 Bitmap 在 Heap 中所占据的 pixel data 内存区域,而不是去问内存重新申请一块区域来存放 Bitmap。利用这种特性,即使是上千张的图片,也只会仅仅只需要占用屏幕所能够显示的图片数量的内存大小,但复用存在一些限制,具体体现在:在 Android 4.4 之前只能重用相同大小的 Bitmap 的内存,而 Android 4.4 及以后版本则只要后来的 Bitmap 比之前的小即可。使用 inBitmap 参数前,每创建一个 Bitmap对象都会分配一块内存供其使用,而使用了 inBitmap 参数后,多个 Bitmap 可以复用一块内存,这样可以提高性能 StringBuilder 替代 String:
在有些时候,代码中会需要使用到大量的字符串拼接的操作,这种时候有必要考虑使用StringBuilder 来替代频繁的“+”避免在类似 onDraw 这样的方法中创建对象,因为它会迅速占用大量内存,引起频繁的 GC 甚至内存抖动减少内存泄漏也是一种避免 OOM 的方法

12.ddms 和 traceView

13.性能优化如何分析 systrace?

14.用 IDE 如何分析内存泄漏?

15.Java 多线程引发的性能问题,怎么解决?

16.App 启动崩溃异常捕捉

17.自定义 View 注意事项

18.现在下载速度很慢,试从网络协议的角度分析原因,并优化(提示:网络的 5 层都可以涉及)。

19.Https 请求慢的解决办法(提示:DNS,携带数据,直接访问 IP)

20.如何保持应用的稳定性

21.RecycleView 优化

22.View 渲染

23.java 中的四种引用的区别以及使用场景

24.强引用置为 null,会不会被回收?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值