【问题标题】:Memory-Leaks Image-Gallery Android - How can other applications handle it?Memory-Leaks Image-Gallery Android - 其他应用程序如何处理它?
【发布时间】:2013-02-08 15:31:25
【问题描述】:

我正在尝试实现一个图片库,它应该显示大约 5-15 个较小的图片和一个“当前选择的”较大的图片。

看起来像:http://www.mobisoftinfotech.com/blog/wp-content/uploads/2012/06/galleryDemo.png

我查了很多资源,现在决定使用位图缓存(lru-cache)(感谢这个论坛的人!)。

目前我没有遇到内存泄漏,但我对这个解决方案不满意,因为每次滚动时,都会从缓存中删除一些图像,我必须重新加载它们......所以用户有等待重新加载图像...每次滚动到另一侧时等待 0.5 - 1 秒真的很烦人...

public static WeakReference<Bitmap> getBitmap(String imageName, int width,
        int height) {
    if (!imageName.contains(".jpg")){
        imageName += ".jpg";
    }
    String pathToImage = getPathToImage(imageName);
    Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(pathToImage, options);

    /*
     * Calculate inSampleSize
     */
    options.inSampleSize = calculateInSampleSize(options, width, height);

    /*
     * Removes the alpha-channel from bitmap (not necessary)
     */
    options.inPreferredConfig = Bitmap.Config.RGB_565;

    /*
     * Decode bitmap with inSampleSize set
     */
    options.inJustDecodeBounds = false;

    WeakReference<Bitmap> scaledBitmap = new WeakReference<Bitmap>(
            BitmapFactory.decodeFile(pathToImage, options));
    return scaledBitmap;

我在获取图像时可能犯了错误吗? 目前我将为单选图像使用 320x480 分辨率,为底部列表使用 64x64...

真正奇怪的是,不管图像的分辨率有多大,lru-cache 仍然会删除一些图像,即使我只选择 64x64 图像... 我可能对 lru 缓存做错了什么?

以下代码显示了我的实现:

    /*
     * Bitmaps which should be shown
     */
    mMemoryCache = (BitmapCache) getLastNonConfigurationInstance();
    if (mMemoryCache == null) {
        // 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/4th of the available memory for this memory cache.
        final int cacheSize = maxMemory / 2; // TODO default 8

        mMemoryCache = new BitmapCache(cacheSize);
    }

还有 BitmapCache 类:

public BitmapCache(int maxSize, Resources resources) {
    super(maxSize);
    this.resources = resources;
}

@Override
protected int sizeOf(String key, AsyncDrawable value) {
    if (value.getBitmap() != null) {
        /* 
         * The cache size will be measured in kilobytes rather than
         * number of items.
         */
        return value.getBitmap().getRowBytes()
                * value.getBitmap().getHeight() / 1024;
    }
    return super.sizeOf(key, value);
}

public void loadBitmap(Picture picture, ImageView mImageView,
        ImageResolution resolution) {
    if (cancelPotentialWork(picture.getFileName(), //  + resolution.name()
            mImageView)) {
        final BitmapWorkerTask task = new BitmapWorkerTask(this,
                mImageView, picture, resolution);
        final AsyncDrawable asyncDrawable = new AsyncDrawable(resources,
                null, task);
        mImageView.setImageDrawable(asyncDrawable);
        task.execute();
    }
}

非常感谢:(

更多代码:BitmapWorkerTask:

@Override
protected Bitmap doInBackground(Void... params) {
    /*
     *  Decode image in background.
     */
    final Bitmap bitmap = FileHandler.getBitmap(
            picture.getPictureId() + "", resolution).get();
    return bitmap;
}

@Override
protected void onPostExecute(Bitmap bitmap) {
    /*
     *  Once complete, see if ImageView is still around and set bitmap.
     */
    if (isCancelled()) {
        bitmap = null;
    }

    if (imageViewReference != null && bitmap != null) {
        final ImageView imageView = imageViewReference.get();

        if (imageView != null) {
            final Drawable drawable = imageView.getDrawable();
            if (drawable instanceof AsyncDrawable) {
                final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
                final BitmapWorkerTask bitmapWorkerTask = asyncDrawable
                        .getBitmapWorkerTask();
                if (this == bitmapWorkerTask && imageView != null) {
                    imageView.setImageBitmap(bitmap);
                    bitmapCache.addBitmapToMemoryCache(
                            picture.getFileName(), // + resolution.name()
                            asyncDrawable);
                }
            }
        }
    }
}

【问题讨论】:

  • 是否有可能获取当前正在查看的图像的最近邻居并缓存它们..只是提出一个想法
  • 嘿,谢谢你的想法!可以获得最近的邻居;)我认为加载它们可能是一个好主意......但这并不能解决一些“快速”滚动动作,用户只是从一侧滑到另一侧。 ..我认为其他图片展示画廊已经解决了这个问题......就像whatsapp - 在组媒体活动中,我可以尽可能快地滚动并且不需要重新加载图像(或者重新加载在某种程度上要好得多实施然后我的)
  • 会不会不是图片加载的问题,而是细胞回收的问题?我的意思是,当您使用适配器快速滚动时,右侧未使用的单元格将在左侧使用(这样它就不会膨胀所有内容)。那么,如果你快速向左滚动会发生什么?它可能会连续启动 5 个异步任务,但会重复使用同一个单元格 2 次。那么当 3 个异步任务尝试设置图像时会发生什么?
  • 或者也可能是并发问题?就像几个异步任务试图同时访问 lru 缓存一样?
  • 嘿,我正在使用“asyncdrawable”,其中每个drawable 都有一个bitmapworker 任务。我的缓存看到,如果当前 asynctask 正在运行并取消它,当另一个任务开始时......我不认为,重新加载是问题。我不明白的事实是,为什么缓存会删除一些位图……或者 whatsapp 或其他应用程序如何同时显示更多图像。我在示例中添加了更多代码。你能看一下吗?

标签: android bitmap gallery lru android-lru-cache


【解决方案1】:

太棒了!我发现了为什么它不能工作的错误。问题出在我的 LruCache 中,它存储了 AsyncDrawables,这完全是愚蠢的。每次调用LruCache中的sizeOf,asyncdrawable里面的bitmap都是null,所以他只返回super.sizeOf(key, value)。

现在它就像一个魅力! :)

感谢您的帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-21
    • 2011-11-11
    • 1970-01-01
    • 1970-01-01
    • 2011-08-02
    相关资源
    最近更新 更多