React Native Android 应用内存使用探究

简介: 本文讲的是React Native Android 应用内存使用探究,我以为是我新找来测试 React Native 工程的 Android 手机有问题。我甚至被这错误的想法牵着刷了 rom (基于 AOSP 5.1.1 的系统)来在更高的 Android 版本上运行 React Native,当然也有着避免被 Samsung 自带应用影响的原因。
本文讲的是React Native Android 应用内存使用探究,

为什么我那台老旧的 Android 手机无法加载图片?

刚开始接触 React Native 应用时,我发现有个现象很奇怪,在 Android 手机上我无法看到任何图片,只有颜色和文字可以显示。但 iOS 手机却没有任何问题。

我以为是我新找来测试 React Native 工程的 Android 手机有问题。我甚至被这错误的想法牵着刷了 rom (基于 AOSP 5.1.1 的系统)来在更高的 Android 版本上运行 React Native,当然也有着避免被 Samsung 自带应用影响的原因。然而,除了样例工程的首屏外,其他地方仍看不到图片。于是我将这手机打入冷宫。

几天后,我的朋友指出 React Native 的 Android 应用在一些特定屏幕上无法加载图片。呃……这可真够奇怪的……等等,我好像在哪儿见过这现象……

好吧,原来不止是我的手机有这现象。

这……一言难尽啊。

代码很明了,在显示图片方面并没有用什么黑科技或者第三方库。我开始在不同Android版本的 GenyMotion 和 Android Virtual Device ( AVD ,Android 虚拟机)上运行(React Native应用)。

  •   我的手机:只能在第一屏看到图片
  •   GenyMotion (API 21, API 22):部分节点有问题
  •   AVD (API 21, API 22, API 23):完全没问题?!

我本以为这是在特定机型或者 API 版本上发生的事情,但显然不是这样的。也就是说我需要考虑一堆其他的可能性。这可真让人头痛。

我的宿敌——内存

这应用有许多作为背景显示的图片,而且这些图片也不算小(400~800 kb)。除此之外,虽然不太可能,但仍有点可疑的是,这些图片都是通过远程 URI 获取的。

我开始对内存结构产生了好奇心,尤其是从远程加载图片时动态分配的堆空间。于是我开始追踪内存使用。

想要一些炫酷的内存查看工具?

几年前,我用这个来查看内存:

adb shell dumpsys meminfo

我喜欢命令行应用,但当涉及到图形化的内存使用时,这真的不是什么界面友好的东西。

别告诉我你喜欢看这样的内存分享界面。

如果你在一个宿醉的周六清晨用这东西,那绝对会让你从梦魇中醒来。(不不,我绝对没干过这种事!:wink:) 我需要能让垃圾回收变得更容易的工具。

最容易获取(并且免费!)的内存查看器就是 Android Device Monitor。如果你安装过 Android Studio 的话,你就已经拥有它了。按照如下步骤来打开它:

  1. 用平常的方式运行 React Native 应用 (react-native run-android)
  2. 运行 Android Studio
  3. 在菜单栏找到并打开 Tools → Android → Enable ADB Integration
  4. 点击 Tools → Android → Android Device Monitor
  5. 当显示 Android Device Monitor 界面时,点击 Monitor → Preferences
  6. 在打开的对话框中找到 Android → DDMS ,选中这两项
  •   Heap updates enabled by default(默认更新堆开启)
  •   Thread updates enable by default (optional)(默认更新线程开启)

之后你就会看到一个如图所示的界面 (System Information tab):

如果你看到这个界面的话:

执行下面这条命令来确保你的开发服务连上了设备。

adb reverse tcp:8081 tcp:8081

当你从 Android Studio 运行一个已经通过 react-native run-android 启动的应用时,可能发生这个问题。

在左边的 Devices 栏选择你的应用。现在内存检查前的工作就已经准备完毕了。

增加堆空间

当我运行 Android Device Monitor 并来回拖动时,我发现了一些奇怪的现象。

即使第一屏使用的内存已经在124MB左右时,堆大小也并没有明显超过124MB的迹象。但垃圾回收却开始执行:

I/art(27035): Background partial concurrent mark sweep GC freed 1584(69KB) AllocSpace objects, 2(30KB) LOS objects, 12% free, 108MB/124MB, paused 3.874ms total 182.718ms

于是问题来了, “为什么堆的内存如此小?”

Android 5.0.0 中 ART Java Heap Parameter 推荐的 dalvik.vm.heapsize 值为 384MB:

source: https://01.org/android-ia/user-guides/android-memory-tuning-android-5.0-and-5.1

我甚至去拉了我手机的 build property 文件 (adb -d pull /system/build.prop) 然后证实堆内存是 256 MB.

