【问题标题】:Load large Bitmaps加载大型位图
【发布时间】:2015-12-19 00:09:36
【问题描述】:

我有以下两种方法可以加载我的Bitmaps。当我尝试加载巨大的图像 2000px x 6000px 时,我得到了 UIfreeze 大约 3 秒。 anubody 可以帮助我提高大图像的加载速度吗?我对屏幕方向的改变很重要,因为这些方法总是被那个库调用并且我的 UI 在旋转之前总是冻结大约 3 秒...

    /**
     * Async task used to get image details
     */ 
    private static class TilesInitTask implements Runnable {
        private final WeakReference<ScaleImageView> viewRef;
        private final WeakReference<Context> contextRef;
        private final WeakReference<DecoderFactory<? extends ImageRegionDecoder>> decoderFactoryRef;
        private final Uri source;
        private ImageRegionDecoder decoder;
        private Exception exception;

        public TilesInitTask(ScaleImageView view, Context context, DecoderFactory<? extends ImageRegionDecoder> decoderFactory, Uri source) {
            this.viewRef = new WeakReference<>(view);
            this.contextRef = new WeakReference<>(context);
            this.decoderFactoryRef = new WeakReference<DecoderFactory<? extends ImageRegionDecoder>>(decoderFactory);
            this.source = source;
        }

        public void run() {
            ScaleImageView view = null;
            try {
                String sourceUri = source.toString();
                Context context = contextRef.get();
                DecoderFactory<? extends ImageRegionDecoder> decoderFactory = decoderFactoryRef.get();
                view = viewRef.get();
                if (context != null && decoderFactory != null && view != null) {
                    decoder = decoderFactory.make();
                    Point dimensions = decoder.init(context, source);
                    int sWidth = dimensions.x;
                    int sHeight = dimensions.y;

                    int exifOrientation = view.getExifOrientation(sourceUri);
                    if (view.sRegion != null) {
                        sWidth = view.sRegion.width();
                        sHeight = view.sRegion.height();
                    }
                    view.onTilesInited(decoder, sWidth, sHeight, exifOrientation);
                }
            } catch (Exception e) {
                Log.e(TAG, "Failed to initialise bitmap decoder", e);
                this.exception = e;
                if (view != null && view.onImageEventListener != null) {
                    view.onImageEventListener.onImageLoadError(exception);
                }
            }
        }
    }

    /**
     * Called by worker task when decoder is ready and image size and EXIF orientation is known.
     */
    private synchronized void onTilesInited(ImageRegionDecoder decoder, int sWidth, int sHeight, int sOrientation) {
        // If actual dimensions don't match the declared size, reset everything.
        if (this.sWidth > 0 && this.sHeight > 0 && (this.sWidth != sWidth || this.sHeight != sHeight)) {
            reset(false);
            if (bitmap != null) {
                if (!bitmapIsCached) {
                    bitmap.recycle();
                }
                bitmap = null;
                bitmapIsPreview = false;
                bitmapIsCached = false;
            }
        }
        this.decoder = decoder;
        this.sWidth = sWidth;
        this.sHeight = sHeight;
        this.sOrientation = sOrientation;
        checkReady();
        checkImageLoaded();
        invalidate();
        requestLayout();
    }

    /**
     * Async task used to load images
     */
    private static class TileLoadTask implements Runnable {
        private final WeakReference<ScaleImageView> viewRef;
        private final WeakReference<ImageRegionDecoder> decoderRef;
        private final WeakReference<Tile> tileRef;
        private Exception exception;

        public TileLoadTask(ScaleImageView view, ImageRegionDecoder decoder, Tile tile) {
            this.viewRef = new WeakReference<>(view);
            this.decoderRef = new WeakReference<>(decoder);
            this.tileRef = new WeakReference<>(tile);
            tile.loading = true;
        }

        public void run() {
            ScaleImageView view = null;
            try {
                view = viewRef.get();
                ImageRegionDecoder decoder = decoderRef.get();
                Tile tile = tileRef.get();
                if (decoder != null && tile != null && view != null && decoder.isReady()) {
                    synchronized (view.decoderLock) {
                        // Update tile's file sRect according to rotation
                        view.fileSRect(tile.sRect, tile.fileSRect);
                        if (view.sRegion != null) {
                            tile.fileSRect.offset(view.sRegion.left, view.sRegion.top);
                        }
                        tile.bitmap = decoder.decodeRegion(tile.fileSRect, tile.sampleSize);
                        tile.loading = false;
                        view.onTileLoaded();
                    }
                } else if (tile != null) {
                    tile.loading = false;
                }
            } catch (Exception e) {
                Log.e(TAG, "Failed to decode tile", e);
                this.exception = e;
                if (view != null && view.onImageEventListener != null) {
                    view.onImageEventListener.onTileLoadError(exception);
                }
            }
        }
    }

    /**
     * Called by worker task when a tile has loaded. Redraws the view.
     */
    private synchronized void onTileLoaded() {
        checkReady();
        checkImageLoaded();
        if (isBaseLayerReady() && bitmap != null) {
            if (!bitmapIsCached) {
                bitmap.recycle();
            }
            bitmap = null;
            bitmapIsPreview = false;
            bitmapIsCached = false;
        }
        invalidate();
    }

【问题讨论】:

  • 如果您在单独的线程中加载图像,则不应有任何形式的 UI 冻结。你为什么用我的AsyncTask?你是如何运行这些Runnables 的?在适当的情况下使用WeakReference 大受好评,但我目前正在手机中,无法详细研究您的源代码。但是,您的代码中似乎仍然存在一些危险信号。就像在Bitmap 上调用recycle() 或在主线程之外访问Views
  • 如果您告诉我您要加载什么样的图像,将能够提供解决方案!
  • @XaverKapeller,这些方法在我的视图中显示一些照片。我在 View 类中使用 .run() 方法调用它们
  • @XaverKapeller,我正在尝试根据我的需要重新制作这个库github.com/davemorrissey/subsampling-scale-image-view。当默认代码图像上的旋转屏幕消失一秒钟并再次重新加载 3 秒钟时,我决定冻结 UI 以避免白屏,但现在我正试图提高加载速度)))这些方法来自 SubsamplingScaleImageView(AsynkTasks)

标签: android android-asynctask runnable android-bitmap


【解决方案1】:

使用位图和图片是一项敏感操作。我建议您使用库来完成此类操作

https://github.com/nostra13/Android-Universal-Image-Loader

http://square.github.io/picasso/

编辑:

在这里您可以了解如何添加和初始化库。 Correct usage of Universal Image Loader

以最佳配置初始化后,您可以使用以下适合您操作的文件路径:

"http://example.com/image.png" // from Web
"file:///mnt/sdcard/image.png" // from SD card
"file:///mnt/sdcard/video.mp4" // from SD card (video thumbnail)
"content://media/external/images/media/13" // from content provider
"content://media/external/video/media/13" // from content provider (video thumbnail)
"assets://image.png" // from assets
"drawable://" + R.drawable.img // from drawables (non-9patch images)

最后你可以加载它:

ImageLoader imageLoader = ImageLoader.getInstance(); // Get singleton instance
imageLoader.displayImage(imageUri, imageView);

祝你好运。

【讨论】:

  • 去看看其他很棒的库(Picasso、Glide 和 Fresco)
猜你喜欢
  • 2013-04-29
  • 1970-01-01
  • 2011-02-17
  • 2014-08-15
  • 2016-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多