<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      UniversalImageLoader的一個(gè)小問題

      最近在使用UniversalImageLoader時(shí)遇到了一個(gè)小問題,多個(gè)地方同時(shí)通過ImageLoader.getInstance().loadImage(url, new ImageSize(dp72, dp72)...加載圖像時(shí),有一定機(jī)率只有部分地方能正確地加載到圖片,其他地方是什么結(jié)果呢?從Log看是這個(gè)樣子:

      1 03-19 15:41:44.167 1500-1541/xxx D/ImageLoader﹕ Start display image task [xxxxxxx/group1/M00/00/04/wKgKklUKfS-AGTJRAAAV5nnd6hE739.jpg_144x144]
      2 03-19 15:41:44.167 1500-1541/xxx D/ImageLoader﹕ Load image from network [xxxxxxx/group1/M00/00/04/wKgKklUKfS-AGTJRAAAV5nnd6hE739.jpg_144x144]
      3 03-19 15:41:44.167 1500-1541/xxx D/ImageLoader﹕ Cache image on disk [cxxxxxxx/group1/M00/00/04/wKgKklUKfS-AGTJRAAAV5nnd6hE739.jpg_144x144]
      4 03-19 15:41:44.187 1500-1538/xxx D/ImageLoader﹕ Start display image task [xxxxxxx/group1/M00/00/04/wKgKklUKfS-AGTJRAAAV5nnd6hE739.jpg_144x144]
      5 03-19 15:41:44.187 1500-1538/xxx D/ImageLoader﹕ Image already is loading. Waiting... [xxxxxxx/group1/M00/00/04/wKgKklUKfS-AGTJRAAAV5nnd6hE739.jpg_144x144]
      6 03-19 15:41:44.199 1500-1541/xxx D/ImageLoader﹕ Cache image in memory [xxxxxxx/group1/M00/00/04/wKgKklUKfS-AGTJRAAAV5nnd6hE739.jpg_144x144]
      7 03-19 15:41:44.199 1500-1538/xxx D/ImageLoader﹕ ...Get cached bitmap from memory after waiting. [xxxxxxx/group1/M00/00/04/wKgKklUKfS-AGTJRAAAV5nnd6hE739.jpg_144x144]
      8 03-19 15:41:44.219 1500-1500/xxx D/ImageLoader﹕ Display image in ImageAware (loaded from NETWORK) [xxxxxxx/group1/M00/00/04/wKgKklUKfS-AGTJRAAAV5nnd6hE739.jpg_144x144]
      9 03-19 15:41:44.219 1500-1500/xxx D/ImageLoader﹕ ImageAware is reused for another image. Task is cancelled. [xxxxxxx/group1/M00/00/04/wKgKklUKfS-AGTJRAAAV5nnd6hE739.jpg_144x144]

      有了Log,再結(jié)合源碼,看下到底是什么原因,從上面的Log可以看到,兩個(gè)地方加載同一張圖片,都發(fā)現(xiàn)緩存中沒有,所以都從網(wǎng)絡(luò)上加載(通過分析可以知道,第1,2,3,6,8是第一個(gè)加載的地方的Log,4,5,7,9是第二個(gè)加載的地方的Log)。

      UniversalImageLoader實(shí)際加載圖片的類叫LoadAndDisplayImageTask,這是一個(gè)Runnable,所以我們從它的run方法開始看。首先要強(qiáng)調(diào)一點(diǎn),由于這兩個(gè)地方加載的是相同的Url,并且ImageSize相同,所以它們的memoryCacheKey是相同的,接下來就看run方法,首先是第一部分代碼。

      ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock;
      L.d(LOG_START_DISPLAY_IMAGE_TASK, memoryCacheKey);
      if (loadFromUriLock.isLocked()) { // 注意這里
          L.d(LOG_WAITING_FOR_IMAGE_LOADED, memoryCacheKey);
      }
      
      loadFromUriLock.lock();
      

      由于memoryCacheKey相同,所以這里獲得的是同一個(gè)鎖,結(jié)果就是第一個(gè)線程鎖住這個(gè)鎖進(jìn)行圖片加載,所以打印出了前3行Log。

      接著輪到第二個(gè)線程執(zhí)行,它發(fā)現(xiàn)另一個(gè)線程鎖住了loadFromUriLock,所以它打印出了第4和第5行Log。

      然后又換第一個(gè)線程執(zhí)行。

      try {
          checkTaskNotActual();
          bmp = configuration.memoryCache.get(memoryCacheKey);
          if (bmp == null || bmp.isRecycled()) {
              bmp = tryLoadBitmap();
              if (bmp == null) {
                  return;
              }
      
              checkTaskNotActual();
              checkTaskInterrupted();
      
              if (bmp != null && options.isCacheInMemory()) {
                  L.d(LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey); // 1
                  configuration.memoryCache.put(memoryCacheKey, bmp);
              }
          } else {
              loadedFrom = LoadedFrom.MEMORY_CACHE;
              L.d(LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING, memoryCacheKey); // 2
          }
      
          checkTaskNotActual();
          checkTaskInterrupted();
      } catch (TaskCancelledException e) {
          fireCancelEvent();
          return;
      } finally {
          // 釋放鎖
          loadFromUriLock.unlock();
      }
      
      // 顯示圖片
      DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
      runTask(displayBitmapTask, syncLoading, handler, engine);
      

      第一個(gè)線程加載完圖片后,在finally中釋放了鎖,然后通過DisplayBitmapTask進(jìn)行圖片的顯示,從Log中可以分析出,線程中加載完圖片后打印了注釋1處的Log,然后釋放了鎖,輪到線程2執(zhí)行。

      由于線程一已經(jīng)加載完圖片并存入了緩存了,所以線程二會進(jìn)入代碼注釋2的代碼塊,打印出第7行Log。

      然后線程一線程二再依次運(yùn)行分別打印第8,9行Log,兩個(gè)線程都取到了Bitmap,為何會一個(gè)正確加載完圖片,而另一個(gè)有一定機(jī)率加載不到呢?這要看UniversalImageLoader的緩存與判斷View重用的機(jī)制。

      ImageLoader在加載圖片前會調(diào)用ImageLoaderEngine.prepareDisplayTaskFor方法來記錄一些東西,具體就是記錄一個(gè)ImageAware在加載哪一個(gè)Url,以判斷當(dāng)圖片加載完成后,這個(gè)ImageAware是否被重用來加載其他的Url了。

      private final Map<Integer, String> cacheKeysForImageAwares = Collections.synchronizedMap(new HashMap<Integer, String>());
      
      void prepareDisplayTaskFor(ImageAware imageAware, String memoryCacheKey) {
          cacheKeysForImageAwares.put(imageAware.getId(), memoryCacheKey);
      }
      

      可以看到就是通過一個(gè)Map記錄的,以ImageAware的id為鍵,對于LoadImage方法,使用的是NonViewAware,它的id是Url.hasCode,所以兩個(gè)地方加載同一個(gè)圖片,在cacheKeysForImageAwares中只有一條記錄。

      當(dāng)加載完圖片后是通過DisplayBitmapTask來顯示圖片并回調(diào)我們的Listener的。

      public void run() {
          if (imageAware.isCollected()) {
              L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);
              listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
          } else if (isViewWasReused()) {
              L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey);
              listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
          } else {
              L.d(LOG_DISPLAY_IMAGE_IN_IMAGEAWARE, loadedFrom, memoryCacheKey);
              displayer.display(bitmap, imageAware, loadedFrom);
              engine.cancelDisplayTaskFor(imageAware);
              listener.onLoadingComplete(imageUri, imageAware.getWrappedView(), bitmap);
          }
      }
      
      void cancelDisplayTaskFor(ImageAware imageAware) {
          cacheKeysForImageAwares.remove(imageAware.getId());
      }
      
      private boolean isViewWasReused() {
          String currentCacheKey = engine.getLoadingUriForView(imageAware);
          return !memoryCacheKey.equals(currentCacheKey);
      }
      

      當(dāng)?shù)谝粋€(gè)地方執(zhí)行到這個(gè)run方法時(shí),會走到else分支里,打印出第8行的Log,然后調(diào)用ImageLoaderEngine.cancelDisplayTaskFor方法,移除在Map中的記錄,并回調(diào)我們的Listener。

      然后第二個(gè)地方執(zhí)行到run中的isViewWasReused方法時(shí),由于Map中的記錄已經(jīng)被第一個(gè)線程移除了,所以取得的currentCacheKey是null,就會判定為View被重用了,所以不能得到正確的結(jié)果。

      那么為什么有時(shí)兩個(gè)地方能同時(shí)得到正確的結(jié)果呢?那是因?yàn)槿绻?dāng)?shù)谝粋€(gè)線程進(jìn)入到else代碼塊但在執(zhí)行cancelDisplayTaskFor之前進(jìn)行了線程調(diào)度,另一個(gè)線程還是有機(jī)會同時(shí)進(jìn)入else代碼塊的。

      其實(shí)對于NonViewAware,基本是不可能被重用的,所以感覺在這里可以做下特殊處理,或者對其生成 id的方法進(jìn)行下修改(但這樣會多次從網(wǎng)絡(luò)取同一張圖片)。或者像Volley一樣,當(dāng)執(zhí)行一個(gè)請求時(shí),如果發(fā)現(xiàn)這個(gè)圖片正在Loading,就將其加入一個(gè)列表,當(dāng)加載完后統(tǒng)一向這個(gè)列表里的請求發(fā)送消息,但這個(gè)修改就比較麻煩了,所以還是對NonViewAware做下特殊處理比較好,畢竟這個(gè)基本是不可能被重用的。

      posted @ 2015-03-19 18:09  AngelDevil  閱讀(7183)  評論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 他掀开裙子把舌头伸进去添视频 | 国产在线精品欧美日韩电影 | 色综合色综合综合综合综合| 成人性生交片无码免费看| 亚洲婷婷综合色高清在线| 国产精品一区二区三区四区 | 白山市| 国产精品自产在线观看一| 精品乱码一区二区三四五区| 亚洲一区二区三区 无码| 色偷偷久久一区二区三区| 最近中文字幕mv免费视频| 亚洲日韩一区二区| 精品不卡一区二区三区| 金堂县| 青青草国产精品日韩欧美| 在线看国产精品自拍内射| 国产精品自产在线观看一| 久久精品| 一个人看的www视频免费观看| 中文字幕亚洲精品人妻| 日本肥老妇色xxxxx日本老妇| 宅男噜噜噜66在线观看| 国产不卡精品视频男人的天堂| 大田县| 国产欧美亚洲精品第1页| 成人亚欧欧美激情在线观看 | 国产91色在线精品三级| 亚洲国产成人AⅤ片在线观看| 欧美极品色午夜在线视频| 国产成人亚洲一区二区三区| 97精品国产91久久久久久久| 最近免费中文字幕大全| 国产精品久久久久久久专区| 国产AV无码专区亚洲AV潘金链| 99在线精品国自产拍中文字幕 | 亚洲第一极品精品无码久久| 鞍山市| 色综合热无码热国产| 国产精品亚洲片夜色在线| 无码中文字幕热热久久|