我们如何将Android应用程序的内存占用减少50%

by Rohit Arya

由Rohit Arya

我们如何将Android应用程序的内存占用减少50% (How we reduced our Android app’s memory footprint by 50%)

Like any other startup momentum-obsessed startup, we didn’t spend a lot of time to building an efficient product on the first go. We shipped our Android app, and it was working “just fine.”

像任何其他初创公司一样,我们一开始就没有花很多时间来构建高效的产品。 我们发布了Android应用程序,并且运行正常。

As we started scaling up in terms of our offering to the customers, our app became bulky — with tons of images — and we started to see performance issues. Our app became slow and froze on low-end devices. Battery consumption also increased.

随着我们开始扩大向客户的服务范围,我们的应用变得庞大(包含大量图像),并且开始看到性能问题。 我们的应用程序变慢并在低端设备上冻结。 电池消耗也增加了。

To debug this issue, we used a memory monitoring tool provided by Android Studio. As we scrolled through a very long list of product images, this is what we observed:

要调试此问题,我们使用了Android Studio提供的内存监视工具。 当我们滚动浏览非常长的产品图片列表时,我们观察到的是:

To explain these graphs a little better:

为了更好地解释这些图:

  • The sudden drop in the allocated memory is due to Garbage Collection (GC) events.

    分配的内存突然减少是由于垃圾回收(GC)事件引起的。
  • The size of free memory increases when an Android kills other apps’ processes (which are in the background) to allocate more memory to the foreground app.

    当Android杀死其他应用程序的进程(在后台)以将更多内存分配给前台应用程序时,可用内存的大小会增加。
  • CPU usage increased when we scrolled through the list of products.

    滚动浏览产品列表时,CPU使用率增加了。

Just by opening the product list page, the app consumed 15 megabytes of memory. If we scrolled all the way to the bottom of the product list page, the app consumed 50 megabytes of memory, with a lot of GC events.

仅通过打开产品列表页面,该应用程序就消耗了15 MB的内存。 如果我们一直滚动到产品列表页面的底部,则该应用程序消耗了50 MB的内存,并带有许多GC事件。

When scrolling through other product lists we observed similar patterns. Here are the graphs:

滚动浏览其他产品列表时,我们观察到了类似的模式。 这是图形:

Again we observed this pattern. By this time Android had allocated maximum memory (for bitmaps) — which it could have allocated to our app by killing other apps’ processes in the background — and the net memory allocation has hit 57 megabytes along with several GC events.

我们再次观察到这种模式。 到这个时候,Android已经分配了最大的内存(用于位图)(可以通过在后台杀死其他应用程序的进程来分配给我们的应用程序),并且净内存分配已经达到57兆字节,并伴随了几次GC事件。

These graphs are from the Android runtime. Dalvik behaves even worse in terms of memory management and Garbage Collection.

这些图来自Android运行时。 Dalvik在内存管理和垃圾回收方面的表现甚至更差

In Android, bitmaps represent the largest contiguous blocks of memory. They occupy heaps, which results in lots of contention to find free space to allocate new bitmaps as we scroll. This then results in more GC events so it can free up memory to provide the necessary space. Since there were so many images getting loaded in the list, these GC events were degrading the performance of our app.

在Android中,位图代表最大的连续内存块。 它们占据堆,这导致大量竞争,以在滚动时找到可用空间来分配新的位图。 然后,这导致更多的GC事件,因此可以释放内存以提供必要的空间。 由于列表中加载了太多图像,因此这些GC事件降低了我们应用的性能。

GC events causes the application to freeze until it finishes. A few of these won’t matter, but too many of these events will result in a lower frame rate. Running a Garbage Collector also causes more CPU, which consumes battery usage.

GC事件导致应用程序冻结直到完成。 其中的一些无关紧要,但是这些事件太多会导致帧速率降低。 运行垃圾收集器还会导致更多的CPU,这会消耗电池电量。

Also, the higher the memory usage of an app, the more likely the system will decide to kill it when it’s running in the background.

同样,应用程序的内存使用率越高,当系统在后台运行时,系统决定杀死它的可能性就越大。

We had to solve this problem before we could move any further with product development. And for that, we moved to an object pool concept for bitmaps, as advised by Colt in this video:

我们必须解决此问题,然后才能进一步开发产品。 为此,正如柯尔特在本视频中建议的那样,我们转到了位图的对象池概念:

So the idea is instead of creating a brand new bitmap, you use an existing piece of memory to load the bitmap into:

因此,您的想法不是创建全新的位图,而是使用现有内存将位图加载到:

