应用应该如何管理内存
在软件开发的各个阶段,你都应该时候注意你的RAM消耗(即便是在括软件的设计阶段)。这里有很多种途径,通过使用它们可以帮助你设计和写出更有效率的代码,
你应该在设计和实现应用的时候采用以下的这些技术来让降低应用的内存消耗。
尽可能少的使用服务
如果你的应用需要使用服务来进行后台操作,那么尽可能让他在的确需要工作的时候才进行工作。另外请确保当你的工作完成之后结束掉你的服务。
当你开启一个服务的时候,系统会在服务运行的时候保留它的进程,这会让这个进行变得非常庞大,因为服务所消耗的资源不能被其他应用使用或者回收。这就会导致保存在LRU缓存中的进程数量减少,使得切换应用的时候效率降低。当后台有足够多的服务正在运行的时候,他甚至可能导致整个系统因为内存紧张而崩溃掉。
管理你的服务最简单的方法是使用一个IntentService,他会在他处理完事件之后自行关闭掉服务,详情请参看使用前台服务。
当不需要的时候还让服务一直在后台运行是内存管理中最不应该犯的错之一。因此不要贪心的让自己的服务一直在后台运行,这不仅会导致你应用的性能变低,用户也有可能发现应用的这种行为并且卸载他。
当用户界面隐藏起来的时候,释放掉内存资源
当用户切换到其他应用,你的ui界面不可见的时候,你就应该释放掉那些被自己界面使用的资源。在这个时候释放掉ui资源能够显著提升系统存放缓存进程的数目,这可以极大的提升用户体验。
当你应用中的所有进程的界面对于用户而言都不可见的时候,会触发onTrimMemory()的回调接口,带着一个TRIM_MEMORY_UI_HIDDEN参数。这个接口同OnStop()的回调接口的不同在于 OnStop()接口会在应用跳转的时候被调用,而onTrimMemory接口只有在所有界面都不可见的情况下被调用,尽管你需要实现onStop()方法来释放掉Activity资源,例如网络访问或者注销掉广播接收器,但这还不够,你不应该在onTrimMemory之前就释放掉你的ui资源。这能够保证用户通过返回键返回到你的应用中,你的ui资源同样存在,而且能够快速的显示出来。
当内存不足时释放掉一些内存
在你的应用的生命周期中,onTrimMemory()接口同样会在当整个设备的内存变得很低的时候被调用。你应该根据从onTrimMemory()中传来的内存等级,选择性的释放掉你资源
TRIM_MEMORY_RUNNING_MODERATE
你的应用正在运行,不可被杀死,但是目前设备剩余内存很少,系统需要从LRU缓存中杀死掉一些进程
TRIM_MEMORY_RUNNING_LOW
你的应用正在运行,不可被杀死,但是目前设备剩余内存不足临界值,因此你需要释放掉不是用的内存来提升系统运行效率
TRIM_MEMORY_RUNNING_CRITICAL
你的应用正在运行,但是系统已经准备杀死大多数LRU中的进程,因此你应该释放掉那些不是很关键的资源。如果系统不能通过回收得到足够的RAM,那么他会清空所有LRU缓存,并且开始杀死那些希望保留的进程,例如拥有一个正在运行后台服务的进程。
同样的,当你的应用正在处于缓存中的时候,你可能会受到以下几个onTrimMemory()等级
TRIM_MEMORY_BACKGROUND
系统正在运行在低内存状态,而你的进程处在LRU列表的开始部分。因此尽管你的应用进程不太可能被杀死,但是系统已经转杯开始杀死LRU中的进程。你应该释放掉那些容易被还原的资源,来确保你仍然在列表中,并且在用户返回到应用的时候能够快速的进行切换
TRIM_MEMORY_MODERATE
系统正在运行在低内存阶段,你的进程处于LRU列表的中部。加入系统无法获得足够的内存资源,你的应用将会被杀死
TRIM_MEMORY_COMPLETE
系统正在运行在低内存阶段,你的进程将会是最开始被系统杀死的进程之一,你应该释放掉所有与你的应用状态无关的资源。
虽然由于onTrimMemory()接口直到API14的时候才被加入,你还是可以使用onLowMemory接口来作为老版本的回调,他可以同onTrimMemory()中TRIM_MEMORY_COMPLETE的等级一样。
注意:当系统开始杀死在LRU缓存中的进程时,尽管他是从下至上开始的,但是他也会考虑优先杀死那些内存消耗比较大的,以回收更多的系统资源。因此,如果你的应用的内存尽可能低,你就可能被始终保留在内存中,能够很快切换回来
判断你应该拥有多少内存
正如之前所提到的,每一个Android设备都会根据自身RAM大小的不同来向应用提供不同大小的堆限制。你可以通过getMemoryClass()来得到应用可能拥有的堆的大小。加入你的应用当内存不足的时候却分配更多的内存,那么就会造成OOM
在一种非常特殊的情况加,你可以通过设置manifest文件中<application>标签里的largeHeap的属性为true。如果你这么做,那么你可以通过getLargeMemoryClass()来获取你个比较大的堆的评估值。
尽管如此,最好只有那些真正需要更多内存空间的应用才请求一个更大的堆空间,例如一个大型的图像编辑应用。记住不要因为你内存溢出就去申请一个大型堆,你需要知道只有你明白你的内存被分配到哪儿了,为什么他一直被保留着。当你确信你的应用需要一个大型堆的时候,你应该避免随意浪费他,使用额外的内存会极大的影响用户的体验,因为垃圾回收会消耗更多的事件,并且系统的效率会在应用切换或者其他的操作上变得更慢
此外,一个大型堆的大小在不同的设备上不一样,当运行在某些拥有限制内存的设备上时,大型堆的大小可能同普通堆得大小一模一样。因此即使你使用了大型堆,你也应该通过getMemoryClass()来检查真正的堆的尺寸,以确保不过超出限制。