「全面理解Android内存优化 3」-从理论到实践

前言

本篇文章是《全面理解Android内存优化》系列文章的最后一篇。系列的主要目的是希望将Android开发中涉及性能优化的部分做一次系统的归纳、总结和学习。本系列文章包含理论基础工具使用项目实践三个部分。

理论基础:「全面理解Android内存优化 1」-Android的内存机制与管理建议,主要讲解Android性能优化时涉及到的各种基础知识

工具使用:「全面理解Android内存优化 2」-内存优化工具的使用,主要讲解Android性能优化时各种常用工具的使用。

项目实践:「全面理解Android内存优化 3」-从理论到实践,以一个实际APP为例,总结在在开发中会被忽视的内存问题。

本文中实战时使用的项目地址:https://github.com/linux-link/Fan,可以先阅读这篇文章了解这个项目一次组件化与Android Jetpack的实践

本篇属于三个部分中的项目实践部分。

目录

  • 关闭无用的Service
  • 多进程WebView的优化
  • 避免一图毁十优
  • 总结

正文

一、关闭无用的Service

《饭fan》是一个单Activity多Fragment的APP,在App的入口Activity同时启动了两个Service,TinkerService用于检查热修复补丁,UpdateService用于检查是否有更新。

在操作APP一段时间后,使用Memory Profiler检查内存,得到下图

「全面理解Android内存优化 3」-从理论到实践_第1张图片
image

可以看到内存中依然存在TinkerService和UpdateService。没有特殊指定的service是运行在主线程中的,这些已经无用的Service会拖慢主线程并占据主进程的可用内存。

解决方案

  • 调用stopService或stopSelf关闭这些service

关闭service后再次使用Memory Profiler检查内存,可以看到,APP占用的总内存已经减少了

「全面理解Android内存优化 3」-从理论到实践_第2张图片
image

二、多进程WebView的优化

从1.0.3版开始《饭fan》中集成了一个简单的商城系统,商城系统的制作参考了
慕课网的一个课程—《混合开发入门 主流开发方案实战京东移动端APP》。

商城系统集成完毕后,调试过程中,LeakCanary提示,ShoppingActivity发生了内存泄漏,如下图所示

「全面理解Android内存优化 3」-从理论到实践_第3张图片
image

WebView应该是Android中最容易发生内存泄漏的系统组件,往往都是Activity退出时,WebView依然持有activity的引用,导致Activity发生泄漏。 网络上有很多如何防止WebView产生泄漏,但是效果都不好,有的甚至根本没有效果。

解决方案

  • 让持有webview的Activity独立运行在一个进程,在activity的onDestroy中关闭这个进程

让Activity独立运行在一个进程中,可以彻底清除掉webview以及Activity
,但是让持有webview的Activity独立在一个进程中,会产生另一个问题——长时间的白屏。
webview本身初始化以及载入Html页面都需要一定的时间,这段时间会产白屏。
如果在启动Activity时需要额外再创建一个进程,那么白屏的时间就会进一步拉长,有时甚至长达4-5秒。

《饭fan》中针对长时间这个问题,又做了进一步优化。

  • 1.在app启动时,同时启动一个ShoppingService。ShoppingService运行在与WebViewActivity相同的进程中,退出WebViewActivity后当前进程会被关闭,在适当时候再重启ShoppingService。
  • 2.引入腾讯的x5WebView和VasSonic,加快webview初始化速度,同时也提高了WebView在各个系统上兼容性。
  • 3.在webview初始化的同时,使用APP内网络框架来请求Html页面中所需的数据。通过并行的方式,节省webview的加载数据的时间。

优化步骤大致就是以上这些,具体实现的代码请参考《饭fan》中Component_shopping组件。

三、Bitmap造成的内存泄漏

在Android内存优化中有“一图毁十优”的说法,一般普通的内存泄漏浪费的内存都在几十KB到几MB之间,但是一个bitmap泄漏就有可能浪费几十MB的内存空间,所以bitmap的优化一直是Android内存优化的重中之重。所以我们接下来的就重点介绍Bitmap的优化方案。

  • 1.使用RGB_565解码图片

在开发中大多数的图片加载框架的默认解码方案是ARGB_8888,这种解码方案,每个像素占4个字节,其实还有一种图片解码方案是RGB_565,这种解码方案,每个像素占2个字节,但是在视觉效果上与ARGB_8888差距并不明显。

所以一些页面的缩略图、背景图片以及一些用户感官上认为它就是缩略图的地方可以使用RGB_565来解码,在减小内存占用上,有立竿见影的效果,强烈推荐使用。

  • 2.不要乱放图片

在开发中我们往往会要求美工一张图标切3到5套不同尺寸的,然后分别放置在res下不同的资源目录里面

目录 对应的dpi
res/drawable 0
res/mipmap-lidp 120
res/mipmap-mdpi 160
res/mipmap-hdpi 240
res/mipmap-xhdpi 320
res/mipmap-xxhdpi 480
res/mipmap-xxxhdpi 640

Android有一套特殊的适配策略,对放在mipmap目录的图标会忽略屏幕密度,会去尽量匹配大一点的,然后系统自动对图片进行缩放,从而优化显示和节省资源。图片的缩放比率=手机的dpi / mipmap目录的dpi。

放在drawable目录下的会根据ROM的不同得到一个默认的dpi,但是这个dpi并一定是手机屏幕的实际dpi。

例如:如果我们将一张500X500的图标仅放在ldpi(120)下,那么在在480dpi的手机上实际的显示尺寸是2000X2000。

当我们分不清图标应该放在哪个目录下时,应该尽量将高品质的图片放在高密度目录下,这样能控制图片的缩放比率小于1,保证画质的前提,内存也是可控的。

  • 3.控制那些不可控的图片

这是什么意思呢,举一个我曾经实际遇到的例子,我们的APP有一个课件的功能,允许教师上传课件,服务器会把这些课件转成图片返回给APP显示,有个老师上传了一篇PDF格式的论文,服务器转换后每个图片足足有4000X8000这么大,加载每张图片需要消耗内存4000X8000X4/1024/1204=122MB,直接导致了OOM。

在这个例子中教师上传的课件转换后的图片就属于不可控图片,如果服务器不做过滤,那么APP就需要对这些用户上传的图片特殊处理。

处理步骤如下:

  • 从服务器下载的图片获取它的高度和宽度
  • 对于高度或宽度大于手机屏幕尺寸的图片计算缩放比率,并做缩放解码
  • 要对所有的图片解码API(decodexxxx)做OutOfMemoryError的异常处理

具体的代码请参考BitmapUtils。

四、总结

本篇主要总结了一些开发中常常被忽视的内存问题,其它常见的内存问题,在第一篇文章已经有所提及,不过正是这些被忽视的问题切切实实地占据了手机中大量的内存,这些问题其实才是我们更应该关注的重点。

参考资料

《Android移动性能实战》-腾讯SNG专项测试团队 编著

你可能感兴趣的:(「全面理解Android内存优化 3」-从理论到实践)