Android性能优化(一)

打算对性能优化写一个系列的文章,先以之前负责的项目为例,对性能方面的优化做个总结。
主要包括如下几个方面:

1、APK包的大小

APK包不能做的太大,太大了不太好推广,国内市场好像还好一些,国外市场就要受限了,因为在推广时会按包的体积大小收费,越精简,推广费用越低,越受欢迎;包越大,推广费用越高,推广者也不愿意接。
而对于普通的用户来说,肯定包越小,他下载的意愿会更强一些。
以下总结了一些压缩方向:

(1)、库

有多种方法,如果实现了同样的功能,可以用混淆的库代替源码库;库中的多国语言包,图片资源如果用不到可以删除;库中的某些模块用不到也可以将其删除。例如,google player service库,可以删除其中的无用部分。具体操作方法是通过解压缩工具,打开此库,然后将其中不用的模块删除即可。
具体哪些模块可以删除,可以参考,
http://stackoverflow.com/questions/21555662/android-app-size-increased-after-adding-admob-ads-using-google-play-services-lib

(2)、代码混淆

这个没有什么好说的,无论从代码安全性,还是压缩体积,删除无用代码,或者优化性能方面考虑,都需要进行混淆。混淆可以起到减少包大小的作用。

(3)、图片资源

删除无用的图片资源,可以借助Lint工具,查找无用的资源,然后依次删除。
还可以对图片进行压缩,可以使用工具,例如这个网站:https://tinypng.com/,提供在线压缩工具,直接将图片拖到压缩位置,然后点击压缩即可生成压缩文件,非常方便,一般能够将原始图片压缩60%-80%。

(4)、删除无用的代码,文字资源,xml等

Xml文件多了后会比较大,即使不使用,也会编译到apk包中,所以尽量删除无用的xml,代码和文字资源。

以上只是我在工作中实际操作的方法,而精简apk并不限于这些方法,还要根据项目的特性,具体情况还要具体分析,达到精简的目的。原理就是删除无用的,精简有用的,将包大小降到最低。

2、应用占用的内存

主要指Pss,它的意思是Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存),通过dumpsys meminfo 包名可以看到应用占用的内存大小,这个等同于在设置–应用中看到的service或者缓存大小。
例如,(表示项目包名,这里我将实际包名替换成了xxx.xxx.xxx)

shell@android:/ # dumpsys meminfo xxx.xxx.xxx
dumpsys meminfo xxx.xxx.xxx
Applications Memory Usage (kB):
Uptime: 71794048 Realtime: 122022986

    ** MEMINFO in pid 588 [xxx.xxx.xxx] **
             Shared  Private  Heap     Heap   Heap
               Pss    Dirty    Dirty     Size    Alloc     Free
            ------   ------   ------   ------   ------   ------
   Native        0        0        0     4364     3153        6
   Dalvik    19772     5680    19688    19260    18951      309
   Cursor        0        0        0
   Ashmem        0        0        0
 Other dev    20404    40840        0
 .so mmap     1502     2576     1228
.jar mmap        0        0        0
.apk mmap      112        0        0
.ttf mmap      435        0        0
.dex mmap     1736        0        8
   Other mmap      815      328      176
   Unknown     1778      296     1776
   TOTAL    46554    49720    22876    23624    22104      315

可以看到Pss总大小为46554kB。
具体哪些模块占用多少内存,哪些有内存泄漏的隐患可以通过MAT工具进行优化,查找内存使用大的模块,一般有图片相关的功能时,bitmap占用是大头。
例如,
以下是应用中切换自定义主题后测试版本占用内存的分析。
从MAT分析的结果看有四处:

(1)、xxx.xxx.xxx.ui.WindowView占用2,903,680 (15.52%) bytes。

(2)、xxx.xxx.xxx.ui.ViewPager使用的bitmap占用内存为2,764,648 (14.78%) bytes.

(3)、xxx.xxx.xxx.ui. FloatImageButton使用的bitmap占用内存为2,536,984 (13.56%) bytes.

(4)、” xxx.xxx.xxx.ThemeLogic使用的bitmap占用内存为1,963,280 (10.49%) bytes.

