public boolean createCache(BaseDanmaku item) { // measure if (!item.isMeasured()) { item.measure(mDisp); } DrawingCache cache = null; try { cache = mCachePool.acquire(); cache = DanmakuUtils.buildDanmakuDrawingCache(item, mDisp, cache); item.cache = cache; } catch (OutOfMemoryError e) { // Log.e("cache", "break at error: oom"); if (cache != null) { mCachePool.release(cache); } item.cache = null; return false; } catch (Exception e) { // Log.e("cache", "break at exception:" + e.getMessage()); if (cache != null) { mCachePool.release(cache); } item.cache = null; return false; } return true; }
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 public IDanmakus sub(long startTime, long endTime) { if (mSortType == ST_BY_LIST || items == null || items.size() == 0) { return null; } if (subItems == null) { subItems = new Danmakus(mDuplicateMergingEnabled); } if (startItem == null) { startItem = createItem("start"); } if (endItem == null) { endItem = createItem("end"); } if (subItems != null) { long dtime = startTime - startItem.time; if (dtime >= 0 && endTime <= endItem.time) { return subItems; } } startItem.time = startTime; endItem.time = endTime; subItems.setItems(((SortedSet<BaseDanmaku>) items).subSet(startItem, endItem)); return subItems; }
public void addDanmaku(BaseDanmaku item) { if (drawTask != null) { item.flags = mContext.mGlobalFlagValues; item.setTimer(timer); drawTask.addDanmaku(item); obtainMessage(NOTIFY_RENDERING).sendToTarget(); } }
private long clearCache(BaseDanmaku oldValue) { if (oldValue.cache.hasReferences()) { oldValue.cache.decreaseReference(); oldValue.cache = null; return 0; } long size = sizeOf(oldValue); oldValue.cache.destroy(); oldValue.cache = null; return size; }
public static void fillText(BaseDanmaku danmaku, String text) { danmaku.text = text; if (TextUtils.isEmpty(text) || !text.contains(BaseDanmaku.DANMAKU_BR_CHAR)) { return; } String[] lines = danmaku.text.split(BaseDanmaku.DANMAKU_BR_CHAR, -1); if (lines.length > 1) { danmaku.lines = lines; } }
@Override public int compare(BaseDanmaku obj1, BaseDanmaku obj2) { if (mDuplicateMergingEnable && DanmakuUtils.isDuplicate(obj1, obj2)) { return 0; } int result = Float.compare(obj2.getTop(), obj1.getTop()); if (result != 0) { return result; } return DanmakuUtils.compare(obj1, obj2); }
@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; } }
@Override public void addDanmaku(BaseDanmaku item) { if (danmakuList == null) return; synchronized (danmakuList) { if (item.isLive) { removeUnusedLiveDanmakusIn(5); } item.setTimer(mTimer); item.index = danmakuList.size(); danmakuList.addItem(item); } }
@Override public boolean removeItem(BaseDanmaku item) { if (item == null) { return false; } if (item.isOutside()) { item.setVisibility(false); } if (items.remove(item)) { mSize--; return true; } return false; }
protected void removeUnusedLiveDanmakusIn(int msec) { if (danmakuList == null || danmakuList.isEmpty()) return; long startTime = System.currentTimeMillis(); IDanmakuIterator it = danmakuList.iterator(); while (it.hasNext()) { BaseDanmaku danmaku = it.next(); if (danmaku.isLive && danmaku.isTimeOut()) { it.remove(); } if (System.currentTimeMillis() - startTime > msec) { break; } } }
@Override public void releaseResource(BaseDanmaku danmaku) { // TODO 重要:清理含有ImageSpan的text中的一些占用内存的资源 例如drawable if (danmaku.text instanceof Spanned) { danmaku.text = ""; } }
protected synchronized void removeUnusedLiveDanmakusIn(int msec) { if (danmakuList == null || danmakuList.isEmpty()) return; long startTime = System.currentTimeMillis(); IDanmakuIterator it = danmakuList.iterator(); while (it.hasNext()) { BaseDanmaku danmaku = it.next(); boolean isTimeout = danmaku.isTimeOut(); if (isTimeout && danmaku.isLive) { it.remove(); onDanmakuRemoved(danmaku); } if (!isTimeout || System.currentTimeMillis() - startTime > msec) { break; } } }
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 Collection<BaseDanmaku> subset(long startTime, long endTime) { if (mSortType == ST_BY_LIST || items == null || items.size() == 0) { return null; } if (subItems == null) { subItems = new Danmakus(mDuplicateMergingEnabled); } if (startSubItem == null) { startSubItem = createItem("start"); } if (endSubItem == null) { endSubItem = createItem("end"); } startSubItem.time = startTime; endSubItem.time = endTime; return ((SortedSet<BaseDanmaku>) items).subSet(startSubItem, endSubItem); }
public static void fillLinePathData( BaseDanmaku item, float[][] points, float scaleX, float scaleY) { if (item.getType() != BaseDanmaku.TYPE_SPECIAL || points.length == 0 || points[0].length != 2) return; for (int i = 0; i < points.length; i++) { points[i][0] *= scaleX; points[i][1] *= scaleY; } ((SpecialDanmaku) item).setLinePathData(points); }
private boolean push(BaseDanmaku item, int itemSize, boolean forcePush) { int size = itemSize; // sizeOf(item); while (mRealSize + size > mMaxSize && mCaches.size() > 0) { BaseDanmaku oldValue = mCaches.first(); if (oldValue.isTimeOut()) { entryRemoved(false, oldValue, item); mCaches.removeItem(oldValue); } else { if (forcePush) { break; } return false; } } this.mCaches.addItem(item); mRealSize += size; // Log.e("CACHE", "realsize:"+mRealSize + ",size" + size); return true; }
private void clearTimeOutCaches(long time) { IDanmakuIterator it = mCaches.iterator(); while (it.hasNext() && !mEndFlag) { BaseDanmaku val = it.next(); if (val.isTimeOut()) { synchronized (mDrawingNotify) { try { mDrawingNotify.wait(30); } catch (InterruptedException e) { e.printStackTrace(); break; } } entryRemoved(false, val, null); it.remove(); } else { break; } } }
private void releaseDanmakuCache(BaseDanmaku item, DrawingCache cache) { if (cache == null) { cache = (DrawingCache) item.cache; } item.cache = null; if (cache == null) { return; } cache.destroy(); mCachePool.release(cache); }
protected void entryRemoved(boolean evicted, BaseDanmaku oldValue, BaseDanmaku newValue) { if (oldValue.cache != null) { IDrawingCache<?> cache = oldValue.cache; long releasedSize = clearCache(oldValue); if (oldValue.isTimeOut()) { mContext.getDisplayer().getCacheStuffer().releaseResource(oldValue); } if (releasedSize <= 0) return; mRealSize -= releasedSize; mCachePool.release((DrawingCache) cache); } }
@Override public void addDanmaku(BaseDanmaku danmaku) { if (mHandler != null) { if (danmaku.isLive) { if (danmaku.isTimeOut()) { return; } mHandler.createCache(danmaku); } else { mHandler.obtainMessage(CacheHandler.ADD_DANMAKKU, danmaku).sendToTarget(); } } }
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; }
@OnClick(R.id.danmu_add) void addDanmu() { i++; BaseDanmaku danmaku = danmukuContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL); Log.d("tag", "测试用弹幕 + " + i + "条"); danmaku.text = "测试用弹幕 + " + i + "条"; danmaku.padding = 10; danmaku.priority = 1; // 1:一定会显示, 一般用于本机发送的弹幕,但会导致行数的限制失效 danmaku.isLive = false; danmaku.textSize = 20; danmaku.textColor = Color.WHITE; danmaku.textShadowColor = 0; // 重要:如果有图文混排,最好不要设置描边(设textShadowColor=0),否则会进行两次复杂的绘制导致运行效率降低 danmakuView.addDanmaku(danmaku); }
/** * Initial translation data of the special danmaku * * @param item * @param dispWidth * @param dispHeight * @param beginX * @param beginY * @param endX * @param endY * @param translationDuration * @param translationStartDelay */ public static void fillTranslationData( BaseDanmaku item, float beginX, float beginY, float endX, float endY, long translationDuration, long translationStartDelay, float scaleX, float scaleY) { if (item.getType() != BaseDanmaku.TYPE_SPECIAL) return; ((SpecialDanmaku) item) .setTranslationData( beginX * scaleX, beginY * scaleY, endX * scaleX, endY * scaleY, translationDuration, translationStartDelay); updateSpecicalDanmakuDuration(item); }
@Override public synchronized void addDanmaku(BaseDanmaku item) { if (danmakuList == null) return; boolean added = false; if (item.isLive) { removeUnusedLiveDanmakusIn(10); } item.index = danmakuList.size(); if (mLastBeginMills <= item.time && item.time <= mLastEndMills) { synchronized (danmakus) { added = danmakus.addItem(item); } } else if (item.isLive) { mLastBeginMills = mLastEndMills = 0; } synchronized (danmakuList) { added = danmakuList.addItem(item); } if (added && mTaskListener != null) { mTaskListener.onDanmakuAdd(item); } }
/** * Initial alpha data of the special danmaku * * @param item * @param beginAlpha * @param endAlpha * @param alphaDuraion */ public static void fillAlphaData( BaseDanmaku item, int beginAlpha, int endAlpha, long alphaDuraion) { if (item.getType() != BaseDanmaku.TYPE_SPECIAL) return; ((SpecialDanmaku) item).setAlphaData(beginAlpha, endAlpha, alphaDuraion); updateSpecicalDanmakuDuration(item); }
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; } }
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; }
private byte buildCache(BaseDanmaku item, boolean forceInsert) { // measure if (!item.isMeasured()) { item.measure(mDisp); } DrawingCache cache = null; try { // try to find reuseable cache BaseDanmaku danmaku = findReuseableCache(item, true, 20); if (danmaku != null) { cache = (DrawingCache) danmaku.cache; } if (cache != null) { cache.increaseReference(); item.cache = cache; // Log.w("cache", danmaku.text + "DrawingCache hit!!:" + item.paintWidth + "," + // danmaku.paintWidth); mCacheManager.push(item, 0, forceInsert); return RESULT_SUCCESS; } // try to find reuseable cache from timeout || no-refrerence caches danmaku = findReuseableCache(item, false, 50); if (danmaku != null) { cache = (DrawingCache) danmaku.cache; } if (cache != null) { danmaku.cache = null; // Log.e("cache", danmaku.text + "DrawingCache hit!!:" + item.paintWidth + "," + // danmaku.paintWidth); cache = DanmakuUtils.buildDanmakuDrawingCache(item, mDisp, cache); // redraw item.cache = cache; mCacheManager.push(item, 0, forceInsert); return RESULT_SUCCESS; } // guess cache size if (!forceInsert) { int cacheSize = DanmakuUtils.getCacheSize((int) item.paintWidth, (int) item.paintHeight); if (mRealSize + cacheSize > mMaxSize) { // Log.d("cache", "break at MaxSize:"+mMaxSize); return RESULT_FAILED; } } cache = mCachePool.acquire(); cache = DanmakuUtils.buildDanmakuDrawingCache(item, mDisp, cache); item.cache = cache; boolean pushed = mCacheManager.push(item, sizeOf(item), forceInsert); if (!pushed) { releaseDanmakuCache(item, cache); // Log.e("cache", "break at push failed:" + mMaxSize); } return pushed ? RESULT_SUCCESS : RESULT_FAILED; } catch (OutOfMemoryError e) { // Log.e("cache", "break at error: oom"); releaseDanmakuCache(item, cache); return RESULT_FAILED; } catch (Exception e) { // Log.e("cache", "break at exception:" + e.getMessage()); releaseDanmakuCache(item, cache); return RESULT_FAILED; } }