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

Commit

Permalink
1 SVGAParser.kt 添加缓存SVGA文件到本地的扩展[SVGACache.kt]
Browse files Browse the repository at this point in the history
2 SVGADrawable.kt SVGAImageView.kt SVGAVideoEntity.kt 公开clear()方法.
3 SVGAImageView.kt 添加clearsAfterDetached,用于控制imageview在onDetach的时候是否要自动调用clear().
  暂时为需要给RecyclerView缓存drawable或者entity的人士提供临时解决方案.用完记得调用clear()!!!!!!!
4 SVGAVideoEntity.kt generateAudioFileMap 方法每次打开同一个文件会解压一次mp3.缓存文件夹会越来越大.改为固定名字.
5 SVGAVideoEntity.kt 里面soundPool如果解析时load同一个svga的声音文件会出现无回调的情况,导致这里的callback不执行,
  原因暂时未知.目前解决方案是公开imageview,drawable,entity的clear(),然后在播放带声音的svgaimageview处,
  把解析完的drawable或者entity缓存下来,下次直接播放.用完再调用clear()!!!!!!!没有声音的不存在此问题.
  (真实场景如下:持续以队列形式播放同一个带声音的svga,由于2.5.8自动clear(),且clear()相关方法没有公开,
  只能一直重新decodeFromURL同一个远程svga文件,会导致onComplete与onError不回调)
  • Loading branch information
xtim.cc committed Aug 26, 2020
1 parent 84fcf55 commit b843ee3
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 62 deletions.
16 changes: 13 additions & 3 deletions library/src/main/java/com/opensource/svgaplayer/SVGACache.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,21 @@ object SVGACache {
this.type = type
}

// fun clearCache(context: Context?){
// context ?: return
// cacheDir = "${context.cacheDir.absolutePath}/svga/"
// File(cacheDir).takeIf { it.exists() }?.delete()
// }

fun isInitialized(): Boolean {
return "/" != cacheDir
}

fun isDefaultCache(): Boolean = type == Type.DEFAULT

