private void evictAllNotInScreen(boolean removeAllReferences) {
   if (mCaches != null) {
     IDanmakuIterator it = mCaches.iterator();
     while (it.hasNext()) {
       BaseDanmaku danmaku = it.next();
       IDrawingCache<?> cache = danmaku.cache;
       boolean hasReferences = cache != null && cache.hasReferences();
       if (removeAllReferences && hasReferences) {
         if (cache.get() != null) {
           mRealSize -= cache.size();
           cache.destroy();
         }
         entryRemoved(true, danmaku, null);
         it.remove();
         continue;
       }
       if (danmaku.hasDrawingCache() == false || danmaku.isOutside()) {
         entryRemoved(true, danmaku, null);
         it.remove();
       }
     }
     // mCaches.clear();
   }
   mRealSize = 0;
 }
 @Override
 protected void onDanmakuRemoved(BaseDanmaku danmaku) {
   super.onDanmakuRemoved(danmaku);
   if (danmaku.hasDrawingCache()) {
     if (danmaku.cache.hasReferences()) {
       danmaku.cache.decreaseReference();
     } else {
       danmaku.cache.destroy();
     }
     danmaku.cache = null;
   }
 }
 private final void addDanmakuAndBuildCache(BaseDanmaku danmaku) {
   if (danmaku.isTimeOut()
       || danmaku.time
           > mCacheTimer.currMillisecond + mContext.mDanmakuFactory.MAX_DANMAKU_DURATION) {
     return;
   }
   if (danmaku.priority == 0 && danmaku.isFiltered()) {
     return;
   }
   if (!danmaku.hasDrawingCache()) {
     buildCache(danmaku, true);
   }
   if (danmaku.isLive) {
     mCacheTimer.update(
         mTimer.currMillisecond + mContext.mDanmakuFactory.MAX_DANMAKU_DURATION * mScreenSize);
   }
 }
 private BaseDanmaku findReuseableCache(
     BaseDanmaku refDanmaku, boolean strictMode, int maximumTimes) {
   IDanmakuIterator it = mCaches.iterator();
   int slopPixel = 0;
   if (!strictMode) {
     slopPixel = mDisp.getSlopPixel() * 2;
   }
   int count = 0;
   while (it.hasNext() && count++ < maximumTimes) { // limit maximum times
     BaseDanmaku danmaku = it.next();
     if (!danmaku.hasDrawingCache()) {
       continue;
     }
     if (danmaku.paintWidth == refDanmaku.paintWidth
         && danmaku.paintHeight == refDanmaku.paintHeight
         && danmaku.underlineColor == refDanmaku.underlineColor
         && danmaku.borderColor == refDanmaku.borderColor
         && danmaku.textColor == refDanmaku.textColor
         && danmaku.text.equals(refDanmaku.text)) {
       return danmaku;
     }
     if (strictMode) {
       continue;
     }
     if (!danmaku.isTimeOut()) {
       break;
     }
     if (danmaku.cache.hasReferences()) {
       continue;
     }
     float widthGap = danmaku.cache.width() - refDanmaku.paintWidth;
     float heightGap = danmaku.cache.height() - refDanmaku.paintHeight;
     if (widthGap >= 0 && widthGap <= slopPixel && heightGap >= 0 && heightGap <= slopPixel) {
       return danmaku;
     }
   }
   return null;
 }
      private long prepareCaches(boolean repositioned) {
        long curr = mCacheTimer.currMillisecond;
        long end = curr + mContext.mDanmakuFactory.MAX_DANMAKU_DURATION * mScreenSize;
        if (end < mTimer.currMillisecond) {
          return 0;
        }
        long startTime = SystemClock.uptimeMillis();
        IDanmakus danmakus = null;
        int tryCount = 0;
        boolean hasException = false;
        do {
          try {
            danmakus = danmakuList.subnew(curr, end);
          } catch (Exception e) {
            hasException = true;
            SystemClock.sleep(10);
          }
        } while (++tryCount < 3 && danmakus == null && hasException);
        if (danmakus == null) {
          mCacheTimer.update(end);
          return 0;
        }
        BaseDanmaku first = danmakus.first();
        BaseDanmaku last = danmakus.last();
        if (first == null || last == null) {
          mCacheTimer.update(end);
          return 0;
        }
        long deltaTime = first.time - mTimer.currMillisecond;
        long sleepTime = 30 + 10 * deltaTime / mContext.mDanmakuFactory.MAX_DANMAKU_DURATION;
        sleepTime = Math.min(100, sleepTime);
        if (repositioned) {
          sleepTime = 0;
        }

        IDanmakuIterator itr = danmakus.iterator();
        BaseDanmaku item = null;
        long consumingTime = 0;
        int orderInScreen = 0;
        int currScreenIndex = 0;
        int sizeInScreen = danmakus.size();
        //                String message = "";
        while (!mPause && !mCancelFlag) {
          boolean hasNext = itr.hasNext();
          if (!hasNext) {
            //                        message = "break at not hasNext";
            break;
          }
          item = itr.next();

          if (last.time < mTimer.currMillisecond) {
            //                        message = "break at last.time < mTimer.currMillisecond";
            break;
          }

          if (item.hasDrawingCache()) {
            continue;
          }

          if (repositioned == false && (item.isTimeOut() || !item.isOutside())) {
            continue;
          }

          if (!item.hasPassedFilter()) {
            mContext.mDanmakuFilters.filter(
                item, orderInScreen, sizeInScreen, null, true, mContext);
          }

          // Log.e("prepareCache", currScreenIndex+","+orderInScreen+"," + item.time+"skip:"+skip);
          if (item.priority == 0 && item.isFiltered()) {
            continue;
          }

          if (item.getType() == BaseDanmaku.TYPE_SCROLL_RL) {
            // 同屏弹幕密度只对滚动弹幕有效
            int screenIndex =
                (int) ((item.time - curr) / mContext.mDanmakuFactory.MAX_DANMAKU_DURATION);
            if (currScreenIndex == screenIndex) orderInScreen++;
            else {
              orderInScreen = 0;
              currScreenIndex = screenIndex;
            }
          }

          if (!repositioned) {
            try {
              synchronized (mDrawingNotify) {
                mDrawingNotify.wait(sleepTime);
              }
            } catch (InterruptedException e) {
              e.printStackTrace();
              break;
            }
          }

          // build cache
          if (buildCache(item, false) == RESULT_FAILED) {
            // message = "break at build failed";
            break;
          }

          if (!repositioned) {
            consumingTime = SystemClock.uptimeMillis() - startTime;
            if (consumingTime >= DanmakuFactory.COMMON_DANMAKU_DURATION * mScreenSize) {
              //                            message = "break at consumingTime out:" + consumingTime;
              break;
            }
          }
        }
        consumingTime = SystemClock.uptimeMillis() - startTime;
        if (item != null) {
          mCacheTimer.update(item.time);
          // Log.i("cache","stop at :"+item.time+","+count+",size:"+danmakus.size()+","+message);
        } else {
          mCacheTimer.update(end);
        }
        return consumingTime;
      }
 public void invalidateDanmaku(BaseDanmaku danmaku) {
   if (mHandler != null && danmaku.hasDrawingCache()) {
     mHandler.obtainMessage(CacheHandler.REBUILD_CACHE, danmaku).sendToTarget();
   }
 }
 @Override
 public void handleMessage(Message msg) {
   int what = msg.what;
   switch (what) {
     case PREPARE:
       evictAllNotInScreen();
       for (int i = 0; i < 300; i++) {
         mCachePool.release(new DrawingCache());
       }
     case DISPATCH_ACTIONS:
       // Log.e(TAG,"dispatch_actions:"+mCacheTimer.currMillisecond+":"+mTimer.currMillisecond);
       long delayed = dispatchAction();
       if (delayed <= 0) {
         delayed = mContext.mDanmakuFactory.MAX_DANMAKU_DURATION / 2;
       }
       sendEmptyMessageDelayed(DISPATCH_ACTIONS, delayed);
       break;
     case BUILD_CACHES:
       removeMessages(BUILD_CACHES);
       boolean repositioned = ((mTaskListener != null && mReadyState == false) || mSeekedFlag);
       prepareCaches(repositioned);
       if (repositioned) mSeekedFlag = false;
       if (mTaskListener != null && mReadyState == false) {
         mTaskListener.ready();
         mReadyState = true;
       }
       //
       // Log.i(TAG,"BUILD_CACHES:"+mCacheTimer.currMillisecond+":"+mTimer.currMillisecond);
       break;
     case ADD_DANMAKKU:
       BaseDanmaku item = (BaseDanmaku) msg.obj;
       addDanmakuAndBuildCache(item);
       break;
     case REBUILD_CACHE:
       Pair<BaseDanmaku, Boolean> pair = (Pair<BaseDanmaku, Boolean>) msg.obj;
       if (pair != null) {
         BaseDanmaku cacheitem = pair.first;
         if (pair.second) {
           cacheitem.requestFlags |= BaseDanmaku.FLAG_REQUEST_REMEASURE;
         }
         cacheitem.requestFlags |= BaseDanmaku.FLAG_REQUEST_INVALIDATE;
         if (!pair.second && cacheitem.hasDrawingCache() && !cacheitem.cache.hasReferences()) {
           DrawingCache cache =
               DanmakuUtils.buildDanmakuDrawingCache(
                   cacheitem, mDisp, (DrawingCache) cacheitem.cache);
           cacheitem.cache = cache;
           push(cacheitem, 0, true);
           return;
         }
         if (cacheitem.isLive) {
           clearCache(cacheitem);
           createCache(cacheitem);
         } else {
           entryRemoved(true, cacheitem, null);
           addDanmakuAndBuildCache(cacheitem);
         }
       }
       break;
     case CLEAR_TIMEOUT_CACHES:
       clearTimeOutCaches();
       break;
     case SEEK:
       Long seekMills = (Long) msg.obj;
       if (seekMills != null) {
         mCacheTimer.update(seekMills.longValue());
         mSeekedFlag = true;
         evictAllNotInScreen();
         resume();
       }
       break;
     case QUIT:
       removeCallbacksAndMessages(null);
       mPause = true;
       evictAll();
       clearCachePool();
       this.getLooper().quit();
       break;
     case CLEAR_ALL_CACHES:
       evictAll();
       mCacheTimer.update(
           mTimer.currMillisecond - mContext.mDanmakuFactory.MAX_DANMAKU_DURATION);
       mSeekedFlag = true;
       break;
     case CLEAR_OUTSIDE_CACHES:
       evictAllNotInScreen(true);
       mCacheTimer.update(mTimer.currMillisecond);
       break;
     case CLEAR_OUTSIDE_CACHES_AND_RESET:
       evictAllNotInScreen(true);
       mCacheTimer.update(mTimer.currMillisecond);
       requestClear();
       break;
   }
 }