Skip to content
This repository has been archived by the owner on Feb 6, 2023. It is now read-only.

Commit

Permalink
Memory thrashing problem.
Browse files Browse the repository at this point in the history
1. Avoid repeated creation of SVGADrawerSprite.
2. The replace function leads to the creation of a large number of objects.
  • Loading branch information
xxjy committed Nov 15, 2020
1 parent a8bcaf2 commit 7877a37
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import android.graphics.Canvas
import android.widget.ImageView
import com.opensource.svgaplayer.SVGAVideoEntity
import com.opensource.svgaplayer.entities.SVGAVideoSpriteFrameEntity
import com.opensource.svgaplayer.utils.Pools
import com.opensource.svgaplayer.utils.SVGAScaleInfo
import kotlin.math.max

/**
* Created by cuiminghui on 2017/3/29.
Expand All @@ -14,7 +16,13 @@ open internal class SGVADrawer(val videoItem: SVGAVideoEntity) {

val scaleInfo = SVGAScaleInfo()

inner class SVGADrawerSprite(val matteKey: String?, val imageKey: String?, val frameEntity: SVGAVideoSpriteFrameEntity)
private val spritePool = Pools.SimplePool<SVGADrawerSprite>(max(1, videoItem.spriteList.size))

inner class SVGADrawerSprite(var _matteKey: String? = null, var _imageKey: String? = null, var _frameEntity: SVGAVideoSpriteFrameEntity? = null) {
val matteKey get() = _matteKey
val imageKey get() = _imageKey
val frameEntity get() = _frameEntity!!
}

internal fun requestFrameSprites(frameIndex: Int): List<SVGADrawerSprite> {
return videoItem.spriteList.mapNotNull {
Expand All @@ -23,13 +31,21 @@ open internal class SGVADrawer(val videoItem: SVGAVideoEntity) {
if (!imageKey.endsWith(".matte") && it.frames[frameIndex].alpha <= 0.0) {
return@mapNotNull null
}
return@mapNotNull SVGADrawerSprite(it.matteKey, it.imageKey, it.frames[frameIndex])
return@mapNotNull (spritePool.acquire() ?: SVGADrawerSprite()).apply {
_matteKey = it.matteKey
_imageKey = it.imageKey
_frameEntity = it.frames[frameIndex]
}
}
}
return@mapNotNull null
}
}

internal fun releaseFrameSprites(sprites: List<SVGADrawerSprite>) {
sprites.forEach { spritePool.release(it) }
}

open fun drawFrame(canvas : Canvas, frameIndex: Int, scaleType: ImageView.ScaleType) {
scaleInfo.performScaleType(canvas.width.toFloat(),canvas.height.toFloat(), videoItem.videoSize.width.toFloat(), videoItem.videoSize.height.toFloat(), scaleType)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ internal class SVGACanvasDrawer(videoItem: SVGAVideoEntity, val dynamicItem: SVG
}
}
}
releaseFrameSprites(sprites)
}

private fun isMatteBegin(spriteIndex: Int, sprites: List<SVGADrawerSprite>): Boolean {
Expand All @@ -101,7 +102,7 @@ internal class SVGACanvasDrawer(videoItem: SVGAVideoEntity, val dynamicItem: SVG
svgaDrawerSprite.matteKey?.let {
if (it.length > 0) {
sprites.get(index - 1)?.let { lastSprite ->
if (lastSprite.matteKey == null || lastSprite.matteKey.length == 0) {
if (lastSprite.matteKey.isNullOrEmpty()) {
boolArray[index] = true
} else {
if (lastSprite.matteKey != svgaDrawerSprite.matteKey) {
Expand Down Expand Up @@ -135,7 +136,7 @@ internal class SVGACanvasDrawer(videoItem: SVGAVideoEntity, val dynamicItem: SVG
boolArray[index] = true
} else {
sprites.get(index + 1)?.let { nextSprite ->
if (nextSprite.matteKey == null || nextSprite.matteKey.length == 0) {
if (nextSprite.matteKey.isNullOrEmpty()) {
boolArray[index] = true
} else {
if (nextSprite.matteKey != svgaDrawerSprite.matteKey) {
Expand Down Expand Up @@ -188,7 +189,7 @@ internal class SVGACanvasDrawer(videoItem: SVGAVideoEntity, val dynamicItem: SVG
val imageKey = sprite.imageKey ?: return
val isHidden = dynamicItem.dynamicHidden[imageKey] == true
if (isHidden) { return }
val bitmapKey = imageKey.replace(".matte", "")
val bitmapKey = if (imageKey.endsWith(".matte")) imageKey.substring(0, imageKey.length - 6) else imageKey
val drawingBitmap = (dynamicItem.dynamicImage[bitmapKey] ?: videoItem.imageMap[bitmapKey]) ?: return
val frameMatrix = shareFrameMatrix(sprite.frameEntity.transform)
val paint = this.sharedValues.sharedPaint()
Expand Down
102 changes: 102 additions & 0 deletions library/src/main/java/com/opensource/svgaplayer/utils/Pools.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.opensource.svgaplayer.utils

/**
* Helper class for creating pools of objects. An example use looks like this:
* <pre>
* public class MyPooledClass {
*
* private static final SynchronizedPool<MyPooledClass> sPool =
* new SynchronizedPool<MyPooledClass>(10);
*
* public static MyPooledClass obtain() {
* MyPooledClass instance = sPool.acquire();
* return (instance != null) ? instance : new MyPooledClass();
* }
*
* public void recycle() {
* // Clear state if needed.
* sPool.release(this);
* }
*
* . . .
* }
* </pre>
*
*/
class Pools private constructor() {

/**
* Interface for managing a pool of objects.
*
* @param <T> The pooled type.
*/
interface Pool<T> {
/**
* @return An instance from the pool if such, null otherwise.
*/
fun acquire(): T?

/**
* Release an instance to the pool.
*
* @param instance The instance to release.
* @return Whether the instance was put in the pool.
*
* @throws IllegalStateException If the instance is already in the pool.
*/
fun release(instance: T): Boolean
}

/**
* Simple (non-synchronized) pool of objects.
*
* @param maxPoolSize The max pool size.
*
* @throws IllegalArgumentException If the max pool size is less than zero.
*
* @param <T> The pooled type.
*/
open class SimplePool<T>(maxPoolSize: Int) : Pool<T> {
private val mPool: Array<Any?>
private var mPoolSize = 0

init {
require(maxPoolSize > 0) { "The max pool size must be > 0" }
mPool = arrayOfNulls(maxPoolSize)
}

@Suppress("UNCHECKED_CAST")
override fun acquire(): T? {
if (mPoolSize > 0) {
val lastPooledIndex = mPoolSize - 1
val instance = mPool[lastPooledIndex] as T?
mPool[lastPooledIndex] = null
mPoolSize--
return instance
}
return null
}

override fun release(instance: T): Boolean {
check(!isInPool(instance)) { "Already in the pool!" }
if (mPoolSize < mPool.size) {
mPool[mPoolSize] = instance
mPoolSize++
return true
}
return false
}

private fun isInPool(instance: T): Boolean {
for (i in 0 until mPoolSize) {
if (mPool[i] === instance) {
return true
}
}
return false
}

}


}

0 comments on commit 7877a37

Please sign in to comment.