后来我知道怎么设置大内存了,只需在 AndroidManifest.xml 中加这行代码:

<application
      android:name=".MainApplication"
      android:allowBackup="true"
      android:label="@string/app_name"
      android:icon="@mipmap/ic_launcher"
      android:theme="@style/AppTheme"
      android:largeHeap="true">

这是我开启 largeHeap 后的结果:

就是这样。是的,就这么一行该死的代码。真是够恶心的!

所有 AVD 设备( API 21 ~ 23)在显示图片时没有这个问题的原因是模拟器更智能。当需要时它会增大堆的大小,虽然设置堆大小(的行为)会产生警告。

emulator: WARNING: Setting VM heap size to 384MB

更上一层楼——如何检查内存泄漏

确切地说,我在上文解决的问题并不算是一个应用内存问题,而是设置问题。如果你的应用有隐藏更深的内存问题,使用基于 Eclipse RCP 的 Memory Analyzer 来检查是否有内存泄漏是一种可行的方法。

这个工具并不需要依赖 Eclipse ,所以你可以下载单独版。链接在此: http://www.eclipse.org/mat/downloads.php

  1. 点击 Cause GC 来执行垃圾回收。

2. 点击 Dump HPROF file 按钮来捕获内存转储文件。

3. 将 Android 转储文件转换成 Memory Analyzer 可以读取的格式。 (你需要 Android SDK的 platform-tools )

hprof-conv com.leak_sample.hprof com.leak_sample_converted.hprof

4. 运行 Memory Analyzer 打开转换后的 hprof 文件。然后选择 Leak Suspects Report (你可以先点取消,稍后再执行)。

5. 就是这样,喵~

举个内存泄漏的例子

假设你的 React Native 应用有个 Android 原生的模块。模块中有个单例类会在调用 listener 的 onUpdate() 函数时创建一个包含 10,000,000 个元素的 String 数组。(我知道这是个无意义类,但我们先关注主要矛盾吧。简单点。)

悲剧的是,你忘记在 onDestroy() 中取消监听了,这就会在每次旋转屏幕时导致内存泄漏。你就会奇怪为什么应用莫名其妙的崩溃了。

以下是 Memory Analyzer 在执行完上述 5 步的界面:

如图所示, LetsLeak 类占用了相当多的内存。注意这只是个假设而不是实际情况

让我们聚焦于 Dominator Tree 。

你可以在 Top Consumers 看到排序后的内存使用列表,但是如果是这种只有一个疑点需要仔细排查的情况, Dominator Tree 是个更好的选择。

在 Dominator Tree 界面, Shallow Heap 是内存引用的意思, Retained Heap 则代表所有类实际持有的内存。

在 Inspector 界面,你可以看到你创建的超大数组。你也许会想,**“我是在单例里创建了一个 String 数组,但为什么会持有这么大的内存?应该只有一个才对……”**之后你会意识到自己并没有释放内存,这是使用单例时的常见问题。

结论

将 Android Device Monitor 和 Memory Analyzer 高效地结合起来可以监视线程并且可以通过转储内存查找所有 Android 系统上的内存问题。 Android 上的 React Native 也不例外。

就像上文举例的内存泄漏问题一样,一个简单对象持有你想不到的大内存这种情况是很容易找到原因的。然而在开发环境中追踪内存泄漏还是相当困难的。毋庸置疑的是,这些工具可以带来极大的便利。

关于 Leon

Leon Kim 是 Infinite Red 公司的软件工程师,来自远东,韩国。他在读研究生时的主要方向是 图像处理与模式识别 ,研发了作为政府研究计划一部分的 prison guard robot ,并有着从 LTE IPsec 安全网关到七号信令系统(Signaling System 7)的 MTP3 层再到制药自动化的不同系统的研发经验。他热爱在 Infinite Red 和这群酷炫的家伙在 web 和移动端开发的生活,当然,也喜欢和朋友们在每个周五晚来一次韩式烤肉。 (불금!)

有什么问题或评论么? 我的推特是 @leonskim 。或者通过 Infinite Red 联系我们**。**





原文发布时间为:2016年11月22日

