姓名:王怀帅 学号:16040410035
转载自://www.greatytc.com/p/6ae497a5a3ec=有修改
【嵌牛导读】:Android Studio 自带的监视器(Monitors)非常有用,特别是其中监视内存那个区域,可以帮我们分析内存使用过高或内存泄漏的原因。
【嵌牛鼻子】:Android Studio
【嵌牛提问】:Android Studio自带内存监视器有什么妙用?
【嵌牛正文】:
1. 问题简述
最近几个月,我们公司的 Android app 进行了一次 UI(User Interface,用户界面)的大更新,新的用户界面比原先的高大上很多。
新界面的设计和素材制作外包给一家法国比较有名的设计公司。
开发这边要把美工设计的图片和界面应用到 Android 代码中。这部分主要是我来做,另一个同事也负责一部分。
现在新 UI 界面已经更新完毕,不过最近测试组发现了一个问题:在 Samsung Galaxy S5 上,app 在打了好几通加密电话后会崩溃。而在其他设备如 Samsung Galaxy A56,Samsung Galaxy S7 / S7 Edge 上则没见崩溃。
2. 寻找原因
这个问题可是非常严重, app 直接崩溃是非常影响用户体验的。因此组长很重视这个问题,拜托我尽快解决。
首先,我通过 adb 查看了一下 Logcat 输出的 Log (日志),很快发现了原来是内存使用过多,超出了承受范围。可以看到日志中有 「Out of memory」(内存用尽了) 的字段。
那么究竟是哪个部分使用了过多的内存呢?就得一点点「寻根究底」了。
3. Android Studio 监视器
Android Studio 绝对是目前开发 Android 的必备工具,因为是 Google 自己家的,所以功能强大,更新频繁。
如果你「不幸」还在用 Eclipse 这样的 Google 不再支持 Android 更新的软件来开发 Android,那我只能对你表示「同情」。
Android Studio 的下载地址,需要「番茄」:https://developer.android.com/studio/index.html
国内的朋友可以去这里,不需要「番茄」:http://www.android-studio.org/
开启 Android Studio 后,在左下方的栏目中可以看到 Android Monitor 这个菜单,点击之后会显示 Logcat 或 Monitors 的界面。
Logcat 就是显示日志的,而 Monitors 中是监视系统的各项数据。
monitor 是英语「监视器」的意思。我们切到 Monitors 那一栏,可以看到有四项数据,分别是 Memory (内存),CPU (中央处理器),Network (网络),GPU (图形处理器)。
如下图所示:
上图中我最关心的当然是 Memory 那一项了,因为我可以选择追踪连接了的哪个设备中的哪一个组件的内存使用情况。
比如上图中,我们选择的是 Samsung SM-G900F Android 4.4.2 API 19 这一个设备。
Samsung SM-G900F 是 Samsung S5 的型号。
Android 4.4.2 表示设备上安装的 Android 版本是 4.4.2。
API 19 表示 Android 的 API 版本是 19。
而被我用白色长条挡住的就是我们公司的产品的那个组件的名称。这样我就可以只监视某一个组件的内存使用了。
可以看到目前的内存使用是 15.89 MB。挺少的,因为我目前位于产品的 Portal(入口,门户)界面,还没进入任何一个子界面。
此时,我点击 Portal 界面上的 Voice 图标,进入 Voice(语音)子界面。 因为我们拨打加密电话就是在 Voice 子界面中进行。
进入 Voice 子界面后,可以看到内存使用如下图所示,迅速蹿高:
可以看到内存使用从原先的 15.89 MB 一下子攀升到 94.71 MB。苍了天了 ...
那究竟是 Voice 子界面中的哪个部分消耗了这么多内存呢?
Voice 子界面是一个 Activity,它有三个 Fragment,是配合 ViewPager 来实现 Fragment 之间的切换。这三个 Fragment 分别是 History,Contacts,Dialer,从左到右排列。
结构是类似下图这样的:
由于公司产品的保密性,我不能放出任何截图。
History 是通话的历史记录,是一个 ListView。
Contacts 是同一个或不同服务器上的其他用户,也是一个 ListView。
Dialer 是一个拨号界面,比较复杂,一共有三块内容,最上面是显示号码的区域,带有删除的键;中间一大块是键盘(0 到 9 这十个数字键,再加 # 键和 + 键。一共 12 个按键);最下面是一个拨打加密电话的按钮。
我初步估计内存占用的大头是 Dialer 这个拨号界面。因为它的 Layout 最复杂,有 LinearLayout,RelativeLayout 的嵌套。而且在 Layout 的编辑界面可以看到,Dialer 这个拨号界面竟然用了 36 张图片。
为了证实我的想法,我需要修改我们的 app 的代码。
为了保证 Voice 这个 Activity 中的三个 Fragment 切换顺畅,我们的代码中设置了 ViewPager 的
setOffscreenPageLimit(2);
参看 Android 官方开发页面:
从事 Android 开发的朋友对这个方法应该不陌生。
那么在我们三个 Fragment 的情况下,不管切换到哪一个 Fragment,都是一直保留三个 Fragment,不会销毁任何一个。
我把它改成:
setOffscreenPageLimit(1);
这样,当我进入到 Voice 这个 Activity 中的时候,因为默认是位于 History 这个 Fragment,那么只会加载 History 和 它右边的 Contacts 这两个 Fragment,而不会去加载 Dialer 这个 Fragment。
当我切换到 Contacts 这个位于中间的 Fragment 时,这三个 Fragment 都会被加载。我就可以看一下 只加载 History 和 Contacts 这两个 Fragment 与 加载三个 Fragment 时,内存使用有什么区别。
果不出所料,在内存监视器中,我发现:只加载 History 和 Contacts 这两个 Fragment,内存使用时 20 多 MB,而加载三个 Fragment 时就一下子攀升到 94.71 MB。
4. 解决内存使用过多
因此罪魁祸首就是 Dialer 这个 Fragment。
既然知道了内存使用过高的部分,那么我们就来优化一下。
进入 Dialer 这个 Fragment 的 Layout 文件(XML 文件),原本我以为是布局比较复杂的缘故。
我尽可能简化了布局,但是运行后占用内存几乎没什么改变。因为布局复杂影响性能并不是因为占用内存,而是嵌套所带来的绘制问题以及过度绘制,过度绘制占用的是 CPU 和 GPU。
我就想是不是每张图片占的资源多,图片数目又多的缘故。
我发现好些小部件的背景竟然用了两张不同的图片,分为「被按下」 和「没被按下」两种情况。
而拨号键盘上的 12 个按键的背景也是如此,每一个都用了两张图片来表示「被按下」 和「没被按下」两种情况。
而且 Dialer 这个 Fragment 中所用的图片的 resolution (分辨率)和图片本身大小都比较高。
因为我把背景都用 color (颜色值)来表示,去掉图片。把 Fragment 中所有的图片的分辨率和大小都重新设计,调到用户可接受的最小值。
经过优化,在进入 Voice 子界面后内存占用变成了如下图的 44.03 MB,比优化前的 94.71 MB 少了一半多。
经过测试,app 再也不会崩溃了。组长非常高兴,因为假如这个问题不解决,我们的新版代码就不能打包发给客户。
这篇文章因为没有公司产品的截图,因此纯文字的叙述比较难理解。还望大家见谅。
但解决这个问题,却让我受益匪浅。之前我一直用的比较多的是 Logcat 这个日志显示程序,没有怎么用过 Android Studio 自带的监视器。
现在我发现原来这个监视器这么有用,虽然在 Android 代码中我们也可以打印出内存使用量,但是没有监视器的实时变化来得直观。
5. 总结
Android 开发中,在不影响用户体验的前提下,尽量降低图片的大小和分辨率,可以大幅度减少内存使用。
Android Studio 绝对是 Android 开发必备工具。如果你还没有用这个 Google 的「亲儿子」而还在用 Google 已经停止更新支持的 Eclipse,那就太赶不上时代了。
Android Studio 自带的监视器(Monitors)非常有用,特别是其中监视内存那个区域,可以帮我们分析内存使用过高或内存泄漏的原因。当然这个监视器还有很多其他妙用,就有待大家去慢慢发觉了。