mBitmapOptions.inBitmap = mCurrentBitmap;//using the mCurrentBitmap to load the new bitmapmCurrentBitmap = BitmapFactory.decodeFile(fileName, mBitmapOptions);

Then when you scroll through a long list of images, there’s no need to load all the images into separate memory allocations. You can just allocate a maximum number of bitmaps that are going to be visible, then reuse their memory — thereby avoiding those horrific GC events.

然后,当您滚动浏览一长串图像时,无需将所有图像加载到单独的内存分配中。 您可以分配最大数量的可见位图,然后重新使用它们的内存,从而避免发生那些可怕的GC事件。

Here are the results showing the improvements:

以下是显示改进的结果:

We scrolled through the same product list again after doing the changes. We observed that with an initial 15 megabytes of memory already allocated, there was only 27 megabytes of total memory allocation done by the time we’d scrolled to the bottom of the page (and with a very few GC events).

进行更改后,我们再次滚动了相同的产品列表。 我们观察到,在已经分配了最初的15 MB内存的情况下,当滚动到页面底部时(只有很少的GC事件),仅完成了27 MB的内存分配。

We scrolled a few more product lists and we observed no extra memory allocations (Bitmap Pool magic) and therefore no major GC events.

我们滚动了一些产品列表, 没有发现额外的内存分配 (位图池魔术),因此没有重大的GC事件。

In the end, we had successfully reduced bitmap memory footprint by almost 50%.

最后,我们成功地将位图的内存占用减少了近50%。

We need to be mindful of the fact that Android has some constraint for reusing bitmaps, with respect to the physical size of the existing bitmaps:

我们需要注意以下事实:就现有位图的物理大小而言,Android在重用位图方面存在一些限制:

  1. In SDK versions 11 through 18, the bitmaps that we load and the bitmaps that we are reusing must be exactly the same size. We solved this by using exact size of ImageView in our list for all versions of the SDK before version 18.

    在SDK版本11到18中,我们加载的位图和我们正在重用的位图必须大小完全相同。 我们通过使用列表中所有版本18之前的SDK的ImageView的确切大小来解决此问题。
  2. In versions of the SDK newer than 19, existing bitmaps that we want to use could be greater than or equal to in the dimensions of the new incoming bitmap.

    在低于19的SDK版本中,我们要使用的现有位图的尺寸可能大于或等于新传入位图的尺寸。

We also try to use same pixel format for reusing bitmaps. So to load an image as an RGB_565 bitmap, we use the RGB_565 bitmap allocation.

我们还尝试使用相同的像素格式来重用位图。 因此,要将图像加载为RGB_565位图,我们使用RGB_565位图分配。

The good news is you don’t have to do all this on your own. There are already some amazing libraries like Glide and Fresco which have built-in capabilities for reusing bitmap memory. All you have to do is make sure your bitmaps can be reused. (Remember that there are constraints with respect to bitmap dimension and Bitmap.Config.) If you do not want to use these, you can just plug a Bitmap Pool into your existing image loader. Using these libraries will also help you save precious memory by pre-scaling bitmaps, and many more things.

好消息是您不必自己做所有这些事情。 已经有一些令人惊叹的库,例如GlideFresco ,它们具有用于重用位图内存的内置功能。 您要做的就是确保可以重用您的位图。 (请记住,有关位图尺寸和Bitmap.Config 。)如果您不想使用这些约束,则只需将位图池插入现有的图像加载器中即可。 使用这些库还可以通过预缩放位图以及更多其他内容来帮助您节省宝贵的内存。

Along with this, we also started using the RGB_565 format, which takes only 16 bits per pixel — as compared to ARGB_8888 which takes 32bits per pixel — on low memory devices. This further reduces our memory footprint.

与此同时,我们还开始在低存储设备上使用RGB_565格式,该格式每像素仅占用16位,而ARGB_8888则需要32位每像素。 这进一步减少了我们的内存占用。

There are many awesome things that you can do to improve your app’s performance. Will keep posting them. Let’s build better apps.

您可以做很多很棒的事情来提高应用程序的性能。 将继续发布它们。 让我们构建更好的应用程序。

If you enjoyed reading this article, it would mean a lot if you recommend it using the ❤ icon and share with your colleagues and friends. Thanks!

如果您喜欢阅读这篇文章,那么使用❤图标进行推荐并与您的同事和朋友分享对您来说意义重大。 谢谢!

Also, Let’s connect on Facebook, Twitter, Linkedin and Github.

另外,让我们在FacebookTwitterLinkedinGithub上进行连接

翻译自: https://www.freecodecamp.org/news/how-we-reduced-memory-footprint-by-50-in-our-android-app-49efa5c93ad8/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值