无法在 RemoteViewsFactory 中加载多个图像
Posted
技术标签:
【中文标题】无法在 RemoteViewsFactory 中加载多个图像【英文标题】:Unable to load multiple images in a RemoteViewsFactory 【发布时间】:2013-12-02 00:51:50 【问题描述】:我正在创建一个简单的 android 小部件,它可以从网站获取并显示一些电影封面。显示图像很简单GridView
。该小部件工作正常,但当我开始滚动时,我得到了 OutOfMemoryError
和 Android 杀死了我的进程。
这是我的RemoteViewsFactory
的代码。我似乎无法理解如何解决这个问题。我使用的图像大小合适,所以我不会在 Android 中浪费内存。它们尺寸完美。正如你所看到的,我使用了一个非常小的 LRU 缓存,并且在我的清单中,我什至启用了android:largeHeap="true"
。我已经在这个问题上绞尽脑汁整整两天了,我想知道我正在尝试做的事情是否可能?
public class SlideFactory implements RemoteViewsFactory
private JSONArray jsoMovies = new JSONArray();
private Context ctxContext;
private Integer intInstance;
private RemoteViews remView;
private LruCache<String, Bitmap> lruCache;
private static String strCouch = "http://192.168.1.110:5984/movies/";
public SlideFactory(Context ctxContext, Intent ittIntent)
this.ctxContext = ctxContext;
this.intInstance = ittIntent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
this.lruCache = new LruCache<String, Bitmap>(4 * 1024 * 1024);
final Integer intMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
final Integer intBuffer = intMemory / 8;
lruCache = new LruCache<String, Bitmap>(intBuffer)
@Override
protected int sizeOf(String strDigest, Bitmap bmpCover)
return bmpCover.getByteCount() / 1024;
;
public RemoteViews getViewAt(int intPosition)
if (intPosition <= getCount())
try
String strDocid = this.jsoMovies.getJSONObject(intPosition).getString("id");
String strDigest = this.jsoMovies.getJSONObject(intPosition).getJSONObject("value").getJSONObject("_attachments").getJSONObject("thumb.jpg").getString("digest");
String strTitle = this.jsoMovies.getJSONObject(intPosition).getJSONObject("value").getString("title");
Bitmap bmpThumb = this.lruCache.get(strDigest);
if (bmpThumb == null)
String strUrl = strCouch + strDocid + "/thumb.jpg";
System.out.println("Fetching" + intPosition);
bmpThumb = new ImageFetcher().execute(strUrl).get();
this.lruCache.put(strDigest, bmpThumb);
remView.setImageViewBitmap(R.id.movie_cover, bmpThumb);
remView.setTextViewText(R.id.movie_title, strTitle);
catch (Exception e)
e.printStackTrace();
return remView;
return null;
public void onCreate()
return;
public void onDestroy()
jsoMovies = null;
public int getCount()
return 20;
public RemoteViews getLoadingView()
return null;//new RemoteViews(this.ctxContext.getPackageName(), R.layout.loading);
public int getViewTypeCount()
return 1;
public long getItemId(int intPosition)
return intPosition;
public boolean hasStableIds()
return true;
public void onDataSetChanged()
this.remView = new RemoteViews(this.ctxContext.getPackageName(), R.layout.slide);
try
DefaultHttpClient dhcNetwork = new DefaultHttpClient();
String strUrl = strCouch + "_design/application/_view/language?" + URLEncoder.encode("descending=true&startkey=[\"hi\", ]&attachments=true");
HttpGet getMovies = new HttpGet(strUrl);
HttpResponse resMovies = dhcNetwork.execute(getMovies);
Integer intMovies = resMovies.getStatusLine().getStatusCode();
if (intMovies != HttpStatus.SC_OK)
throw new HttpResponseException(intMovies, "Server responded with an error");
String strMovies = EntityUtils.toString(resMovies.getEntity(), "UTF-8");
this.jsoMovies = new JSONObject(strMovies).getJSONArray("rows");
catch (Exception e)
Log.e("SlideFactory", "Unknown error encountered", e);
这是获取图像的AsyncTask
的来源:
public class ImageFetcher extends AsyncTask<String, Void, Bitmap>
@Override
protected Bitmap doInBackground(String... strUrl)
Bitmap bmpThumb = null;
try
URL urlThumb = new URL(strUrl[0]);
HttpURLConnection hucConnection = (HttpURLConnection) urlThumb.openConnection();
InputStream istThumb = hucConnection.getInputStream();
bmpThumb = BitmapFactory.decodeStream(istThumb);
istThumb.close();
hucConnection.disconnect();
catch (Exception e)
e.printStackTrace();
return bmpThumb;
【问题讨论】:
@commonsware,您能对此有所了解吗?你一直都有答案。 :) 谢谢。 你能发布一些 logcat 输出吗? @MridangAgarwalla 你能发布ImageFetcher
的代码吗?您还可以提及您正在加载的图像的大小(或者可能是其中最大的大小)..??
@AmulyaKhare,我已经发布了ImageFetcher
的来源。我可以使用样本大小,但它会大大降低质量。我的图像都是 250x375 像素。我将println
放入方法getViewAt
方法中,并注意到Android 尝试一次加载所有图像,即使GridView
中只有少数图像可见。我认为ListView
、GridView
和StackVIew
的这些适配器的工作方式是它们只预加载一些项目,并且当用户滚动并且它们失去焦点时,Android 会丢弃旧的布局。我不明白为什么它会尝试加载所有项目。
Have you read it? 而这条this.lruCache = new LruCache<String, Bitmap>(4 * 1024 * 1024);
行没用。
【参考方案1】:
我有类似的痛苦经历,经过大量挖掘后,我发现setImageViewBitmap
将所有位图复制到新实例中,因此占用了双倍内存。
考虑将以下行更改为静态资源或其他内容!
remView.setImageViewBitmap(R.id.movie_cover, bmpThumb);
这需要大量内存并 ping 垃圾收集器来清理内存,但速度太慢,以至于您的应用无法及时使用释放的内存。
【讨论】:
由于我是从远程服务器下载图像,我将如何创建静态引用。通过静态引用,您是指可绘制文件夹中的资产吗?如果您可以链接到使用setImageViewBitmap
方法发现问题的 Android 资源,那也很棒。谢谢@AVEbrahimi
谢谢!这真的挽救了一天!
@MridangAgarwalla 你能发布你的解决方案吗?
我认为问题尚未解决。如果您找到了解决方案,请发布!【参考方案2】:
我的解决方法是使用 LRUCache 来存储小部件的位图:
首先,照常初始化:
protected void initializeCache()
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/10th of the available memory for this memory cache.
final int cacheSize = maxMemory / 10;
bitmapLruCache = 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;
;
然后重用位图:
void updateImageView(RemoteViews views, int resourceId, final String imageUrl)
Bitmap bitmap = bitmapLruCache.get(imageUrl);
if (bitmap == null)
bitmap = // get bitmap from web with your loader
bitmapLruCache.put(imageUrl, bitmap);
views.setImageViewBitmap(resourceId, bitmap);
使用此代码小部件现在不会使我的应用崩溃。
更多信息here
【讨论】:
以上是关于无法在 RemoteViewsFactory 中加载多个图像的主要内容,如果未能解决你的问题,请参考以下文章
调用 notifyAppWidgetViewDataChanged 后,在 RemoteViewsFactory 中不调用 onDataSetChanged
用于内容的 Widget RemoteViewsFactory 中的 Android 权限拒绝
空数据集时 RemoteViewsFactory 调用 getViewAt
RemoteViewsFactory grantUriPermission 来获取数据