本文来自云栖社区合作伙伴掘金,了解相关信息可以关注掘金网站。
目录
相关文章
|
1天前
|
存储 缓存 安全
Android系统 应用存储路径与权限
Android系统 应用存储路径与权限
5 0
Android系统 应用存储路径与权限
|
1天前
|
存储 安全 Android开发
Android系统 自定义系统和应用权限
Android系统 自定义系统和应用权限
5 0
|
6天前
|
缓存 移动开发 Android开发
构建高效Android应用:从优化用户体验到提升性能表现
【4月更文挑战第18天】 在移动开发的世界中,打造一个既快速又流畅的Android应用并非易事。本文深入探讨了如何通过一系列创新的技术策略来提升应用性能和用户体验。我们将从用户界面(UI)设计的简约性原则出发,探索响应式布局和Material Design的实践,再深入剖析后台任务处理、内存管理和电池寿命优化的技巧。此外,文中还将讨论最新的Android Jetpack组件如何帮助开发者更高效地构建高质量的应用。此内容不仅适合经验丰富的开发者深化理解,也适合初学者构建起对Android高效开发的基础认识。
5 0
|
6天前
|
移动开发 Android开发 开发者
构建高效Android应用:采用Kotlin进行内存优化的策略
【4月更文挑战第18天】 在移动开发领域,性能优化一直是开发者关注的焦点。特别是对于Android应用而言,由于设备和版本的多样性,确保应用流畅运行且占用资源少是一大挑战。本文将探讨使用Kotlin语言开发Android应用时,如何通过内存优化来提升应用性能。我们将从减少不必要的对象创建、合理使用数据结构、避免内存泄漏等方面入手,提供实用的代码示例和最佳实践,帮助开发者构建更加高效的Android应用。
8 0
|
8天前
|
缓存 移动开发 Java
构建高效的Android应用:内存优化策略
【4月更文挑战第16天】 在移动开发领域,尤其是针对资源有限的Android设备,内存优化是提升应用性能和用户体验的关键因素。本文将深入探讨Android应用的内存管理机制,分析常见的内存泄漏问题,并提出一系列实用的内存优化技巧。通过这些策略的实施,开发者可以显著减少应用的内存占用,避免不必要的后台服务,以及提高垃圾回收效率,从而延长设备的电池寿命并确保应用的流畅运行。
|
10天前
|
搜索推荐 开发工具 Android开发
安卓即时应用(Instant Apps)开发指南
【4月更文挑战第14天】Android Instant Apps让用户体验部分应用功能而无需完整下载。开发者需将应用拆分成模块,基于已上线的基础应用构建。使用Android Studio的Instant Apps Feature Library定义模块特性,优化代码与资源以减小模块大小,同步管理即时应用和基础应用的版本。经过测试,可发布至Google Play Console,提升用户便利性,创造新获客机会。
|
11天前
|
Java API 调度
安卓多线程和并发处理:提高应用效率
【4月更文挑战第13天】本文探讨了安卓应用中多线程和并发处理的优化方法,包括使用Thread、AsyncTask、Loader、IntentService、JobScheduler、WorkManager以及线程池。此外,还介绍了RxJava和Kotlin协程作为异步编程工具。理解并恰当运用这些技术能提升应用效率,避免UI卡顿,确保良好用户体验。随着安卓技术发展,更高级的异步处理工具将助力开发者构建高性能应用。
|
11天前
|
编解码 人工智能 测试技术
安卓适配性策略:确保应用在不同设备上的兼容性
【4月更文挑战第13天】本文探讨了提升安卓应用兼容性的策略,包括理解平台碎片化、设计响应式UI(使用dp单位,考虑横竖屏)、利用Android SDK的兼容工具(支持库、资源限定符)、编写兼容性代码(运行时权限、设备特性检查)以及优化性能以适应低端设备。适配性是安卓开发的关键,通过这些方法可确保应用在多样化设备上提供一致体验。未来,自动化测试和AI将助力应对设备碎片化挑战。
|
16天前
|
监控 API Android开发
构建高效安卓应用:探究Android 12中的新特性与性能优化
【4月更文挑战第8天】 在本文中,我们将深入探讨Android 12版本引入的几项关键技术及其对安卓应用性能提升的影响。不同于通常的功能介绍,我们专注于实际应用场景下的性能调优实践,以及开发者如何利用这些新特性来提高应用的响应速度和用户体验。文章将通过分析内存管理、应用启动时间、以及新的API等方面,为读者提供具体的技术实现路径和代码示例。
|
17天前
|
移动开发 API Android开发
构建高效Android应用:探究Kotlin协程的优势与实践
【4月更文挑战第7天】 在移动开发领域,性能优化和应用响应性的提升一直是开发者追求的目标。近年来,Kotlin语言因其简洁性和功能性在Android社区中受到青睐,特别是其对协程(Coroutines)的支持,为编写异步代码和处理并发任务提供了一种更加优雅的解决方案。本文将探讨Kotlin协程在Android开发中的应用,揭示其在提高应用性能和简化代码结构方面的潜在优势,并展示如何在实际项目中实现和优化协程。