Android 高效地缓存、传递和查看位图
我有一个新闻提要,其中新闻项包含图像。每个新闻项(在表视图中)从服务器获取一个图像url,并异步/从缓存下载图像 我将使用一些伪代码/Java以尽可能简单的术语解释我的过程 NewsItemAdapterAndroid 高效地缓存、传递和查看位图,android,caching,bitmap,Android,Caching,Bitmap,我有一个新闻提要,其中新闻项包含图像。每个新闻项(在表视图中)从服务器获取一个图像url,并异步/从缓存下载图像 我将使用一些伪代码/Java以尽可能简单的术语解释我的过程 NewsItemAdapter Map<String, Bitmap> imageCache = new HashMap<String, Bitmap>(); // init cache String imurl = get image url from appropriate NewsObject
Map<String, Bitmap> imageCache = new HashMap<String, Bitmap>(); // init cache
String imurl = get image url from appropriate NewsObject;
Bitmap value = imageCache.get(imurl);
if (value != null) { // if bitmap is in cache
load bitmap into image view from cache;
add bitmap to NewsObject for accessing later;
}else {
execute Asynchronous bitmap download task;
}
main活动
setOnNewsItemClickListener{
intent = get intent to mainNewsScreen; //after you click on news item
intent.putExta("newsBitmap", bitmap from NewsObject); // set in NewsItemAdapter
startActivity(intent);
}
主新闻屏幕
onCreate(){
load bitmap from intent extras into image view;
}
我的主要问题是,如果我删除了找到的scaleDownBitmap()
方法,那么我就会得到内存错误
但我正在失去很多图像质量。正如您所看到的,我根本没有使用bitmap.recycle()
,我不完全确定在哪里使用它,因为我需要将图像保存在内存中(我原以为)
你知道如何提高效率吗。我相信这对很多尝试创建类似应用程序的人都会有帮助。考虑改用lrucache,并在图像从缓存中掉出时将其存储在磁盘上 此处介绍了最佳实践: 将此代码视为一个想法,其中一些是从上面的链接复制的:
...
// Get max available VM memory, exceeding this amount will throw an
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
// int in its constructor.
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount() / 1024;
}
@Override
protected void entryRemoved (boolean evicted, K key, V oldValue, V newValue) {
// Save your entry to disc instead
}
};
。。。
//获取最大可用VM内存,超过此数量将抛出
//OutOfMemory异常。当LruCache接受
//int在其构造函数中。
final int maxMemory=(int)(Runtime.getRuntime().maxMemory()/1024);
//将可用内存的1/8用于此内存缓存。
最终int cacheSize=maxMemory/8;
mMemoryCache=新的LruCache(缓存大小){
@凌驾
受保护的int-sizeOf(字符串键、位图){
//缓存大小将以KB为单位,而不是以字节为单位
//项目数量。
返回bitmap.getByteCount()/1024;
}
@凌驾
受保护的void entryRemoved(布尔逐出、K键、V oldValue、V newValue){
//将您的条目保存到光盘
}
};
Google制作了一个DiscLruCache,您可以简单地下载并在项目中使用(上面的链接描述了它的用法):
也不要在视图中保存无限多的新闻/图像。当用户滚动浏览并阅读新闻条目时,您必须删除它们
考虑使用回收器视图(如果你想让你的应用程序有一种材料设计的感觉,还可以使用卡片):
太好了,谢谢你的回答!将位图传递给intent怎么样?我是否最好传递对全局位图的引用并手动回收它?或者我所做的是真正有效的方法吗?我尽量避免手动回收位图,这是在询问状态问题和对它们的长期引用。Android会自动回收你的位图,并在最后一次引用丢失时释放所有相关内存(你必须用位图手动处理本机内存的日子已经过去),我也会避免通过意图传递位图,而是在活动之间共享缓存。实现这一点的一种方法是将缓存设为单例,在应用程序中共享。请记住,如果选择此方法,可能需要手动调用executeAll();在适当的情况下,不要占用太多的RAM。最后,您可能需要考虑片段方法而不是基于活动的方法。这样你就可以在你的活动中存储LruCache,并在你的片段中访问它。在我接受你的回答之前,有几个问题:首先,新闻将被限制在大约50个项目,所以回收仍然是必要的吗?第二,你能详细说明为什么我应该使用单例变量而不是全局变量吗?我对这东西已经够新了。第三。如果我使用的是片段,我是否需要使用单例呢?谢谢!
...
// Get max available VM memory, exceeding this amount will throw an
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
// int in its constructor.
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount() / 1024;
}
@Override
protected void entryRemoved (boolean evicted, K key, V oldValue, V newValue) {
// Save your entry to disc instead
}
};
<!-- A RecyclerView with some commonly used attributes -->
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
// use a linear layout manager
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager)
<!-- A CardView that contains a TextView -->
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_view"
android:layout_gravity="center"
android:layout_width="200dp"
android:layout_height="200dp"
card_view:cardCornerRadius="4dp">
<TextView
android:id="@+id/info_text"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v7.widget.CardView>