Android应用开发管理app内存

转自谷歌官网:https://developer.android.google.cn/topic/performance/memory.html#java

管理你的应用程序的内存

随机存取存储器(RAM)是任何软件开发环境中的宝贵资源,但在物理内存经常受到约束的移动操作系统上它更有价值。虽然Android Runtime(ART)和Dalvik虚拟机都执行例行垃圾收集,但这并不意味着您可以忽略应用分配和释放内存的时间和位置。您仍然需要避免引入内存泄漏,通常是由于保留静态成员变量中的对象引用而引起的,并且Reference在生命周期回调定义的适当时间释放任何对象。

本页介绍了如何主动减少应用内存使用量。有关Android操作系统如何管理内存的信息,请参阅 Android内存管理概述

 

监视可用内存和内存使用情况

 

在修复应用程序中的内存使用问题之前,首先需要找到它们。Android Studio中的Memory Profiler可帮助您通过以下方式查找和诊断内存问题:

  1. 了解您的应用随时间分配内存的方式。Memory Profiler显示应用程序使用的内存量,分配的Java对象数以及何时进行垃圾收集的实时图表。
  2. 在您的应用运行时启动垃圾收集事件并拍摄Java堆的快照。
  3. 记录应用程序的内存分配,然后检查所有已分配的对象,查看每个分配的堆栈跟踪,并跳转到Android Studio编辑器中的相应代码。

 

释放内存以响应事件

 

Android内存管理概述中所述,Android可以通过多种方式从您的应用程序中回收内存,或者在必要时完全终止您的应用程序以释放内存以执行关键任务。为了进一步帮助平衡系统内存并避免系统需要终止应用程序进程,您可以ComponentCallbacks2Activity类中实现该接口。提供的onTrimMemory()回调方法允许您的应用在应用处于前台或后台时侦听与内存相关的事件,然后释放对象以响应应用生命周期或系统事件,指示系统需要回收内存。

例如,您可以实现onTrimMemory()回调以响应不同的与内存相关的事件,如下所示:

import android.content.ComponentCallbacks2;
// Other import statements ...

public class MainActivity extends AppCompatActivity
    implements ComponentCallbacks2 {

    // Other activity code ...

    /**
     * Release memory when the UI becomes hidden or when system resources become low.
     * @param level the memory-related event that was raised.
     */
    public void onTrimMemory(int level) {

        // Determine which lifecycle or system event was raised.
        switch (level) {

            case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:

                /*
                   Release any UI objects that currently hold memory.

                   The user interface has moved to the background.
                */

                break;

            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:

                /*
                   Release any memory that your app doesn't need to run.

                   The device is running low on memory while the app is running.
                   The event raised indicates the severity of the memory-related event.
                   If the event is TRIM_MEMORY_RUNNING_CRITICAL, then the system will
                   begin killing background processes.
                */

                break;

            case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
            case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
            case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:

                /*
                   Release as much memory as the process can.

                   The app is on the LRU list and the system is running low on memory.
                   The event raised indicates where the app sits within the LRU list.
                   If the event is TRIM_MEMORY_COMPLETE, the process will be one of
                   the first to be terminated.
                */

                break;

            default:
                /*
                  Release any non-critical data structures.

                  The app received an unrecognized memory level value
                  from the system. Treat this as a generic low-memory message.
                */
                break;
        }
    }
}

该 onTrimMemory() 回调是在搭载Android 4.0(API级别14)加入。对于早期版本,您可以使用onLowMemory(),大致相当于 TRIM_MEMORY_COMPLETE事件。

检查你应该使用多少内存

为了允许多个正在运行的进程,Android为每个应用程序分配的堆大小设置了硬限制。根据设备总体可用的RAM量,确切的堆大小限制因设备而异。如果您的应用程序已达到堆容量并尝试分配更多内存,系统将抛出一个OutOfMemoryError

为避免内存不足,您可以查询系统以确定当前设备上可用的堆空间。您可以通过调用查询系统中的此图getMemoryInfo()。这将返回一个ActivityManager.MemoryInfo对象,该 对象提供有关设备当前内存状态的信息,包括可用内存,总内存和内存阈值 - 系统开始终止进程​​的内存级别。该 ActivityManager.MemoryInfo对象还公开了一个简单的布尔值,lowMemory 它告诉您设备是否在内存上运行不足。

以下代码段显示了如何getMemoryInfo()在应用程序中使用该方法的示例 。

public void doSomethingMemoryIntensive() {

    // Before doing something that requires a lot of memory,
    // check to see whether the device is in a low memory state.
    ActivityManager.MemoryInfo memoryInfo = getAvailableMemory();

    if (!memoryInfo.lowMemory) {
        // Do memory intensive work ...
    }
}

// Get a MemoryInfo object for the device's current memory status.
private ActivityManager.MemoryInfo getAvailableMemory() {
    ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
    ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
    activityManager.getMemoryInfo(memoryInfo);
    return memoryInfo;
}

使用更多内存有效的代码构造

一些Android功能,Java类和代码构造倾向于使用比其他更多的内存。您可以通过在代码中选择更有效的替代方案来最小化应用程序使用的内存量。

谨慎使用服务

在不需要时运行服务是 Android应用程序可能犯的最严重的内存管理错误之一。如果您的应用需要 服务 来在后台执行工作,请不要让它继续运行,除非它需要运行作业。请记住在完成任务后停止服务。否则,您可能会无意中导致内存泄漏。

