欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android开发中常见内存问题与优化避免

程序员文章站 2022-05-07 22:05:31
android开发中常见内存问题与优化避免,比起前几年,现在的 android 设备拥有更大的内存。但是,即使现在可以使用更多的内存,也是有一个上限的,具体大小和各个厂商的设置有关...

android开发中常见内存问题与优化避免,比起前几年,现在的 android 设备拥有更大的内存。但是,即使现在可以使用更多的内存,也是有一个上限的,具体大小和各个厂商的设置有关。如果内存使用不当,还是会影响到app的性能。内存问题主要有两类,一是内存溢出,二是内存泄漏。解决内存问题,主要靠借助工具检测分析,然后做代码优化。

一、android 应用开发中的内存问题

1.1 单个进程可用内存限制

目前的 android 设备,动辄4g、6g甚至8g内存,但是,android设备的内存再大,每个应用可以使用的内存大小也是有限制的,具体可以使用的最大内存和各大厂商的出厂设置有关。
可以通过adb 查看应用可以使用的最大内存:

adb shell cat /system/build.prop
// ...
dalvik.vm.heapstartsize=8m
dalvik.vm.heapgrowthlimit=256m
dalvik.vm.heapsize=512m
// ...

上面的设备是 galaxy s7,可以使用的最大内存为512m,这需要在 manifest.xml 中 application 节点添加 android:largeheap=”true”,否则,最多只能使用256m。
也可以直接查看它们的值:

adb shell getprop dalvik.vm.heapgrowthlimit
256m
adb shell getprop dalvik.vm.heapsize
512m
adb shell getprop dalvik.vm.heapstartsize
8m

现在的应用,对于图片质量的要求越来越高,动辄几百k,甚至上m一张图片,对于需要加载大量图片的应用来说,256m内存,分分钟就上去了。256m说大不大,说小不小,但是使我们的应用尽量少用内存总不会错。因为占用内存越大,gc频率就可能越高,然后就会出现卡顿的现象。

1.2 内存溢出

内存溢出就是你要求分配的内存超出了能给你的,系统不能满足需求,于是产生溢出,简单来说就是,需要的内存超过了可以使用的最大内存值。一旦出现内存溢出,就会报 out of memory 的错误,app crash。 导致内存溢出的原因有很多,这里根据项目中曾经出现过的做一个总结:

直接加载原图
需要浏览大图的时候,使用 imageview 直接加载原图;或者需要加载bitmap时直接读取了原图大小。 使用图片背景
app的每个页面都使用一张图片作为背景(表示对图片背景深恶痛绝),导致内存暴增,然后oom。 接口处理太多数据
有接口需要将文件如图片等进行urlencode为字符串,然后通过接口上传到服务器,然后一次处理多个文件,导致oom。 内存泄露并堆积
activity和对话框等内存泄露,得不到释放,导致oom。

1.3 内存泄漏

内存泄漏是指程序在申请内存后,无法释放已申请的内存空间。内存泄露和内存溢出是有区别的,内存溢出必然导致oom,而内存泄露则可能导致内存溢出。内存泄露的原因有很多,下面是比较常见的几个原因:

静态变量导致内存泄露 非静态内部类导致内存泄漏,如handler等,持有activity的引用无法释放
activity中非静态内部类如handler等,并不是总会引起内存泄漏,只是可能。 线程未完成任务导致activity不能释放,内存泄漏,如thread、asynctask、timer和timertask等。 webview导致内存泄漏 错误使用单例模式(持有activity的context) 资源未关闭造成的内存泄漏(io操作,操作) 属性动画造成内存泄露 未取消注册或回调导致内存泄露

二、内存优化方法

内存优化可以从四个方向进行:开源,节流,复用,回收。开源,即必要的时候使用largeheap、多进程等,使应用可以使用更多的内存,需要慎重考虑,因为largeheap和多进程引进的问题比它们带来的优化更多。节流,减少不必要的内存分配,这个需要在有一个良好的编码习惯,是每个开发者应该尽力做到的。复用,顾名思义,就是可以复用的对象就不要重复创建,如listview 使用viewholder进行优化就是复用了view。

