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日

本文来自云栖社区合作伙伴掘金,了解相关信息可以关注掘金网站。
目录
相关文章
|
15天前
|
IDE Java 开发工具
深入探索安卓应用开发:从环境搭建到第一个"Hello, World!"应用
本文将引导读者完成安卓应用开发的初步入门,包括安装必要的开发工具、配置开发环境、创建第一个简单的安卓项目,以及解释其背后的一些基本概念。通过一步步的指导和解释,本文旨在为安卓开发新手提供一个清晰、易懂的起点,帮助读者顺利地迈出安卓开发的第一步。
197 65
|
15天前
|
存储 Java Android开发
探索安卓应用开发:构建你的第一个"Hello World"应用
【9月更文挑战第24天】在本文中,我们将踏上一段激动人心的旅程,深入安卓应用开发的奥秘。通过一个简单而经典的“Hello World”项目,我们将解锁安卓应用开发的基础概念和步骤。无论你是编程新手还是希望扩展技能的老手,这篇文章都将为你提供一次实操体验。从搭建开发环境到运行你的应用,每一步都清晰易懂,确保你能顺利地迈出安卓开发的第一步。让我们开始吧,探索如何将一行简单的代码转变为一个功能齐全的安卓应用!
|
2天前
|
安全 Java API
Java 泛型在安卓开发中的应用
在Android开发中,Java泛型广泛应用于集合类、自定义泛型类/方法、数据绑定、适配器及网络请求等场景,有助于实现类型安全、代码复用和提高可读性。例如,结合`ArrayList`使用泛型可避免类型转换错误;自定义泛型类如`ApiResponse&lt;T&gt;`可处理不同类型API响应;RecyclerView适配器利用泛型支持多种视图数据;Retrofit结合泛型定义响应模型,明确数据类型。然而,需注意类型擦除导致的信息丢失问题。合理使用泛型能显著提升代码质量和应用健壮性。
|
20天前
|
开发框架 搜索推荐 开发工具
打造个性化安卓应用:从零开始的Flutter之旅
【8月更文挑战第51天】本文是一篇面向初学者的Flutter入门教程,旨在通过简单易懂的语言和实际代码示例,引导读者步入跨平台移动应用开发的世界。文章首先介绍了Flutter的基本概念和优势,然后逐步展示了如何搭建开发环境、创建第一个Flutter应用,并实现了一个简单的待办事项列表。最后,文章探讨了Flutter在实现高性能和美观界面方面的潜力,鼓励读者发挥创意,探索更多可能。
71 15
|
10天前
|
监控 安全 Java
Kotlin 在公司上网监控中的安卓开发应用
在数字化办公环境中,公司对员工上网行为的监控日益重要。Kotlin 作为一种基于 JVM 的编程语言,具备简洁、安全、高效的特性,已成为安卓开发的首选语言之一。通过网络请求拦截,Kotlin 可实现网址监控、访问时间记录等功能,满足公司上网监控需求。其简洁性有助于快速构建强大的监控应用,并便于后续维护与扩展。因此,Kotlin 在安卓上网监控应用开发中展现出广阔前景。
10 1
|
20天前
|
Java Android开发 UED
🧠Android多线程与异步编程实战!告别卡顿,让应用响应如丝般顺滑!🧵
在Android开发中,为应对复杂应用场景和繁重计算任务,多线程与异步编程成为保证UI流畅性的关键。本文将介绍Android中的多线程基础,包括Thread、Handler、Looper、AsyncTask及ExecutorService等,并通过示例代码展示其实用性。AsyncTask适用于简单后台操作,而ExecutorService则能更好地管理复杂并发任务。合理运用这些技术,可显著提升应用性能和用户体验,避免内存泄漏和线程安全问题,确保UI更新顺畅。
50 5
|
23天前
|
开发框架 Dart 前端开发
Android 跨平台方案对比之Flutter 和 React Native
本文对比了 Flutter 和 React Native 这两个跨平台移动应用开发框架。Flutter 使用 Dart 语言,提供接近原生的性能和丰富的组件库;React Native 则基于 JavaScript,具备庞大的社区支持和灵活性。两者各有优势,选择时需考虑团队技能和项目需求。
132 8
|
21天前
|
前端开发 Java 数据库
💡Android开发者必看!掌握这5大框架,轻松打造爆款应用不是梦!🏆
在Android开发领域,框架犹如指路明灯,助力开发者加速应用开发并提升品质。本文将介绍五大必备框架:Retrofit简化网络请求,Room优化数据库访问,MVVM架构提高代码可维护性,Dagger 2管理依赖注入,Jetpack Compose革新UI开发。掌握这些框架,助你在竞争激烈的市场中脱颖而出,打造爆款应用。
101 3
|
21天前
|
存储 API Android开发
"解锁Android权限迷宫:一场惊心动魄的动态权限请求之旅,让你的应用从平凡跃升至用户心尖的宠儿!"
随着Android系统的更新,权限管理成为应用开发的关键。尤其在Android 6.0(API 级别 23)后,动态权限请求机制的引入提升了用户隐私保护,要求开发者进行更精细的权限管理。
47 2
|
22天前
|
搜索推荐 Java 测试技术
打造个性化安卓应用:从设计到发布的完全指南
【9月更文挑战第17天】在这个数字时代,拥有一款个性化的安卓应用无疑是展现创意、实现梦想的一大步。本文将带你走进安卓应用的开发世界,从设计理念的孕育到实际代码的编写,再到最终的应用发布,我们将一步步揭开应用开发的神秘面纱。无论你是编程新手还是希望提升现有技能,这篇文章都将是你的宝贵资源。让我们开始这段激动人心的旅程吧!

热门文章

最新文章