0

我正在尝试为 GMGridView 单元格加载图像。问题是图像加载过程不是那么快,所以我决定使用多线程。我为背景图像加载创建了一个很好的一体化类。这是它的内容:

    public void LoadImageIntoView (string imageURL, UIImageView imageView, int index)
    {
        rwl.AcquireReaderLock (Timeout.Infinite);
        if (disposed)
            return;

        UIImage image;
        lock (locker) {
            cache.TryGetValue (imageURL, out image);
        }
        if (image != null)
            imageView.Image = image;
        else {
            new Thread (() => {
                if (MediaLoader.IsFileCached (imageURL))
                    LoadImage (index, imageURL);
                else {
                    MediaLoader loader = new MediaLoader ();
                    loader.OnCompleteDownload += (object sender, OnCompleteDownloadEventArgs e) => {
                        if (e.Success)
                            LoadImage (index, e.FileURL);
                    };
                    loader.GetFileAsync (imageURL, false, DownloadPriority.Low);
                }
            }).Start ();
        }
        rwl.ReleaseReaderLock ();
    }

    private void LoadImage (int index, string imageURL)
    {
        rwl.AcquireReaderLock (Timeout.Infinite);
        if (disposed)
            return;

        string pathToFile = MediaLoader.GetCachedFilePath (imageURL);

        UIImage uiImage = UIImage.FromFile (pathToFile);;

        // Load the image
        if (uiImage != null) {
            lock (locker) {
                cache [imageURL] = uiImage;
            }
            BeginInvokeOnMainThread (() => InsertImage (false, index, uiImage));
        }
        rwl.ReleaseReaderLock ();
    }

    private void InsertImage (bool secondTime, int index, UIImage image)
    {
        rwl.AcquireReaderLock (Timeout.Infinite);
        if (disposed)
            return;

        UIImageView imageView = FireGetImageViewCallback (index);

        if (imageView != null) {
            CATransition transition = CATransition.CreateAnimation ();
            transition.Duration = 0.3f;
            transition.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut);
            transition.Type = CATransition.TransitionFade;
            imageView.Layer.AddAnimation (transition, null);

            imageView.Image = image;
        } else {
            if (!secondTime) {
                new Thread (() => {
                    Thread.Sleep (150);
                    BeginInvokeOnMainThread (() => InsertImage (true, index, image));
                }).Start ();
            }
        }
        rwl.ReleaseReaderLock ();
    }

我还尝试了在 LoadImage 方法中加载图像的代码:

        UIImage loadedImage = UIImage.FromFile (pathToFile);
        CGImage image = loadedImage.CGImage;
        if (image != null) {
            CGColorSpace colorSpace = CGColorSpace.CreateDeviceRGB ();
            // Create a bitmap context from the image's specifications
            CGBitmapContext bitmapContext = new CGBitmapContext (null, image.Width, image.Height, image.BitsPerComponent, image.Width * 4, colorSpace, CGImageAlphaInfo.PremultipliedFirst);
            bitmapContext.ClearRect (new System.Drawing.RectangleF (0, 0, image.Width, image.Height));
            // Draw the image into the bitmap context and retrieve the
            // decompressed image

            bitmapContext.DrawImage (new System.Drawing.RectangleF (0, 0, image.Width, image.Height), image);
            CGImage decompressedImage = bitmapContext.ToImage ();

            // Create a UIImage
            uiImage = new UIImage (decompressedImage);

            // Release everything
            colorSpace.Dispose ();
            decompressedImage.Dispose ();
            bitmapContext.Dispose ();
            image.Dispose ();
        }

当我构建并尝试我的应用程序时,我的 ImageLoader 返回的图像有时会在其中包含工件。有时它可能是随机位置的白色矩形,有时它可能是一些意想不到的彩色像素。我很高兴听到这个问题的解决方案,因为该应用程序即将进入 AppStore,这个问题令人头疼。

PS FireGetImageViewCallback 通过我在类的构造函数中设置的委托返回一个 UIImageView。Cache 是 Dictionary , locker 只是一个对象, rwl 是 ReaderWriterLock 实例。

4

1 回答 1

0

通过使用 GCD 而不是通常的 C# 线程解决了这个问题。它将任务放入队列中,使它们一个接一个地运行,但不是同时运行。这很完美,除了当您向下滚动大量图像并且所有图像都进入队列时,当前可见的行需要很长时间才能被图像填充。这就是我应用某种优化的原因:当我的 ImageLoader 的 LoadImageIntoView 方法被调用时,它还提供了一个索引,因此 ImageLoader 知道最后获取了哪一行。在任务中,我检查将要下载其图像的单元格当前是否可见,如果不可见,则它只是返回,允许执行下一个任务。下面是一些说明这种方法的代码:

    private void LoadImage (int index, string imageURL)
    {
        DispatchQueue.GetGlobalQueue (DispatchQueuePriority.Low).DispatchAsync (() => {
            rwl.AcquireReaderLock (Timeout.Infinite);
            if (disposed)
                return;

            bool shouldDownload = false;
            lastAcquiredIndexRwl.AcquireReaderLock (Timeout.Infinite);
            shouldDownload = index <= (lastAcquiredIndex + visibleRange) && index >= (lastAcquiredIndex - visibleRange);
            lastAcquiredIndexRwl.ReleaseReaderLock ();

            if (shouldDownload) {
                string pathToFile = MediaLoader.GetCachedFilePath (imageURL);

                UIImage uiImage = null;

                // Load the image
                CGDataProvider dataProvider = new CGDataProvider (pathToFile);

                CGImage image = null;
                if (pathToFile.IndexOf (".png") != -1)
                    image = CGImage.FromPNG (dataProvider, null, false, CGColorRenderingIntent.Default);
                else
                    image = CGImage.FromJPEG (dataProvider, null, false, CGColorRenderingIntent.Default);

                if (image != null) {
                    CGColorSpace colorSpace = CGColorSpace.CreateDeviceRGB ();

                    // Create a bitmap context from the image's specifications
                    CGBitmapContext bitmapContext = new CGBitmapContext (null, image.Width, image.Height, image.BitsPerComponent, image.Width * 4, colorSpace, CGImageAlphaInfo.PremultipliedFirst | (CGImageAlphaInfo)CGBitmapFlags.ByteOrder32Little);
                    colorSpace.Dispose ();  

                    bitmapContext.ClearRect (new System.Drawing.RectangleF (0, 0, image.Width, image.Height));

                    // Draw the image into the bitmap context and retrieve the
                    // decompressed image   
                    bitmapContext.DrawImage (new System.Drawing.RectangleF (0, 0, image.Width, image.Height), image);
                    image.Dispose ();
                    CGImage decompressedImage = bitmapContext.ToImage ();
                    bitmapContext.Dispose ();

                    uiImage = new UIImage (decompressedImage);
                    decompressedImage.Dispose ();
                }

                if (uiImage != null) {
                    lock (locker) {
                        cache [imageURL] = uiImage;
                    }
                    DispatchQueue.MainQueue.DispatchAsync (() => InsertImage (false, index, uiImage));
                }
            }
            rwl.ReleaseReaderLock ();
        });
    }
于 2013-07-15T08:51:52.020 回答