2.1 开源

让应用使用最大内存

让应用可以使用最大内存,只需要在 manifest.xml 中设置 android:largeheap=”true” 即可。这个设置效果还是很明显的,如需要加载一张原图,没有添加这个属性之前,使用bitmap.setresources等方法时应用可能直接就 crash 了,而增大应用可用内存后,加载一张原图可能就不会导致 crash 了。

多进程

尽量不要使用多进程,因为多进程带来的问题可能比其带来的优化还要多,如果不了解,不熟悉,就不要使用。但是,当有需要的时候还是要考虑一下多进程,需要多进程时,就需要先了解下ipc。多进程如何实现,有什么坑,这里就不多做描述,毕竟总结起来比内存优化篇幅长多了。

2.2 节流

节流的含义是减少不必要的内存分配,从int还是long类型选择,到bitmap加载图片,到是否需要将某个属性升级为成员变量,可以从各个方面去考虑、优化。下面列出几个可以优化的点:

采用合适的数据类型 使用 bitmapfactory.options 加载合适尺寸大小和合适位深的图片 避免使用图片背景,如果必要,看看是否可以使用颜色背景和更小的图片一起替换 使用 viewstub 延迟加载那些可能用不到的view 避免使用activity作为全局变量,如果有必要,看看是否可以使用application的context代替 如果需要,使用stringbuilder代替字符串拼接

2.3 复用

使用viewholder优化listview ( recyclerview代替listview是一个不错的选择 ) 使用三级缓存的图片加载工具显示图片,如glide等

2.4 回收

不再需要的对象就要及时回收,一般来说gc会帮我们擦屁股,但是有些对象仍然需要主动释放,或者一些不规范的代码会导致对象无法被gc回收。

及时回收不再使用的bitmap 根据需要修改象的引用类型:strong reference,softreference,weakreference,phantomreference 注意 handler、线程、timer 等异步工具类的使用,防止内存泄漏 及时关闭 closeable,如 inputstream 和 outputstream,数据库的cursor等

三、内存问题检测与定位

有很多工具可以检测发现内存问题,静态代码分析工具 lint,运行时发现代码问题的 strictmode,专门用于内存泄漏检查的 leakcanary。

3.1 lint

lint 是一个很好的代码分析工具,在我们编码阶段就可以给予我们很“友好”的优化提示,把 bug 拦截在编译之前。

lint 是android studio 提供的 代码扫描分析工具,它可以帮助我们发现代码结构/质量问题,同时提供一些解决方案,而且这个过程不需要我们手写测试用例。

除了发现代码结构/质量问题外,lint 还可以发现和删除没有使用的资源,从而减少apk的体积。

3.2 严格模式

strictmode类是android 2.3 (api 9)引入的一个工具类,可以用来帮助开发者发现代码中的一些不规范的问题,以达到提升应用响应能力的目的。举个例子来说,如果开发者在ui线程中进行了网络操作或者文件系统的操作,而这些缓慢的操作会严重影响应用的响应能力,甚至出现anr对话框。为了在开发中发现这些容易忽略的问题,我们使用strictmode,系统检测出主线程违例的情况并做出相应的反应,最终帮助开发者优化和改善代码逻辑。

严格模式的接入比较简单,但对于android的性能优化(内存泄漏和anr)有非常大的帮助。

3.3 leakcanary

在实际的项目中,其实没有用到leakcanary,但是各种书,博客都在介绍 leakcanary,想必也是一个很好的工具。leakcanary的接入也不难。

3.4 android profiler 内存分析

android profiler 是 android studio 3.0 版本代替原有的android monitor的性能分析工具。

四、总结

导致 oom 的问题大多是内存泄漏的问题,特别是activity无法释放的问题尤为严重。通过各种工具分析定位,比较容易找到问题的代码,从而改正它。而除了内存泄漏,其它一些问题则是比较难发现,因为这些问题隐藏在代码间。要做到心中有数,就要有一个良好的编码规范,该分配创建对象时创建,该复用的复用,该回收时回收。内存优化是一条很远路,优化在每一行代码之间,只有时刻保持警觉,严格要求自己,才能写出健壮的代码。