Android知识笔记:如果你深受其害,不妨来看看这篇内存优化

Glide 是一个快速高效的 Android 图片加载库,注重于平滑的滚动。Glide 提供了易用的 API,高性能、可扩展的图片解码管道(decode pipeline),以及自动的资源池技术。

Glide 也是 Google 推荐过的开源项目,详见:Glide。

复用系统自带的资源

Android 系统本身内置了很多的资源(例如:字符串、颜色、图片、动画、样式以、简单布局等等),这些资源都可以在应用程序中直接引用。

这样做不仅仅可以减少应用程序的自身负重,减小 APK 的大小,另外还可以一定程度上减少内存的开销,复用性更好。但是也有必要留意 Android 系统的版本差异性,对那些不同系统版本上表现存在很大差异,不符合需求的情况,还是需要应用程序自身内置进去。

复用 ConvertView

在 ListView、GridView 等出现大量重复子组件的视图里面对 ConvertView 的复用。

onLowMemory()

OnLowMemory 是 Android 提供的API,在系统内存不足,所有后台程序(优先级为 Background 的进程,不是指后台运行的进程)都被杀死时,系统会调用 OnLowMemory。

系统提供的回调有:Application、Activity、Fragementice、Service、ContentProvider。

onTrimMemory()

OnTrimMemory 是 Android 4.0 之后提供的 API,系统会根据不同的内存状态来回调。

系统提供的回调有:Application、Activity、Fragementice、Service、ContentProvider。

OnTrimMemory的参数是一个 int 数值,代表不同的内存状态。

当 App 在前台运行时,该函数的 level (从低到高)有:

  • TRIM_MEMORY_RUNNING_MODERATE

系统开始运行在低内存状态下 App 正在运行,不会被杀掉。

  • TRIM_MEMORY_RUNNING_LOW

系统运行在更加低内存状态下,App 在运行,不会被杀掉 App 可以清理一些资源来保证系统的流畅。

  • TRIM_MEMORY_RUNNING_CRITICAL

系统运行在相当低内存状态下,App 在运行,且系统不认为可以杀掉此 App,系统要开始杀掉后台进程。此时,App 应该去释放一些不重要的资源。

当 App 在后台运行时,level 状态有:

  • TRIM_MEMORY_UI_HIDDEN:

App 的 UI 不可见,App 可以清理 UI 使用的较大的资源。

当 App 进入后台 LRU List 时:

  • TRIM_MEMORY_BACKGROUND

系统运行在低内存下,App 进程在 LRU List 开始处附近,尽管 App 没有被杀掉的风险,但是系统也许已经正在杀后台进程。App 应该清理一些容易恢复的资源。

  • TRIM_MEMORY_MODERATE

系统运行在低内存下,App 进程在 LRU List 中间处附件,App 此时有被杀的可能。

  • TRIM_MEMORY_COMPLETE

系统运行在低内存下,App 是首先被杀的选择之一,App 应该及时清理掉恢复 App 到前台状态,不重要的所有资源。

另外,一个 App 占用内存越多,则系统清理后台 LRU List 时,越可能优先被清理。所以,内存使用我们要谨慎使用。

避免在 onDraw 方法里面执行对象的创建

类似 onDraw 等频繁调用的方法,一定需要注意避免在这里做创建对象的操作,因为他会迅速增加内存的使用,而且很容易引起频繁的 GC,甚至是内存抖动。

序列化

在 Android 中实现序列化一般用 Serializable 和 Parcelable 两种方式。

两者最大的区别在于 存储媒介的不同,Serializable 使用 I/O 读写存储在硬盘上,而 Parcelable 是直接 在内存中读写。很明显,内存的读写速度通常大于 IO 读写,所以在 Android 中传递数据优先选择 Parcelable。Serializable 会使用反射,序列化和反序列化过程需要大量 I/O 操作, Parcelable 自已实现封送和解封(marshalled &unmarshalled)操作不需要用反射,数据也存放在 Native 内存中,效率要快很多。

Parcelable 的性能比 Serializable 好,在内存开销方面较小,所以在内存间数据传输时推荐使用 Parcelable(如 Activity 间传输数据)。

而 Serializable 可将数据持久化方便保存,所以在需要保存或网络传输数据时选择 Serializable,因为 Android 不同版本 Parcelable 可能不同,所以不推荐使用 Parcelable进行数据持久化.

StringBuilder

