2017-02-28 44 views
2

我的應用程序的特點是通過URLImageURLImage.ImageAdapter填充了圖像的InfiniteScrollAdapter模擬器中的InfiniteScrollAdapter中的URLImage顯示NPE(CodenameOne)

在模擬器(Iphone3GS或Xoom或GoogleNexus7)中,NPE在第一次出現InfiniteScrollAdapter時顯示,但該文件確實存在於服務器上。

請注意:在此測試中,數據庫中只有一個條目。所以在下面的圖片中,你應該看到的是同一行(圖片+文本)重複3次。

NPE in InfiniteScrollAdapter

請注意,在未被顯示的圖標的順序可以不同

NPE with different order in the undisplayed icons

我用來下載圖像的代碼是:

Image tempPlaceholder = Image.createImage(
      ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX, 
      ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX, 
      ParametresGeneraux.accentColor); 
    Graphics gr = tempPlaceholder.getGraphics(); 
    gr.setAntiAliased(true); 
    gr.setColor(ParametresGeneraux.accentColor); 
    gr.fillArc(0, 0, ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX, ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX, 0, 360); 

    EncodedImage roundPlaceholder = EncodedImage.createFromImage(tempPlaceholder, true); 

    final Image reportImage = URLImage.createToStorage(
          roundPlaceholder, 
          photoFilenameInStorage, 
          currentReport.getPhotoPath(), 
          ParametresGeneraux.RESIZE_SCALE_WITH_ROUND_MASK 
        ); 

這裏是被覆蓋的imageAdapter方法:

public final static URLImage.ImageAdapter RESIZE_SCALE_WITH_ROUND_MASK = new URLImage.ImageAdapter() { 
    @Override 
    public EncodedImage adaptImage(EncodedImage downloadedImage, EncodedImage placeholderImage) { 
     final Image[] tmp = new Image[1]; 

     if (!Display.getInstance().isEdt()) { 
      // The image scaling has to be called from EDT 
      Display.getInstance().callSeriallyAndWait(() -> { 

       tmp[0] = downloadedImage.scaledLargerRatio(placeholderImage.getWidth(), placeholderImage.getHeight()); 
       if (tmp[0].getWidth() > placeholderImage.getWidth()) { 
        int diff = tmp[0].getWidth() - placeholderImage.getWidth(); 
        int x = diff/2; 
        tmp[0] = tmp[0].subImage(x, 0, placeholderImage.getWidth(), placeholderImage.getHeight(), true); 
       } else if (tmp[0].getHeight() > placeholderImage.getHeight()) { 
        int diff = tmp[0].getHeight() - placeholderImage.getHeight(); 
        int y = diff/2; 
        tmp[0] = tmp[0].subImage(0, y, Math.min(placeholderImage.getWidth(), tmp[0].getWidth()), 
          Math.min(placeholderImage.getHeight(), tmp[0].getHeight()), true); 
       } 
      }); 
     } else { 
      tmp[0] = downloadedImage.scaledLargerRatio(placeholderImage.getWidth(), placeholderImage.getHeight()); 
      if (tmp[0].getWidth() > placeholderImage.getWidth()) { 
       int diff = tmp[0].getWidth() - placeholderImage.getWidth(); 
       int x = diff/2; 
       tmp[0] = tmp[0].subImage(x, 0, placeholderImage.getWidth(), placeholderImage.getHeight(), true); 
      } else if (tmp[0].getHeight() > placeholderImage.getHeight()) { 
       int diff = tmp[0].getHeight() - placeholderImage.getHeight(); 
       int y = diff/2; 
       tmp[0] = tmp[0].subImage(0, y, Math.min(placeholderImage.getWidth(), tmp[0].getWidth()), 
         Math.min(placeholderImage.getHeight(), tmp[0].getHeight()), true); 
      } 
     } 

     EncodedImage[] image2Return = new EncodedImage[1]; 
     if (!Display.getInstance().isEdt()) { 
      // The image scaling has to be called from EDT 
      Display.getInstance().callSeriallyAndWait(() -> { 
       Image roundMask = Image.createImage(tmp[0].getWidth(), tmp[0].getHeight(), 0xff000000); 
       Graphics gr = roundMask.getGraphics(); 
       gr.setColor(0xffffff); 

       gr.fillArc(0, 0, tmp[0].getWidth(), tmp[0].getHeight(), 0, 360); 
       Object mask = roundMask.createMask(); 
       tmp[0] = tmp[0].applyMask(mask); 
       image2Return[0] = EncodedImage.createFromImage(tmp[0], false); 
      }); 
     } else { 
      Image roundMask = Image.createImage(tmp[0].getWidth(), tmp[0].getHeight(), 0xff000000); 
      Graphics gr = roundMask.getGraphics(); 
      gr.setColor(0xffffff); 

      gr.fillArc(0, 0, tmp[0].getWidth(), tmp[0].getHeight(), 0, 360); 
      Object mask = roundMask.createMask(); 
      tmp[0] = tmp[0].applyMask(mask); 
      image2Return[0] = EncodedImage.createFromImage(tmp[0], false); 
     } 

     return image2Return[0]; 

    } 

在堆棧跟蹤中,NPE似乎從重寫URLImage.ImageAdapter幹:

java.lang.IllegalArgumentException異常:創建圖像失敗長度的給定 圖像數據:在 com.codename1 0 .ui.Image.createImage(Image.java:654)at com.codename1.ui.EncodedImage.getInternal(EncodedImage.java:365)at com.codename1.ui.EncodedImage.getInternalImpl(EncodedImage.java:340) at com.codename1.ui.EncodedImage.getHeight(EncodedImage.java:522)at com.codename1.ui.Image.scaledLa rgerRatio(Image.java:899)at com.my.application.ParametresGeneraux $ 1.lambda $ adaptImage $ 0(ParametresGeneraux.java:564) at com.codename1.ui.RunnableWrapper.run(RunnableWrapper.java:95)at com.codename1.ui.Display.processSerialCalls(Display.java:1154)at com.codename1.ui.Display.edtLoopImpl(Display.java:1098)at com.codename1.ui.Display.invokeAndBlock(Display.java: 1207)at com.codename1.ui.Display.invokeAndBlock(Display.java:1244)at com.codename1.ui.URLImage $ DownloadCompleted.actionPerformed(URLImage.java:233) at com.codename1.ui.URLImage $ 4 .onSucess(URLImage.java:301)at com.codename1.ui.URLImage $ 4.onSucess(URLImage.java:297)at com.codename1.util.CallbackDispatcher.run(Cal lbackDispatcher.java:53) at com.codename1.ui.Display.processSerialCalls(Display.java:1154)at com.codename1.ui.Display.edtLoopImpl(Display.java:1098)at com.codename1.ui。 Display.mainEDTLoop(Display.java:999)at com.codename1.ui.RunnableWrapper.run(RunnableWrapper.java:120)at com.codename1.impl.CodenameOneThread.run(CodenameOneThread.java:176) [EDT] 0:0:0,1 - 代號一個修訂: e5c43877074c18b4b5c7748d000e5cfac75ab749 2318

[EDT] 0:0:0,1 - 例外:顯示java.lang.NullPointerException - 在 com.codename1空 顯示java.lang.NullPointerException .impl.javase.JavaSEPort.scale(JavaSEPort.java: 3996)at com.codename1.ui.Image.scale(Image。java:1007)at com.codename1.ui.Image.scaledImpl(Image.java:953)at com.codename1.ui.Image.scaled(Image.java:918)at com.codename1.impl.javase。 JavaSEPort $ 71.save(JavaSEPort.java:7659)at com.codename1.ui.EncodedImage.scaledEncoded(EncodedImage.java:626)at com.codename1.ui.EncodedImage.scaled(EncodedImage.java:653)at com .codename1.ui.Image.scaledLargerRatio(Image.java:904)at com.my.application.ParametresGeneraux $ 1.lambda $ adaptImage $ 0(ParametresGeneraux.java:564) at com.codename1.ui.RunnableWrapper.run(RunnableWrapper .java:95)at com.codename1.ui.Display.processSerialCalls(Display.java:1154)at com.codename1.ui.Display.edtLoopImpl(Display.java:1098)at com.codename1.ui.Display.invokeAndBlock(Display.java:1207)at com.codename1.ui.Display.invokeAndBlock(Display.java:1244)at com.codename1.ui.URLImage $ DownloadCompleted.actionPerformed(URLImage (URLImage.java:297)at com.codename1.util .CallbackDispatcher.run(CallbackDispatcher.java:53) at com.codename1.ui.Display.processSerialCalls(Display.java:1154)at com.codename1.ui.Display.edtLoopImpl(Display.java:1098)at com .codename1.ui.Display.mainEDTLoop(Display.java:999)at com.codename1.ui.RunnableWrapper.run(RunnableWrapper.java:120)at com.codename1.impl.Co denameOneThread.run(CodenameOneThread.java:176)

而且,在.cn1目錄一目瞭然顯示了當一切作品,未經NPE不出現後綴「ImageURLTMP」的URLImage存儲文件名。最後,如果我以後再回到這個表單,所有事情都按預期工作(顯示圖像,不顯示NPE)。我試圖在imageAdapter中測試downloadImage nullness,但EncodedImage不爲null。

我該如何避免這種NPE?

編輯2017年3月1日

在從@Diamond和@Shai的答案,我相信NPE發生的原因InfiniteScrollAdapter要填寫與行的屏幕,因此同時推出相同的圖像的下載(因爲它不在緩存中)。所以一個解決方案可以防止InfiniteScrollAdapter循環(所以它變得有限)。我怎樣才能做到這一點 ?

請注意,沒有404錯誤,網絡監視器顯示響應代碼200,如下所示。然而,圖像不應該被下載3次,應該嗎?

No 404

+0

後您用來爲您的InfinteScroller的代碼片段和URLImage – Diamond

+0

我忘了!看我的編輯@Diamond! – HelloWorld

回答

1

您ImageAdapter更改爲以下:

public static final URLImage.ImageAdapter RESIZE_SCALE_WITH_ROUND_MASK = new URLImage.ImageAdapter() { 
    @Override 
    public EncodedImage adaptImage(EncodedImage downloadedImage, EncodedImage placeholderImage) { 
     Image tmp = downloadedImage.scaledLargerRatio(placeholderImage.getWidth(), placeholderImage.getHeight()); 
     if (tmp.getWidth() > placeholderImage.getWidth()) { 
      int diff = tmp.getWidth() - placeholderImage.getWidth(); 
      int x = diff/2; 
      tmp = tmp.subImage(x, 0, placeholderImage.getWidth(), placeholderImage.getHeight(), true); 
     } else if (tmp.getHeight() > placeholderImage.getHeight()) { 
      int diff = tmp.getHeight() - placeholderImage.getHeight(); 
      int y = diff/2; 
      tmp = tmp.subImage(0, y, Math.min(placeholderImage.getWidth(), tmp.getWidth()), 
        Math.min(placeholderImage.getHeight(), tmp.getHeight()), true); 
     } 
     Image roundMask = Image.createImage(tmp.getWidth(), tmp.getHeight(), 0xff000000); 
     Graphics gr = roundMask.getGraphics(); 
     gr.setColor(0xffffff); 
     gr.fillArc(0, 0, tmp.getWidth(), tmp.getHeight(), 0, 360); 
     Object mask = roundMask.createMask(); 
     tmp = tmp.applyMask(mask); 
     return EncodedImage.createFromImage(tmp, false); 
    } 

    @Override 
    public boolean isAsyncAdapter() { 
     return true; 
    } 
}; 

不需要檢查EDT

確保您tempPlaceholder圖像應用到您的組件首先,在你的邏輯結束,請致電URLImagecallSerially()方法:

Image tempPlaceholder = Image.createImage(
     ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX, 
     ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX, 
     ParametresGeneraux.accentColor); 
Graphics gr = tempPlaceholder.getGraphics(); 
gr.setAntiAliased(true); 
gr.setColor(ParametresGeneraux.accentColor); 
gr.fillArc(0, 0, ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX, ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX, 0, 360); 

myComponent.setIcon(tempPlaceholder); 


... 


//Then call this at the end of everything 
Display.getInstance().callSerially(() -> { 
    EncodedImage roundPlaceholder = EncodedImage.createFromImage(tempPlaceholder, true); 

    final Image reportImage = URLImage.createToStorage(
         roundPlaceholder, 
         photoFilenameInStorage, 
         currentReport.getPhotoPath(), 
         ParametresGeneraux.RESIZE_SCALE_WITH_ROUND_MASK 
       ); 
    myComponent.setIcon(reportImage); 
    myComponent.getComponentForm().repaint(); 
}); 

編輯:

基於@夏嘉曦的回答,你可以檢查你是否正在下載相同的圖像,並防止另一個被拉。因爲這通常會導致衝突:

//Declare this at the top of your class 
final static private Map<String, Image> LOADED_URLS = new HashMap<>(); 

//Then change the URLImage image method to this 
Display.getInstance().callSerially(() -> { 
    EncodedImage roundPlaceholder = EncodedImage.createFromImage(tempPlaceholder, true); 

    final Image reportImage = LOADED_URLS.containsKey(photoFilenameInStorage) ? LOADED_URLS.get(photoFilenameInStorage) 
         : URLImage.createToStorage(
         roundPlaceholder, 
         photoFilenameInStorage, 
         currentReport.getPhotoPath(), 
         ParametresGeneraux.RESIZE_SCALE_WITH_ROUND_MASK 
       ); 
    LOADED_URLS.put(photoFilenameInStorage, reportImage); 
    myComponent.setIcon(reportImage); 
    myComponent.getComponentForm().repaint(); 
}); 
+0

謝謝@Diamond,下載進度檢查很有意義。我會試一試。 – HelloWorld

+0

你能解釋爲什麼首先應用'tempPlaceholder'而不是'roundPlaceholder'是必須的嗎?你說的正確的事實是,從服務器上下載一個已下載的文件會導致問題。事實上,我遇到的情況是,只有在我的數據庫中沒有足夠數量的條目(實際上在縱向上的GoogleNexus7上少於3條)時纔會發生NPE。但即使使用了你建議的'LOADED_URLS',NPE仍然會發生。最好的辦法是防止InfiniteScrollAdapter多次顯示相同的元素。 – HelloWorld

+0

您可以使用這兩種中的任何一種。你有沒有通過網絡監視器檢查,看看隊列中的任何圖像是否返回'404錯誤'? – Diamond

1

在適配器檢查是否downloadedImage.getData()null。我認爲這不是,它是一個404錯誤頁面或類似的東西。

在這種情況下,您的適配器可以捕獲異常並返回一個後備,該後備與您希望在沒有映像存在時所看到的相符。

這是第二次運行,因爲系統看到tmp文件並假定下載正在進行中,因此它不會再次調用下載代碼。 tmp文件稍後將重命名爲最終的可下載文件。

+0

謝謝@Shai。正如我的問題所述,文件存在於服務器上(否404)!但我想你的意思是「它不是404」。我在哪裏可以捕獲NPE,我無法在'URLImage'類中找到一個錯誤方法來覆蓋? – HelloWorld

+0

包圍'tmp [0] = downloadedImage.scaledLargerRatio(placeholderImage.getWidth(),placeholderImage.getHeight()); '在try-catch塊中確實抓住了NPE。但是,然後服務器上的原始圖像將「永不」(至少在清除緩存之前)纔會顯示。有沒有辦法阻止'InfiniteScrollAdapter'多次顯示一行?我找不到任何'FiniteScrollAdapter'類。 – HelloWorld

+0

如果您的圖像命名約定很嚴格(例如使用通用前綴),則可以迭代存儲的元素並檢查可以刪除的TMP文件。您也可以通過刪除舊圖像來強制刷新。我們的一位客戶在每次應用程序啓動時都會自動執 –