启动服务时,系统始终始终保持该服务的进程运行。此行为使服务进程非常昂贵,因为服务使用的RAM仍然不可用于其他进程。这减少了系统可以保留在LRU高速缓存中的高速缓存进程数,从而降低了应用程序切换的效率。当内存紧张并且系统无法维护足够的进程来托管当前运行的所有服务时,它甚至可能导致系统中的颠簸。

您通常应该避免使用持久性服务,因为它们会对可用内存提出持续的要求。相反,我们建议您使用替代实现,例如JobScheduler。有关如何使用JobScheduler计划后台进程的详细信息,请参阅 后台优化

如果必须使用服务,限制服务生命周期的最佳方法是使用a IntentService,它在处理启动它的意图后立即完成。有关更多信息,请阅读 在后台服务中运行

使用优化的数据容器

编程语言提供的某些类未针对移动设备进行优化。例如,泛型 HashMap实现可能非常低效,因为每个映射都需要一个单独的入口对象。

Android框架包括若干优化的数据的容器,其中包括 SparseArraySparseBooleanArray,和LongSparseArray。例如,SparseArray类更有效,因为它们避免了系统需要 自动 进行密钥的自动操作,有时还需要值(每个条目创建另一个或两个对象)。

如有必要,您始终可以切换到原始数组以获得非常精简的数据结构。

注意代码抽象

开发人员通常将抽象简单地用作良好的编程实践,因为抽象可以提高代码的灵活性和维护。但是,抽象的成本很高:通常它们需要更多的代码才能执行,需要更多的时间和更多的RAM才能将代码映射到内存中。因此,如果您的抽象没有提供显着的好处,您应该避免它们。

使用nano protobufs进行序列化数据

协议缓冲区 是一种与语言无关,平台中立,可扩展的机制,由Google设计用于序列化结构化数据 - 类似于XML,但更小,更快,更简单。如果您决定使用protobufs作为数据,则应始终在客户端代码中使用nano protobufs。常规protobufs会生成极其冗长的代码,这会导致应用程序出现多种问题,例如RAM使用量增加,APK大小增加以及执行速度变慢。

有关更多信息,请参阅protobuf自述文件中的“Nano版本”部分 。

避免记忆流失

如前所述,垃圾收集事件通常不会影响应用程序的性能。但是,在很短的时间内发生的许多垃圾收集事件可能会很快耗尽您的帧时间。系统花在垃圾收集上的时间越长,处理渲染或流式传输音频等其他事情的时间就越少。

通常,内存流失可能导致大量垃圾收集事件发生。实际上,内存流失描述了在给定时间内发生的已分配临时对象的数量。

例如,您可以在for循环中分配多个临时对象 。或者您可以 在 视图的功能中创建新的 PaintBitmap对象onDraw()。在这两种情况下,应用程序都会以高容量快速创建大量对象。这些可以快速消耗年轻代中的所有可用内存,从而迫使垃圾收集事件发生。

当然,您需要在代码中找到内存流失率较高的位置,然后才能修复它们。为此,您应该在Android Studio中使用Memory Profiler

一旦确定代码中的问题区域,请尝试减少性能关键区域内的分配数量。考虑将事物移出内部循环或者将它们移动到基于 工厂 的分配结构中。

删除内存密集型资源和库

代码中的某些资源和库可以在不知情的情况下吞噬内存。APK的总体大小(包括第三方库或嵌入式资源)可能会影响应用消耗的内存量。您可以通过从代码中删除任何冗余,不必要或臃肿的组件,资源或库来提高应用程序的内存消耗。

减少整体APK大小

您可以通过减少应用的总体大小来显着降低应用的内存使用量。位图大小,资源,动画帧和第三方库都可以影响APK的大小。Android Studio和Android SDK提供了多种工具来帮助您减少资源和外部依赖项的大小。

有关如何降低整体APK大小的详细信息,请参阅 缩小APK大小

使用Dagger 2进行依赖注入

依赖注入框架可以简化您编写的代码,并提供适用于测试和其他配置更改的自适应环境。

如果您打算在应用程序中使用依赖注入框架,请考虑使用 Dagger 2。Dagger不使用反射扫描您的应用程序代码。Dagger的静态编译时实现意味着它可以在Android应用程序中使用,而无需不必要的运行时成本或内存使用。

使用反射的其他依赖注入框架倾向于通过扫描代码来注释来初始化进程。此过程可能需要更多的CPU周期和RAM,并且可能会在应用程序启动时导致明显的延迟。

使用外部库时要小心

外部库代码通常不是为移动环境编写的,并且在用于移动客户端上工作时效率低下。当您决定使用外部库时,可能需要针对移动设备优化该库。在决定使用它之前,预先规划该工作并在代码大小和RAM占用空间方面分析库。

即使是一些移动优化库也会因实现不同而导致问题。例如,一个库可能使用nano protobufs,而另一个库使用微型protobuf,导致您的应用程序中有两个不同的protobuf实现。这可能发生在日志记录,分析,图像加载框架,缓存以及许多其他您不期望的事情的不同实现中。

尽管ProGuard可以帮助使用正确的标志删除API和资源,但它无法删除库的大型内部依赖项。您在这些库中需要的功能可能需要较低级别的依赖项。当你使用Activity库中的子类(它往往具有大量的依赖关系)时,这会变得特别成问题,当库使用反射时(这是常见的并且意味着你需要花费大量时间手动调整ProGuard以使其工作), 等等。

另外,请避免在几十个功能中仅使用一个或两个功能的共享库。您不希望引入大量您甚至不使用的代码和开销。在考虑是否使用库时,请查找与您需要的实现非常匹配的实现。否则,您可能决定创建自己的实现。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值