2013-07-12 26 views
0

我試圖加載GMGridView單元格的圖像。問題是,圖像加載過程並不那麼快,所以我決定去多線程。我爲背景圖片加載創建了一個很好的一體化類。下面是它的內容:在後臺加載UIImage中的文物

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,這個問題是一個非常頭疼的問題。

P.S. FireGetImageViewCallback通過我在類的構造函數中設置的委託返回一個UIImageView。 Cache是​​一個Dictionary,locker只是一個對象,rwl是一個ReaderWriterLock實例。

+0

工件是否只出現在多線程中?多線程實際上是否可以提高性能?圖像從哪裏加載? –

+0

圖像是從文件系統加載的,是的,它們中的很多需要花費一些時間才能在UI線程上加載。平滑滾動是不可能的,所以多線程是我唯一的選擇。我會再次用Ui線程加載圖像來測試我的應用程序,但是我記得當時沒有問題。 – Danchoys

+0

UIKit不是線程安全的。您必須僅從主線程更新UI。將UI的所有更新強制到主(UI)線程。您可以在後臺線程加載到NSData,然後從主線程上的NSData更新映像。我很好奇這是否能夠提高性能。 –

回答

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(); 
     }); 
    }