这是2018年底,所以情况发生了变化。
首先:运行您的应用并在Android Studio中打开Android Profiler选项卡。 你会看到它消耗了多少内存,你会惊讶但它可以分配大量的内存。
也 这是一篇很棒的文章 在官方文档中详细说明如何使用Memory Profiler,它可以让您深入了解内存管理。
但在大多数情况下,您的常规Android Profiler对您来说已经足够了。
通常,应用程序以50Mb的RAM分配开始,但当您开始在内存中加载一些照片时立即跳转到90Mb。当您使用带有预加载照片(每个3,5Mb)的ViewPager打开Activity时,您可以在几秒钟内轻松获得190Mb。
但这并不意味着您在内存管理方面存在问题。
我能给出的最好建议是遵循指南和最佳实践,使用顶级库进行图像加载(Glide,Picasso),你会没事的。
但是如果你需要定制一些内容并且你真的需要知道你可以手动分配多少内存,你可以获得总可用内存并计算出预定部分(以%表示)。 在我的情况下,我需要在内存中缓存解密的照片,因此每次用户在列表中滑动时我都不需要解密它们。
为此,您可以使用随时可用 LruCache类 。它是一个缓存类,可以自动跟踪对象分配的内存量(或实例数),并删除最旧的以保持最近的使用历史记录。 这是 关于如何使用它的精彩教程。
在我的例子中,我创建了2个缓存实例:用于拇指和附件。 通过单例访问使它们成为静态,因此它们可在整个应用程序中全局使用。
缓存类:
public class BitmapLruCache extends LruCache<Uri, byte[]> { private static final float CACHE_PART_FOR_THUMBS_PRC = 0.01f; // 1% (Nexus 5X - 5Mb) private static final float CACHE_PART_FOR_ATTACHMENTS_PRC = 0.03f;// 3% (Nexus 5X - 16Mb) private static BitmapLruCache thumbCacheInstance; private static BitmapLruCache attachmentCacheInstance; public static synchronized BitmapLruCache getDecryptedThumbCacheInstance() { if (thumbCacheInstance == null) { int cacheSize = getCacheSize(CACHE_PART_FOR_THUMBS_PRC); //L.log("creating BitmapLruCache for Thumb with size: " + cacheSize + " bytes"); thumbCacheInstance = new BitmapLruCache(cacheSize); return thumbCacheInstance; } else { return thumbCacheInstance; } } public static synchronized BitmapLruCache getDecryptedAttachmentCacheInstance() { if (attachmentCacheInstance == null) { int cacheSize = getCacheSize(CACHE_PART_FOR_ATTACHMENTS_PRC); // L.log("creating BitmapLruCache for Attachment with size: " + cacheSize + " bytes"); attachmentCacheInstance = new BitmapLruCache(cacheSize); return attachmentCacheInstance; } else { return attachmentCacheInstance; } } private BitmapLruCache(int maxSize) { super(maxSize); } public void addBitmap(Uri uri, byte[] bitmapBytes) { if (get(uri) == null && bitmapBytes != null) put(uri, bitmapBytes); } public byte[] getBitmap(Uri uri) { return get(uri); } @Override protected int sizeOf(Uri uri, byte[] bitmapBytes) { // The cache size will be measured in bytes rather than number of items. return bitmapBytes.length; } }
这就是我如何计算可用的可用内存以及我可以咬掉它的程度:
private static int getCacheSize(float partOfTotalFreeMemoryToUseAsCache){ final long maxMemory = Runtime.getRuntime().maxMemory(); //Use ... of available memory for List Notes thumb cache return (int) (maxMemory * partOfTotalFreeMemoryToUseAsCache); }
这就是我在Adapters中使用它来获取缓存图像的方法:
byte[] decryptedThumbnail = BitmapLruCache.getDecryptedThumbCacheInstance().getBitmap(thumbUri);
以及如何在后台线程(常规AsyncTask)中将其设置为缓存:
BitmapLruCache.getDecryptedThumbCacheInstance().addBitmap(thumbUri, thumbBytes);
我的应用程序针对API 19+,因此设备不老,可用RAM的这些部分足以满足我的缓存(1%和3%)。
的 有趣的事实: 强> Android没有任何API或其他黑客来获取分配给您的应用程序的内存量,它是根据各种因素动态计算的。
附:我正在使用静态类字段来保存缓存,但根据最新的Android指南,建议使用它 ViewModel架构组件 为了这个目的。
Android应用程序(不是系统应用程序)可以使用的最大内存量(以兆字节/占总RAM的百分比)是多少?
这取决于设备。 getMemoryClass() 上 ActivityManager 将为您提供运行代码的设备的价值。
getMemoryClass()
ActivityManager
Android版本之间有什么区别吗?
是的,就多年来操作系统要求的增加而言,设备必须进行调整才能匹配。
有关设备制造商的差异吗?
是的,就制造商制造设备而言,尺寸因设备而异。
在确定应用程序可以使用多少RAM时,会考虑哪些“因素”?
我不知道“边缘因素”是什么意思。
早期设备的每个应用上限为16MB;后来的设备增加到24MB或32MB
那是对的。屏幕分辨率是一个重要的决定因素,因为较大的分辨率意味着较大的位图,因此平板电脑和高分辨率手机的价值往往更高。例如,您将看到具有48MB堆的设备,如果有高于此值的值,我不会感到惊讶。
应用程序如何超出此限制?
您假设该应用程序的作者知道他正在做什么。考虑到这一点 核心Android工程师很难确定应用的内存使用情况 ,我不认为有问题的应用程序必然提供特别准确的结果。
话虽这么说,本机代码(NDK)不受堆限制。而且,从Android 3.0开始,应用程序可以请求“大堆”,通常在数百MB的范围内,但对于大多数应用程序而言,这被认为是糟糕的形式。
此外,我注意到当使用大约30-40兆字节时,我的一些应用程序崩溃了OutOfMemoryException。
请记住,Android垃圾收集器不是压缩垃圾收集器。真的应该是例外 CouldNotFindSufficientlyLargeBlockOfMemoryException ,但这可能被认为太罗嗦了。 OutOfMemoryException 意味着你 的 无法分配您请求的块 强> 并不是说你完全耗尽了你的堆。
CouldNotFindSufficientlyLargeBlockOfMemoryException
OutOfMemoryException