在有些时候,代码中会需要使用到大量的字符串拼接的操作,这种时候有必要考虑使用 StringBuilder 来替代频繁的 +

避免内存泄漏


内存泄漏(Memory Leak)指程序运行过程中分配内存给临时变量,用完之后却没有被 GC 回收,始终占用着内存,既不能被使用也不能分配给其他程序,于是就发生了内存泄漏。

内存泄露有时不严重且不易察觉,这样开发者就不知道存在内存泄露,但有时也会很严重,甚至会提示你 OOM。

Context 的泄露

在 Android 开发中,最容易引发的内存泄漏问题的是 Context。比如 Activity 的 Context,就包含大量的内存引用,一旦泄漏了 Context,也意味泄漏它指向的所有对象。

造成 Activity 泄漏的常见原因:

静态引用 Activity

在类中定义了静态 Activity 变量,把当前运行的 Activity 实例赋值于这个静态变量。如果这个静态变量在 Activity 生命周期结束后没有清空,就导致内存泄漏。

因为 static 变量是贯穿这个应用的生命周期的,所以被泄漏的 Activity 就会一直存在于应用的进程中,不会被垃圾回收器回收。

static Activity activity; // 这种代码要避免

单例中保存 Activity

在单例模式中,如果 Activity 经常被用到,那么在内存中保存一个 Activity 实例是很实用的。

但是由于单例的生命周期是应用程序的生命周期,这样会强制延长 Activity 的生命周期,这是相当危险而且不必要的,无论如何都不能在单例子中保存类似 Activity 的对象。

public class Singleton {

private static Singleton instance;

private Context mContext;

private Singleton(Context context){

this.mContext = context;

}

public static Singleton getInstance(Context context){

if (instance == null){

synchronized (Singleton.class){

if (instance == null){

instance = new Singleton(context);

}

}

}

return instance;

}

}

在调用 Singleton 的 getInstance() 方法时传入了 Activity。那么当 instance 没有释放时,这个 Activity 会一直存在,因此造成内存泄露。

考虑使用 Application Context 而不是 Activity Context

对于大部分非必须使用 Activity Context 的情况(Dialog 的 Context 就必须是 Activity Context),我们都可以考虑使用 Application Context 而不是 Activity 的 Context,这样可以避免不经意的 Activity 泄露。

Inner Classes

内部类的优势可以提高可读性和封装性,而且可以访问外部类,不幸的是,导致内存泄漏的原因,就是内部类持有外部类实例的强引用(例如在内部类中持有 Activity 对象)。

解决方法:

  • 将内部类变成静态内部类;

  • 如果有强引用 Activity 中的属性,则将该属性的引用方式改为弱引用;

  • 在业务允许的情况下,当 Activity 执行 onDestory 时,结束这些耗时任务。

避免使用异步回调

异步回调被执行的时间不确定,很有可能发生在 Activity 已经被销毁之后,这不仅仅很容易引起 crash,还很容易发生内存泄露。

注意临时 Bitmap 对象的及时回收

虽然在大多数情况下,我们会对 Bitmap 增加缓存机制,但是在某些时候,部分 Bitmap 是需要及时回收的。

例如:临时创建的某个相对比较大的 Bitmap 对象,在经过变换得到新的 Bitmap 对象之后,应该尽快回收原始的 Bitmap,这样能够更快释放原始 Bitmap 所占用的空间。

需要特别留意的是 Bitmap 类里面提供的 createBitmap() 方法,这个函数返回的 Bitmap 有可能和 source bitmap 是同一个,在回收的时候,需要特别检查 source bitmap 与 return bitmap 的引用是否相同,只有在不等的情况下,才能够执行 source bitmap 的 recycle 方法。

注意监听器的注销

在 Android 程序里面存在很多需要 register 与 unregister 的监听器,我们需要确保在合适的时候及时 unregister 那些监听器。自己手动 add 的 listener,需要记得及时 remove 这个 listener。

注意 Cursor 对象是否及时关闭

在程序中我们经常会进行查询数据库的操作,但时常会存在不小心使用 Cursor 之后没有及时关闭的情况。这些 Cursor 的泄露,反复多次出现的话会对内存管理产生很大的负面影响,我们需要谨记对 Cursor 对象的及时关闭。

注意缓存容器中的对象泄漏

有时候,我们为了提高对象的复用性把某些对象放到缓存容器中,可是如果这些对象没有及时从容器中清除,也是有可能导致内存泄漏的。