可以看到前3块内存是系统预置的图片占用的内存,第4块是管理自定义主题后占用的内存,他们的使用的都是和bitmap相关的,可以统一管理起来。

优化方向:

(1)、

Android有一个机制,应用退出后会有一个缓存一直存在,这样便于以后启动。但这个后台缓存作用其实并不大,并且第三方内存统计软件会将其统计到内存使用中,所以可以在应用退出时考虑将后台缓存清除。
方案是客户端程序退出10秒后,将后台缓存杀死。杀死后台缓存不会影响到后台进程,这样能降低到原来内存占用的一半或者不到一半的样子(对于双进程或者多进程的应用比较有效,因为界面进程退出后,它的服务进程并没有退出,所以可以考虑将界面进程即缓存杀掉)。

(2)、

去掉硬件加速配置android:hardwareAccelerated=”true”,可以节省内存。
硬件加速会占用一些内存,具体表现在使用 dumpsys meminfo统计到的Other dev字段中,去掉其后,内存占用经测试会小一些。但后果可能会影响画图的效率,但是否真正造成实际使用的影响,需要测试多验证,但从一些其它应用比如单手划划反编来看,都没有此设置,所以感觉可以去掉。

(3)、

修改图片缓存机制,将private ArrayList> CurrentBitmapsList = new ArrayList>()使用lrucache代替缓存。使用lrucache进行图片缓存,之前是解析每个自定义主题包后将bitmap文件缓存到内存中,不使用时进行释放,采用手动管理,其中有bitmap的强引用,虽然解决了之前outofmemory的问题,但还是没有达到对内存最优的管理方式。使用新结构lrucache 可以起到由系统回收bitmap的作用,并且可以限制它的大小。

(4)、

将预置的图片资源也要加到缓存中,切换自定义主题后可以删除。
因为预置的图片都是以类似.xml方式提供的,如下所示,

<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ ic_root " android:state_pressed="false"/>
    <item android:drawable="@drawable/ ic_root_pressed " android:state_pressed="true"/>
</selector>

要对其中的bitmap进行分开解析,然后存储。
将预置图片也保存到缓存中,和自定义主题统一管理。程序主要占用内存的地方是bitmap,只要将这部分内存降下来,并且合理管理起来,例如切换主题时不产生内存泄露,及时释放,就能将程序内存降下来。优化的方向是通过解析预置图片,将其保存到了 lrucache 结构中进行管理,也起到的很好的作用。通过MAT内存工具可以看到,在将预置主题切换到自定义主题时,之前出现的预置图片多占用的内存已经不存在了。

(5)、

确保系统中用到的资源都能放到缓存中,重点是自定义主题部分;方法同4介绍的相同。

以上就是针对内存优化的解决方案,优化方向主要是DALVIK层,对bitmap使用的优化。优化后在悬浮窗待机情况下,可以维持在一个较小的范围之内(从测试结果看有的手机保持在20M-30M左右)。
如果有时间和精力可以再研究一下Other dev,.so map,unknown占用的内存优化方法。

3、启动的service尽量少。如果启动多个可以将其放到一个进程中

这样用户在设置中看到的进程和服务会少一些。

4、耗电优化

这个主要是查找一下待机情况下是否有应用在工作,比如timer有没有停掉?程序退出了后台服务还在工作,和服务器端交互,是否在收发数据?有没有长连接等。
这个需要对项目代码有个充分的了解,再有一些预判的情况下,才能很好的定位问题。
还有一种方法,可以在debug模式下,通过打log来发现一些蛛丝马迹。

以上是根据之前负责的项目优化情况做的一个总结,大家可以参考学习,有问题也可以随时讨论。

Jason Xiao wechat
欢迎您扫一扫上面的微信公众号,订阅我的个人公众号!本公众号将以推送Android各种碎片化小知识或小技巧,以及整理Android面试知识点为主,也会不定期将开发老司机日常工作中踩过的坑,平时自学的一些知识总结出来进行分享。每天一点干货小知识把你的碎片时间充分利用起来。
坚持原创技术分享,您的支持将鼓励我继续创作,谢谢!