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

この問題は、通常の C# スレッドの代わりに GCD を使用することで解決されました。タスクをキューに入れると、タスクは次々に実行されますが、同時には実行されません。これは、画像の膨大なリストを下にスクロールしてすべての画像がキューに入るときに、現在表示されている行が画像で満たされるまでに時間がかかるという事実を除いて、完璧に機能しました. そのため、ある種の最適化を適用しました。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 に答える