例如:针对 2.3 的系统,如果把 drawable 添加到缓存容器,因为 drawable 与 View 的强应用,很容易导致 activity 发生泄漏。而从 4.0 开始,就不存在这个问题。解决这个问题,需要对 2.3 系统上的缓存 drawable 做特殊封装,处理引用解绑的问题,避免泄漏的情况。

注意 WebView 的泄漏

Android 中的 WebView 存在很大的兼容性问题,不仅仅是 Android 系统版本的不同对 WebView 产生很大的差异,另外不同的厂商出货的 ROM 里面 WebView 也存在着很大的差异。更严重的是标准的 WebView 存在内存泄露的问题,看这里 WebView causes memory leak - leaks the parent Activity。

所以通常根治这个问题的办法是为 WebView 开启另外一个进程,通过 AIDL 与主进程进行通信, WebView 所在的进程可以根据业务的需要选择合适的时机进行销毁,从而达到内存的完整释放。

Lint Tool


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

Lint 发现的每个问题都有描述信息和等级(和测试发现 bug 很相似),我们可以很方便地定位问题,同时按照严重程度进行解决。

点击 Analyze > Inspect Code 可打开 Lint 工具。

详细使用介绍可查看 Android 开发文档 - 使用 Lint 改进您的代码、Android 性能优化:使用 Lint 优化代码、去除多余资源 这两篇文章,使用比较简单,网上介绍资源很多,这里不再详细介绍。

adb dumpsys


dumpsys 是 Android 系统提供的一个工具,可以查看系统服务的相关信息,dumpsys 通过 adb 命令来调用。

查看指定进程包名的内存使用情况:

$ adb shell dumpsys meminfo <包名>

输出内容如下:

当然 dumpsys 的功能还有很多,可以查看 Android 开发文档 - dumpsys、dumpsys 命令用法 这两篇文章了解更多用法。

Heap Viewer


Heap Viewer 是 DDMS 中的一个工具,实时查看 App 分配的内存大小和空闲内存大小,可帮助查找内存泄露。Heap Viewer 支持 5.0 及以上的系统,现在已经弃用,官方推荐使用 Memory Profiler 来查看 App 内存分配情况。

Memory Profiler


Memory Profiler 是 Android Profiler 中的一个组件,可帮助开发者识别导致应用卡顿、OOM 和内存泄露。它显示一个应用内存使用量的实时图表,可以捕获堆转储、强制执行垃圾回收以及跟踪内存分配。

可以在 View > Tool Windows > Android Profiler 中打开 Memory Profiler 界面。

Memory Profile 的具体使用可查看 Android 开发文档 - 使用 Memory Profiler 查看 Java 堆和内存分配、Android Studio 3.0 利用 Android Profiler 测量应用性能 这两篇文章。

MAT


MAT(Memory Analyzer Tool),一个基于 Eclipse 的内存分析工具,是一个快速、功能丰富的 Java Heap 分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。

使用内存分析工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。

当然 MAT 也有独立的不依赖 Eclipse 的版本,只不过这个版本在调试 Android 内存的时候,需要将 Android Studio 生成的文件进行转换,才可以在独立版本的 MAT 上打开。.

一般情况下使用 Memory Profiler 就可以检测出内存泄露的大致位置,使用 MAT 可以更详细的分析内存的具体情况。

MAT 下载 点击这里,使用教程可查看 Eclipse Wiki - MemoryAnalyzer,也可以参考 Android 内存优化之一:MAT 使用入门、Android内存优化之二:MAT使用进阶 这两篇文章。

LeakCanary


LeakCanary 是一个用于检测 Android 内存泄漏的开源库,上面介绍的 Memory Profiler、MAT 使用起来比较复杂,LeakCanary 堪称傻瓜式的内存泄露检测工具。

使用方式详见 https://square.github.io/leakcanary,也可参考 LeakCanary 中文使用说明 这篇文章。

最后

最后我想说:对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

最后这里是关于**我自己的Android 学习,面试文档,视频收集大整理**,有兴趣的伙伴们可以看看~

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
24年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-nKMdPCY3-1715484115309)]

[外链图片转存中…(img-JVJiJP4h-1715484115310)]

[外链图片转存中…(img-2QYIoo7k-1715484115312)]

[外链图片转存中…(img-m8xEWW6d-1715484115314)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值