【问题标题】:Unable to load multiple images in a RemoteViewsFactory无法在 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 中只有少数图像可见。我认为ListViewGridViewStackVIew 的这些适配器的工作方式是它们只预加载一些项目,并且当用户滚动并且它们失去焦点时,Android 会丢弃旧的布局。我不明白为什么它会尝试加载所有项目。
  • Have you read it? 而这条this.lruCache = new LruCache&lt;String, Bitmap&gt;(4 * 1024 * 1024); 行没用。

标签: java android android-gridview android-appwidget android-remoteview


【解决方案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

【讨论】:

    猜你喜欢
    • 2015-12-16
    • 1970-01-01
    • 2019-05-08
    • 2021-03-09
    • 2019-05-30
    • 2020-01-20
    • 2013-07-01
    • 2013-04-12
    • 1970-01-01
    相关资源
    最近更新 更多