fun isCached(cacheKey: String): Boolean {
return (if (isDefaultCache()) buildCacheDir(cacheKey) else buildCacheFile(
cacheKey
return (if (isDefaultCache()) buildCacheDir(cacheKey) else buildSvgaFile(
cacheKey
)).exists()
}

Expand All @@ -56,8 +62,12 @@ object SVGACache {
return File("$cacheDir$cacheKey/")
}

fun buildCacheFile(cacheKey: String): File {
fun buildSvgaFile(cacheKey: String): File {
return File("$cacheDir$cacheKey.svga")
}

fun buildAudioFile(audio: String): File {
return File("$cacheDir$audio.mp3")
}

}
130 changes: 78 additions & 52 deletions library/src/main/java/com/opensource/svgaplayer/SVGAParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import org.json.JSONObject
import java.io.*
import java.net.HttpURLConnection
import java.net.URL
import java.security.MessageDigest
import java.util.concurrent.Executors
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.atomic.AtomicInteger
Expand Down Expand Up @@ -108,7 +107,7 @@ class SVGAParser(context: Context?) {
private var mShareParser = SVGAParser(null)

internal var threadPoolExecutor = Executors.newCachedThreadPool { r ->
Thread(r, "SVGAParser-Thread-${threadNum.getAndIncrement()}")
Thread(r, "SVGAParser-Thread-${threadNum.getAndIncrement()}")
}

fun setThreadPoolExecutor(executor: ThreadPoolExecutor) {
Expand Down Expand Up @@ -178,63 +177,90 @@ class SVGAParser(context: Context?) {
}
}

/**
* 读取解析本地缓存的svga文件.
*/
fun _decodeFromCacheKey(cacheKey: String, callback: ParseCompletion?) {
val svga = SVGACache.buildCacheFile(cacheKey)
val svga = SVGACache.buildSvgaFile(cacheKey)
try {
LogUtils.info(TAG, "binary change to entity")
FileInputStream(svga).use {
LogUtils.info(TAG, "binary change to entity success")
this.invokeCompleteCallback(
SVGAVideoEntity(
MovieEntity.ADAPTER.decode(it),
SVGACache.buildCacheDir(cacheKey),
mFrameWidth,
mFrameHeight
), callback
)
LogUtils.info(TAG, "cache.binary change to entity")
FileInputStream(svga).use { inputStream ->
try {
readAsBytes(inputStream)?.let { bytes ->
LogUtils.info(TAG, "cache.inflate start")
inflate(bytes)?.let { inflateBytes ->
LogUtils.info(TAG, "cache.inflate success")
val videoItem = SVGAVideoEntity(
MovieEntity.ADAPTER.decode(inflateBytes),
File(cacheKey),
mFrameWidth,
mFrameHeight
)
videoItem.prepare {
LogUtils.info(TAG, "cache.prepare success")
this.invokeCompleteCallback(videoItem, callback)
}
} ?: doError("cache.inflate(bytes) cause exception", callback)
} ?: doError("cache.readAsBytes(inputStream) cause exception", callback)
} catch (e: Exception) {
this.invokeErrorCallback(e, callback)
} finally {
inputStream.close()
}
}
} catch (e: Exception) {
LogUtils.error(TAG, "binary change to entity fail", e)
SVGACache.buildCacheDir(cacheKey).delete()
svga.delete()
LogUtils.error(TAG, "cache.binary change to entity fail", e)
svga.takeIf { it.exists() }?.delete()
this.invokeErrorCallback(e, callback)
}
}

fun doError(error: String, callback: ParseCompletion?) {
LogUtils.info(TAG, error)
this.invokeErrorCallback(
Exception(error),
callback
)
}

/**
* 读取解析来自URL的svga文件.并缓存成本地文件
*/
fun _decodeFromInputStream(
inputStream: InputStream,
cacheKey: String,
callback: ParseCompletion?
inputStream: InputStream,
cacheKey: String,
callback: ParseCompletion?
) {
threadPoolExecutor.execute {
try {
LogUtils.info(TAG, "Input.binary change to entity")
readAsBytes(inputStream)?.let { bytes ->
LogUtils.info(TAG, "decode from input stream, inflate start")
inflate(bytes)?.let { inflateBytes ->
threadPoolExecutor.execute {
SVGACache.buildCacheFile(cacheKey).let { cacheFile ->
cacheFile.takeIf { !it.exists() }?.createNewFile()
FileOutputStream(cacheFile).write(inflateBytes)
}
threadPoolExecutor.execute {
SVGACache.buildSvgaFile(cacheKey).let { cacheFile ->
cacheFile.takeIf { !it.exists() }?.createNewFile()
FileOutputStream(cacheFile).write(bytes)
}
}
LogUtils.info(TAG, "Input.inflate start")
inflate(bytes)?.let { inflateBytes ->
LogUtils.info(TAG, "Input.inflate success")
val videoItem = SVGAVideoEntity(
MovieEntity.ADAPTER.decode(inflateBytes),
File(cacheKey),
mFrameWidth,
mFrameHeight
MovieEntity.ADAPTER.decode(inflateBytes),
File(cacheKey),
mFrameWidth,
mFrameHeight
)
// 里面soundPool如果解析时load同一个svga的声音文件会出现无回调的情况,导致这里的callback不执行,
// 原因暂时未知.目前解决方案是公开imageview,drawable,entity的clear(),然后在播放带声音
// 的svgaimageview处,把解析完的drawable或者entity缓存下来,下次直接播放.用完再调用clear()
// 在ImageView添加clearsAfterDetached,用于控制imageview在onDetach的时候是否要自动调用clear.
// 以暂时缓解需要为RecyclerView缓存drawable或者entity的人士.用完记得调用clear()
videoItem.prepare {
LogUtils.info(TAG, "decode from input stream, inflate end")
LogUtils.info(TAG, "Input.prepare success")
this.invokeCompleteCallback(videoItem, callback)
}
} ?: this.invokeErrorCallback(
Exception("inflate(bytes) cause exception"),
callback
)
} ?: this.invokeErrorCallback(
Exception("readAsBytes(inputStream) cause exception"),
callback
)
} ?: doError("Input.inflate(bytes) cause exception", callback)
} ?: doError("Input.readAsBytes(inputStream) cause exception", callback)
} catch (e: Exception) {
this.invokeErrorCallback(e, callback)
} finally {
Expand All @@ -244,10 +270,10 @@ class SVGAParser(context: Context?) {
}

fun decodeFromInputStream(
inputStream: InputStream,
cacheKey: String,
callback: ParseCompletion?,
closeInputStream: Boolean = false
inputStream: InputStream,
cacheKey: String,
callback: ParseCompletion?,
closeInputStream: Boolean = false
) {
if (mContext == null) {
LogUtils.error(TAG, "在配置 SVGAParser context 前, 无法解析 SVGA 文件。")
Expand Down Expand Up @@ -277,24 +303,24 @@ class SVGAParser(context: Context?) {
LogUtils.info(TAG, "decode from input stream, inflate start")
inflate(bytes)?.let {
val videoItem = SVGAVideoEntity(
MovieEntity.ADAPTER.decode(it),
File(cacheKey),
mFrameWidth,
mFrameHeight
MovieEntity.ADAPTER.decode(it),
File(cacheKey),
mFrameWidth,
mFrameHeight
)
videoItem.prepare {
LogUtils.info(TAG, "decode from input stream, inflate end")
this.invokeCompleteCallback(videoItem, callback)
}

} ?: this.invokeErrorCallback(
Exception("inflate(bytes) cause exception"),
callback
Exception("inflate(bytes) cause exception"),
callback
)
}
} ?: this.invokeErrorCallback(
Exception("readAsBytes(inputStream) cause exception"),
callback
Exception("readAsBytes(inputStream) cause exception"),
callback
)
} catch (e: java.lang.Exception) {
this.invokeErrorCallback(e, callback)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,12 +200,12 @@ class SVGAVideoEntity {
// 除数不能为 0
return item
}
audiosFileMap[audio.audioKey]?.let {
val fis = FileInputStream(it)
val length = fis.available().toDouble()
val offset = ((startTime / totalTime) * length).toLong()
item.soundID = soundPool?.load(fis.fd, offset, length.toLong(), 1)
fis.close()
audiosFileMap[audio.audioKey]?.let { file ->
FileInputStream(file).use {
val length = it.available().toDouble()
val offset = ((startTime / totalTime) * length).toLong()
item.soundID = soundPool?.load(it.fd, offset, length.toLong(), 1)
}
}
return item
}
Expand All @@ -221,7 +221,7 @@ class SVGAVideoEntity {
val audiosFileMap = HashMap<String, File>()
if (audiosDataMap.count() > 0) {
audiosDataMap.forEach {
val audioCache = File(it.key + ".mp3")
val audioCache = SVGACache.buildAudioFile(it.key)
audiosFileMap[it.key] =
audioCache.takeIf { file -> file.exists() } ?: generateAudioFile(
audioCache,
Expand Down

0 comments on commit b843ee3

Please sign in to comment.