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

Commit 84fcf55

Browse files
author
linjp
committed
* 1 SVGAParser.kt 添加缓存SVGA文件到本地的扩展
2 SVGADrawable.kt SVGAImageView.kt SVGAVideoEntity.kt 公开clear()方法. 3 SVGAVideoEntity.kt generateAudioFileMap 方法每次打开同一个文件会解压一次mp3.缓存文件会越来越大.改为固定名字.
1 parent 88d7296 commit 84fcf55

File tree

7 files changed

+219
-48
lines changed

7 files changed

+219
-48
lines changed

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
// Top-level build file where you can add configuration options common to all sub-projects/modules.
22

33
buildscript {
4-
ext.kotlin_version = '1.3.11'
4+
ext.kotlin_version = '1.3.50'
55
repositories {
66
google()
77
jcenter()
88
}
99
dependencies {
10-
classpath 'com.android.tools.build:gradle:3.3.0'
10+
classpath 'com.android.tools.build:gradle:4.0.0'
1111
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1212

1313
// NOTE: Do not place your application dependencies here; they belong

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists
6-
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
6+
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package com.opensource.svgaplayer
2+
3+
import android.content.Context
4+
import java.io.File
5+
import java.net.URL
6+
import java.security.MessageDigest
7+
8+
9+
object SVGACache {
10+
enum class Type {
11+
DEFAULT,
12+
FILE
13+
}
14+
15+
private var type: Type = Type.DEFAULT
16+
private var cacheDir: String = "/"
17+
18+
fun onCreate(context: Context?) {
19+
onCreate(context, Type.DEFAULT)
20+
}
21+
22+
fun onCreate(context: Context?, type: Type) {
23+
if (isInitialized()) return
24+
context ?: return
25+
cacheDir = "${context.cacheDir.absolutePath}/svga/"
26+
File(cacheDir).takeIf { !it.exists() }?.mkdir()
27+
this.type = type
28+
}
29+
30+
fun isInitialized(): Boolean {
31+
return "/" != cacheDir
32+
}
33+
34+
fun isDefaultCache(): Boolean = type == Type.DEFAULT
35+
36+
fun isCached(cacheKey: String): Boolean {
37+
return (if (isDefaultCache()) buildCacheDir(cacheKey) else buildCacheFile(
38+
cacheKey
39+
)).exists()
40+
}
41+
42+
fun buildCacheKey(str: String): String {
43+
val messageDigest = MessageDigest.getInstance("MD5")
44+
messageDigest.update(str.toByteArray(charset("UTF-8")))
45+
val digest = messageDigest.digest()
46+
var sb = ""
47+
for (b in digest) {
48+
sb += String.format("%02x", b)
49+
}
50+
return sb
51+
}
52+
53+
fun buildCacheKey(url: URL): String = buildCacheKey(url.toString())
54+
55+
fun buildCacheDir(cacheKey: String): File {
56+
return File("$cacheDir$cacheKey/")
57+
}
58+
59+
fun buildCacheFile(cacheKey: String): File {
60+
return File("$cacheDir$cacheKey.svga")
61+
}
62+
63+
}

library/src/main/java/com/opensource/svgaplayer/SVGADrawable.kt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,31 @@ class SVGADrawable(val videoItem: SVGAVideoEntity, val dynamicItem: SVGADynamicE
5454

5555
}
5656

57-
internal fun clear() {
57+
fun resume() {
58+
videoItem.audioList.forEach { audio ->
59+
audio.playID?.let {
60+
videoItem.soundPool?.resume(it)
61+
}
62+
}
63+
}
64+
65+
fun pause() {
66+
videoItem.audioList.forEach { audio ->
67+
audio.playID?.let {
68+
videoItem.soundPool?.pause(it)
69+
}
70+
}
71+
}
72+
73+
fun stop() {
74+
videoItem.audioList.forEach { audio ->
75+
audio.playID?.let {
76+
videoItem.soundPool?.stop(it)
77+
}
78+
}
79+
}
80+
81+
fun clear() {
5882
videoItem.audioList.forEach { audio ->
5983
audio.playID?.let {
6084
videoItem.soundPool?.stop(it)

library/src/main/java/com/opensource/svgaplayer/SVGAImageView.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ open class SVGAImageView @JvmOverloads constructor(context: Context, attrs: Attr
3434

3535
var loops = 0
3636
var clearsAfterStop = true
37+
var clearsAfterDetached = true
3738
var fillMode: FillMode = FillMode.Forward
3839
var callback: SVGACallback? = null
3940

@@ -187,7 +188,7 @@ open class SVGAImageView @JvmOverloads constructor(context: Context, attrs: Attr
187188
callback?.onFinished()
188189
}
189190

190-
private fun clear() {
191+
fun clear() {
191192
getSVGADrawable()?.cleared = true
192193
getSVGADrawable()?.clear()
193194
// 清除对 drawable 的引用
@@ -270,7 +271,9 @@ open class SVGAImageView @JvmOverloads constructor(context: Context, attrs: Attr
270271
override fun onDetachedFromWindow() {
271272
super.onDetachedFromWindow()
272273
stopAnimation(true)
273-
clear()
274+
if (clearsAfterDetached) {
275+
clear()
276+
}
274277
}
275278

276279
private class AnimatorListener(view: SVGAImageView) : Animator.AnimatorListener {

library/src/main/java/com/opensource/svgaplayer/SVGAParser.kt

Lines changed: 110 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ private var isUnzipping = false
2727
class SVGAParser(context: Context?) {
2828
private var mContext = context?.applicationContext
2929

30+
init {
31+
SVGACache.onCreate(context)
32+
}
33+
3034
@Volatile
3135
private var mFrameWidth: Int = 0
3236

@@ -118,6 +122,7 @@ class SVGAParser(context: Context?) {
118122

119123
fun init(context: Context) {
120124
mContext = context.applicationContext
125+
SVGACache.onCreate(mContext)
121126
}
122127

123128
fun setFrameSize(frameWidth: Int, frameHeight: Int) {
@@ -134,7 +139,7 @@ class SVGAParser(context: Context?) {
134139
LogUtils.info(TAG, "================ decode from assets ================")
135140
threadPoolExecutor.execute {
136141
mContext?.assets?.open(name)?.let {
137-
this.decodeFromInputStream(it, buildCacheKey("file:///assets/$name"), callback, true)
142+
this.decodeFromInputStream(it, SVGACache.buildCacheKey("file:///assets/$name"), callback, true)
138143
}
139144
}
140145
} catch (e: java.lang.Exception) {
@@ -148,24 +153,102 @@ class SVGAParser(context: Context?) {
148153
return null
149154
}
150155
LogUtils.info(TAG, "================ decode from url ================")
151-
return if (this.isCached(buildCacheKey(url))) {
156+
val cacheKey = SVGACache.buildCacheKey(url);
157+
return if (SVGACache.isCached(cacheKey)) {
152158
LogUtils.info(TAG, "this url cached")
153159
threadPoolExecutor.execute {
154-
this.decodeFromCacheKey(buildCacheKey(url), callback)
160+
if (SVGACache.isDefaultCache()) {
161+
this.decodeFromCacheKey(cacheKey, callback)
162+
} else {
163+
this._decodeFromCacheKey(cacheKey, callback)
164+
}
155165
}
156-
null
157-
}
158-
else {
166+
return null
167+
} else {
159168
LogUtils.info(TAG, "no cached, prepare to download")
160169
fileDownloader.resume(url, {
161-
this.decodeFromInputStream(it, this.buildCacheKey(url), callback)
170+
if (SVGACache.isDefaultCache()) {
171+
this.decodeFromInputStream(it, cacheKey, callback)
172+
} else {
173+
this._decodeFromInputStream(it, cacheKey, callback)
174+
}
162175
}, {
163176
this.invokeErrorCallback(it, callback)
164177
})
165178
}
166179
}
167180

168-
fun decodeFromInputStream(inputStream: InputStream, cacheKey: String, callback: ParseCompletion?, closeInputStream: Boolean = false) {
181+
fun _decodeFromCacheKey(cacheKey: String, callback: ParseCompletion?) {
182+
val svga = SVGACache.buildCacheFile(cacheKey)
183+
try {
184+
LogUtils.info(TAG, "binary change to entity")
185+
FileInputStream(svga).use {
186+
LogUtils.info(TAG, "binary change to entity success")
187+
this.invokeCompleteCallback(
188+
SVGAVideoEntity(
189+
MovieEntity.ADAPTER.decode(it),
190+
SVGACache.buildCacheDir(cacheKey),
191+
mFrameWidth,
192+
mFrameHeight
193+
), callback
194+
)
195+
}
196+
} catch (e: Exception) {
197+
LogUtils.error(TAG, "binary change to entity fail", e)
198+
SVGACache.buildCacheDir(cacheKey).delete()
199+
svga.delete()
200+
this.invokeErrorCallback(e, callback)
201+
}
202+
}
203+
204+
fun _decodeFromInputStream(
205+
inputStream: InputStream,
206+
cacheKey: String,
207+
callback: ParseCompletion?
208+
) {
209+
threadPoolExecutor.execute {
210+
try {
211+
readAsBytes(inputStream)?.let { bytes ->
212+
LogUtils.info(TAG, "decode from input stream, inflate start")
213+
inflate(bytes)?.let { inflateBytes ->
214+
threadPoolExecutor.execute {
215+
SVGACache.buildCacheFile(cacheKey).let { cacheFile ->
216+
cacheFile.takeIf { !it.exists() }?.createNewFile()
217+
FileOutputStream(cacheFile).write(inflateBytes)
218+
}
219+
}
220+
val videoItem = SVGAVideoEntity(
221+
MovieEntity.ADAPTER.decode(inflateBytes),
222+
File(cacheKey),
223+
mFrameWidth,
224+
mFrameHeight
225+
)
226+
videoItem.prepare {
227+
LogUtils.info(TAG, "decode from input stream, inflate end")
228+
this.invokeCompleteCallback(videoItem, callback)
229+
}
230+
} ?: this.invokeErrorCallback(
231+
Exception("inflate(bytes) cause exception"),
232+
callback
233+
)
234+
} ?: this.invokeErrorCallback(
235+
Exception("readAsBytes(inputStream) cause exception"),
236+
callback
237+
)
238+
} catch (e: Exception) {
239+
this.invokeErrorCallback(e, callback)
240+
} finally {
241+
inputStream.close()
242+
}
243+
}
244+
}
245+
246+
fun decodeFromInputStream(
247+
inputStream: InputStream,
248+
cacheKey: String,
249+
callback: ParseCompletion?,
250+
closeInputStream: Boolean = false
251+
) {
169252
if (mContext == null) {
170253
LogUtils.error(TAG, "在配置 SVGAParser context 前, 无法解析 SVGA 文件。")
171254
return
@@ -176,9 +259,9 @@ class SVGAParser(context: Context?) {
176259
readAsBytes(inputStream)?.let { bytes ->
177260
if (bytes.size > 4 && bytes[0].toInt() == 80 && bytes[1].toInt() == 75 && bytes[2].toInt() == 3 && bytes[3].toInt() == 4) {
178261
LogUtils.info(TAG, "decode from zip file")
179-
if (!buildCacheDir(cacheKey).exists() || isUnzipping) {
262+
if (!SVGACache.buildCacheDir(cacheKey).exists() || isUnzipping) {
180263
synchronized(fileLock) {
181-
if (!buildCacheDir(cacheKey).exists()) {
264+
if (!SVGACache.buildCacheDir(cacheKey).exists()) {
182265
isUnzipping = true
183266
LogUtils.info(TAG, "no cached, prepare to unzip")
184267
ByteArrayInputStream(bytes).use {
@@ -193,14 +276,26 @@ class SVGAParser(context: Context?) {
193276
} else {
194277
LogUtils.info(TAG, "decode from input stream, inflate start")
195278
inflate(bytes)?.let {
196-
val videoItem = SVGAVideoEntity(MovieEntity.ADAPTER.decode(it), File(cacheKey), mFrameWidth, mFrameHeight)
279+
val videoItem = SVGAVideoEntity(
280+
MovieEntity.ADAPTER.decode(it),
281+
File(cacheKey),
282+
mFrameWidth,
283+
mFrameHeight
284+
)
197285
videoItem.prepare {
198286
LogUtils.info(TAG, "decode from input stream, inflate end")
199287
this.invokeCompleteCallback(videoItem, callback)
200288
}
201-
} ?: LogUtils.error(TAG, "inflate(bytes) cause exception")
289+
290+
} ?: this.invokeErrorCallback(
291+
Exception("inflate(bytes) cause exception"),
292+
callback
293+
)
202294
}
203-
} ?: LogUtils.error(TAG, "readAsBytes(inputStream) cause exception")
295+
} ?: this.invokeErrorCallback(
296+
Exception("readAsBytes(inputStream) cause exception"),
297+
callback
298+
)
204299
} catch (e: java.lang.Exception) {
205300
this.invokeErrorCallback(e, callback)
206301
} finally {
@@ -251,10 +346,6 @@ class SVGAParser(context: Context?) {
251346
}
252347
}
253348

254-
private fun isCached(cacheKey: String): Boolean {
255-
return buildCacheDir(cacheKey).exists()
256-
}
257-
258349
private fun decodeFromCacheKey(cacheKey: String, callback: ParseCompletion?) {
259350
LogUtils.info(TAG, "================ decode from cache ================")
260351
LogUtils.debug(TAG, "decodeFromCacheKey called with cacheKey : $cacheKey")
@@ -311,30 +402,14 @@ class SVGAParser(context: Context?) {
311402
}
312403
}
313404

314-
private fun buildCacheKey(str: String): String {
315-
val messageDigest = MessageDigest.getInstance("MD5")
316-
messageDigest.update(str.toByteArray(charset("UTF-8")))
317-
val digest = messageDigest.digest()
318-
var sb = ""
319-
for (b in digest) {
320-
sb += String.format("%02x", b)
321-
}
322-
return sb
323-
}
324-
325-
private fun buildCacheKey(url: URL): String = buildCacheKey(url.toString())
326-
327-
private fun buildCacheDir(cacheKey: String): File = File(mContext?.cacheDir?.absolutePath + "/" + cacheKey + "/")
328-
329405
private fun readAsBytes(inputStream: InputStream): ByteArray? {
330406
ByteArrayOutputStream().use { byteArrayOutputStream ->
331407
val byteArray = ByteArray(2048)
332408
while (true) {
333409
val count = inputStream.read(byteArray, 0, 2048)
334410
if (count <= 0) {
335411
break
336-
}
337-
else {
412+
} else {
338413
byteArrayOutputStream.write(byteArray, 0, count)
339414
}
340415
}
@@ -362,7 +437,7 @@ class SVGAParser(context: Context?) {
362437

363438
private fun unzip(inputStream: InputStream, cacheKey: String) {
364439
LogUtils.info(TAG, "================ unzip prepare ================")
365-
val cacheDir = this.buildCacheDir(cacheKey)
440+
val cacheDir = SVGACache.buildCacheDir(cacheKey)
366441
cacheDir.mkdirs()
367442
try {
368443
BufferedInputStream(inputStream).use {

0 commit comments

Comments
 (0)