fix: 尝试优化 banner + svga 的内存占用, 添加资源回收逻辑

This commit is contained in:
eggmanQQQ
2025-02-05 15:58:43 +08:00
parent ef3810a0d5
commit db2194fe02
9 changed files with 188 additions and 107 deletions

View File

@@ -59,9 +59,9 @@ public class RoomNormalListAdapter extends RecyclerView.Adapter<RoomNormalListAd
if (UserModel.get().getCacheLoginUserInfo() != null && UserModel.get().getCacheLoginUserInfo().isSuperAdmin()) {
OtherExtKt.setVis(holder.operationImg, true, false);
OtherExtKt.setVis(holder.operationImg, false, false);
} else {
OtherExtKt.setVis(holder.operationImg,false,false);
OtherExtKt.setVis(holder.operationImg,true,false);
}
}

View File

@@ -20,4 +20,7 @@ class HomeBannerAdapter : BaseBannerAdapter<BannerInfo?>() {
val effectView = helper.findViewById<EffectView>(R.id.effectView)
effectView.load(item?.bannerPic?:"",item?.fillVo?.imgMap,item?.fillVo?.textMap)
}
}

View File

@@ -55,23 +55,26 @@ object AnimLoadUtil {
}
fun isSvgaFile(path: String,callBack: (isSvga: Boolean,videoItem: SVGAVideoEntity?) -> Unit = {_,_->}){
val inputStream = BufferedInputStream(FileInputStream(path))
shareParser().decodeFromInputStream(
inputStream,
path,
object : SVGAParser.ParseCompletion {
override fun onComplete(videoItem: SVGAVideoEntity) {
callBack(true,videoItem)
}
try {
val inputStream = BufferedInputStream(FileInputStream(path))
shareParser().decodeFromInputStream(
inputStream,
path,
object : SVGAParser.ParseCompletion {
override fun onComplete(videoItem: SVGAVideoEntity) {
callBack(true,videoItem)
}
override fun onError() {
callBack(false,null)
}
},
true,
null,
null
)
override fun onError() {
callBack(false,null)
}
},
true,
null,
null
)
} catch (e: Exception) {
}
}
//垃圾 不顶用

View File

@@ -20,6 +20,7 @@ import java.io.File
object ResourceManager {
public var mClientResource : ClientResource? = null
private val needLog = false;
fun initResource() {
InitialModel.get().clientResource().doOnSuccess {
@@ -73,7 +74,7 @@ object ResourceManager {
private fun doLoad(url: String) {
if (url.isVerify()) {
"预加载 -> ResourceManager doLoad url = $url".doLog()
"预加载 -> ResourceManager doLoad url = $url".doLog(needLog)
GlideUtils.instance().downloadFromUrl2(AppUtils.getApp(),url,object : RequestListener<File?> {
override fun onLoadFailed(
e: GlideException?,
@@ -81,7 +82,7 @@ object ResourceManager {
target: Target<File?>?,
isFirstResource: Boolean
): Boolean {
"预加载 -> ResourceManager onLoadFailed error = ${e?.message} url = $url".doLog()
"预加载 -> ResourceManager onLoadFailed error = ${e?.message} url = $url".doLog(needLog)
return true
}
@@ -92,7 +93,7 @@ object ResourceManager {
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
"预加载 -> ResourceManager onResourceReady Success url = $url".doLog()
"预加载 -> ResourceManager onResourceReady Success url = $url".doLog(needLog)
return true
}
})

View File

@@ -13,6 +13,7 @@ import android.text.StaticLayout
import android.text.TextPaint
import android.text.TextUtils
import android.util.AttributeSet
import android.util.LruCache
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
@@ -37,6 +38,7 @@ import com.netease.nim.uikit.support.glide.GlideApp
import com.opensource.svgaplayer.SVGACallback
import com.opensource.svgaplayer.SVGADrawable
import com.opensource.svgaplayer.SVGADynamicEntity
import com.opensource.svgaplayer.SVGAImageView
import com.opensource.svgaplayer.SVGAParser
import com.opensource.svgaplayer.SVGAParser.Companion.shareParser
import com.opensource.svgaplayer.SVGAVideoEntity
@@ -60,6 +62,7 @@ class EffectView : FrameLayout {
var mCallBack : CallBack? = null
var hasAnimImg = false; // 图片是否可能带 序列帧动图
var mPlayerCount = 1;
private var mResourceUrl = ""
private var mEffectType = EffectType.IMG
@@ -71,6 +74,10 @@ class EffectView : FrameLayout {
private var mNeedLog = false
private var mImage : ImageView? = null
private var mSvga : SVGAView? = null
private var mVap : AnimView? = null
private fun init(context: Context) {
}
@@ -119,7 +126,7 @@ class EffectView : FrameLayout {
} else {
loadEffect(EffectType.MP4,resource)
}
}
}
}
}
} else {
@@ -153,14 +160,8 @@ class EffectView : FrameLayout {
private fun loadImg(resource: File) {
"EffectView loadImg() ".doLog(mNeedLog)
try {
val imageView = ImageView(context)
this@EffectView.addView(imageView)
imageView.setViewWH(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
false
)
imageView.scaleType = ImageView.ScaleType.FIT_XY
val imageView = createImageView()
mImage = imageView
Glide.with(this)
.asBitmap()
@@ -189,26 +190,8 @@ class EffectView : FrameLayout {
imageView?.post {
if (!imageView.isAttachedToWindow) return@post
resource?.let {
if (hasAnimImg) {
val split: List<Drawable> = split(it)
if (split.isVerify()) {
val animationDrawable = AnimationDrawable()
for (i in split.indices) {
animationDrawable.addFrame(split[i], 200)
}
imageView.setImageDrawable(animationDrawable)
animationDrawable.isOneShot = false
animationDrawable.start()
} else {
imageView.setImageBitmap(it)
}
"EffectView loadImg() start".doLog(mNeedLog)
playCallBack(true)
} else {
"EffectView loadImg() start".doLog(mNeedLog)
playCallBack(true)
imageView.setImageBitmap(it)
}
mCallBack?.onCache(resource)
imgResourceLoad(imageView,resource)
}
}
return true
@@ -221,15 +204,48 @@ class EffectView : FrameLayout {
}
}
private fun createImageView() : ImageView {
val imageView = ImageView(context)
this@EffectView.addView(imageView)
imageView.setViewWH(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
false
)
imageView.scaleType = ImageView.ScaleType.FIT_XY
return imageView
}
private fun imgResourceLoad(imageView: ImageView, resource: Bitmap) {
if (hasAnimImg) {
val split: List<Drawable> = split(resource)
if (split.isVerify()) {
val animationDrawable = AnimationDrawable()
for (i in split.indices) {
animationDrawable.addFrame(split[i], 200)
}
imageView.setImageDrawable(animationDrawable)
animationDrawable.isOneShot = false
animationDrawable.start()
} else {
imageView.setImageBitmap(resource)
}
"EffectView loadImg() start".doLog(mNeedLog)
playCallBack(true)
} else {
"EffectView loadImg() start".doLog(mNeedLog)
playCallBack(true)
imageView.setImageBitmap(resource)
}
}
private fun loadMp4(resource: File) {
"EffectView loadMp4() ".doLog(mNeedLog)
try {
val animView = AnimView(context)
mVap = animView
animView.setLoop(mPlayerCount)
this@EffectView.addView(animView)
animView.setViewWH(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT,false)
// AnimView.setLoop(Int.MAX_VALUE)
// AnimView.setScaleType(ScaleType.FIT_XY)
animView.setAnimListener(object : IAnimListener {
override fun onFailed(errorType: Int, errorMsg: String?) {
playCallBack(false)
@@ -326,6 +342,10 @@ class EffectView : FrameLayout {
resources?.forEach {
it?.bitmap?.recycle()
}
bitmapMap?.forEach { t, u ->
u?.recycle()
}
}
})
animView.startPlay(resource)
@@ -375,23 +395,15 @@ class EffectView : FrameLayout {
private fun loadSvga(resource: File) {
"EffectView loadSvga() ".doLog(mNeedLog)
try {
val svgaView = SVGAView(context)
this@EffectView.addView(svgaView)
svgaView.setViewWH(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT,false)
svgaView.scaleType = ImageView.ScaleType.FIT_XY
val svgaView = createSvgaView()
mSvga = svgaView
val inputStream = BufferedInputStream(FileInputStream(resource.path))
shareParser().decodeFromInputStream(
inputStream,
resource.path,
object : SVGAParser.ParseCompletion {
override fun onComplete(videoItem: SVGAVideoEntity) {
svgaView.post {
if (!svgaView.isAttachedToWindow) return@post
svgaView.setImageDrawable(SVGADrawable(videoItem))
svgaView.startAnimation()
playCallBack(true)
"EffectView loadSvga() true".doLog(mNeedLog)
}
playSvga(svgaView,videoItem)
}
override fun onError() {
@@ -419,27 +431,8 @@ class EffectView : FrameLayout {
return
}
val svgaView = SVGAView(context)
this@EffectView.addView(svgaView)
svgaView.setViewWH(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT,false)
svgaView.scaleType = ImageView.ScaleType.FIT_XY
svgaView.callback = object : SVGACallback {
override fun onPause() {
"EffectView loadSvga() SVGACallback onPause()".doLog(mNeedLog)
}
override fun onFinished() {
"EffectView loadSvga() SVGACallback onFinished()".doLog(mNeedLog)
}
override fun onRepeat() {
// "EffectView loadSvga() SVGACallback onRepeat()".doLog(mNeedLog)
}
override fun onStep(i: Int, v: Double) {
}
}
val svgaView = createSvgaView()
mSvga = svgaView
val dynamicEntity = SVGADynamicEntity()
if (mTextMap?.isNotEmpty() == true) {
@@ -498,14 +491,6 @@ class EffectView : FrameLayout {
}
}
private fun playCallBack(isSuccess: Boolean,type: Int? = -1) {
if (isSuccess) {
"EffectView playCallBack() 加载成功 url = $mResourceUrl".doLog(mNeedLog)
} else {
"EffectView playCallBack() 加载失败 url = $mResourceUrl".doLog(mNeedLog)
}
mCallBack?.onPlay(isSuccess,type)
}
private fun split(bitmap: Bitmap?): List<Drawable> {
"EffectView split() ".doLog(mNeedLog)
@@ -580,24 +565,108 @@ class EffectView : FrameLayout {
.submit()
}
private fun playSvga(svgaView:SVGAView,videoItem: SVGAVideoEntity,dynamicEntity: SVGADynamicEntity) {
val drawable = SVGADrawable(videoItem, dynamicEntity)
private fun createSvgaView() :SVGAView{
val svgaView = SVGAView(context)
this@EffectView.addView(svgaView)
svgaView.setViewWH(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT,false)
svgaView.scaleType = ImageView.ScaleType.FIT_XY
svgaView.loops = mPlayerCount
svgaView.fillMode = SVGAImageView.FillMode.Forward
svgaView.callback = object : SVGACallback {
override fun onPause() {
"EffectView loadSvga() SVGACallback onPause()".doLog(mNeedLog)
}
override fun onFinished() {
"EffectView loadSvga() SVGACallback onFinished()".doLog(mNeedLog)
}
override fun onRepeat() {
// "EffectView loadSvga() SVGACallback onRepeat()".doLog(mNeedLog)
}
override fun onStep(i: Int, v: Double) {
}
}
return svgaView
}
private fun playSvga(svgaView:SVGAView,videoItem: SVGAVideoEntity,dynamicEntity: SVGADynamicEntity?=null) {
val drawable = if (dynamicEntity != null) SVGADrawable(videoItem, dynamicEntity) else SVGADrawable(videoItem)
mCallBack?.onCache(drawable)
svgaView.post {
if (!svgaView.isAttachedToWindow) return@post
svgaView.setImageDrawable(drawable)
svgaView.stepToFrame(0, true)
playCallBack(true)
"EffectView loadSvga() start".doLog(mNeedLog)
"EffectView playSvga() start".doLog(mNeedLog)
}
}
public fun loadUrlForCache() {
}
public fun setTextView(textSize: Float) {
mTextSize = textSize
}
public interface CallBack{
fun onPlay(isSuccess: Boolean,type: Int? = -1)
private fun playCallBack(isSuccess: Boolean,type: Int? = -1) {
if (isSuccess) {
"EffectView playCallBack() 加载成功 url = $mResourceUrl".doLog(mNeedLog)
} else {
"EffectView playCallBack() 加载失败 url = $mResourceUrl".doLog(mNeedLog)
}
mCallBack?.onPlay(isSuccess,type)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
"EffectView 被销毁了 $mResourceUrl".doLog()
try {
mImage?.setImageBitmap(null)
mImage = null
mSvga?.pauseAnimation()
mSvga?.stopAnimation()
mSvga?.clearAnimation()
mSvga?.clear()
mSvga = null
mVap?.stopPlay()
mVap?.clearAnimation()
mVap=null
removeAllViews()
} catch (e: Exception) {
"EffectView 被销毁了,回收资源异常 ${e.message}".doLog()
}
}
public interface CallBack{
fun onPlay(isSuccess: Boolean,type: Int? = -1)
fun onCache(resource:Any){};
}
class EffectLruCache(maxSize: Int){
val mLruCache = LruCache<String, Any>(maxSize)
fun get(key: String): Any? {
return mLruCache.get(key)
}
fun put(key: String, entity: Any) {
mLruCache.put(key, entity)
}
fun clear() {
mLruCache.evictAll()
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -882,16 +882,21 @@ public class GiftModel extends BaseModel implements IGiftModel {
@Override
public void handleMessage(Message msg) {
GiftModel giftModel = mReference.get();
if (giftModel == null) return;
super.handleMessage(msg);
if (giftModel.giftQueue.size() > 0) {
CustomAttachment attachment = giftModel.giftQueue.remove(0);
if (attachment != null)
giftModel.parseChatRoomAttachment(attachment);
GiftModel giftModel = null;
try {
giftModel = mReference.get();
if (giftModel == null) return;
super.handleMessage(msg);
if (giftModel.giftQueue.size() > 0) {
CustomAttachment attachment = giftModel.giftQueue.remove(0);
if (attachment != null)
giftModel.parseChatRoomAttachment(attachment);
}
} catch (Exception e) {
OtherExtKt.doLog(" 礼物消息异常 : "+e.getMessage());
}
if (giftModel.giftQueue.size() > 0) {
if (giftModel != null && !giftModel.giftQueue.isEmpty()) {
sendEmptyMessageDelayed(0, 150);
}
}

View File

@@ -2944,7 +2944,7 @@ public final class IMNetEaseManager {
carAttachment.effect = carInfo.getEffect();
carAttachment.viewUrl = carInfo.getViewUrl();
carAttachment.otherViewType = carInfo.getOtherViewType();
OtherExtKt.doLog(" 座驾 , 发送 座驾消息 播放动画");
ChatRoomMessage message = ChatRoomMessageBuilder.createChatRoomCustomMessage(
String.valueOf(roomInfo.getRoomId()), carAttachment);
e.onSuccess(message);

View File

@@ -267,7 +267,7 @@ class SVGAView : SVGAImageView, ILog {
logD("SVGAView parseCompletion onComplete url:$url")
if (resourceUrl != url) {
return
}
}
svgaCache?.put(resourceUrl ?: "", videoItem)
this@SVGAView.post {
this@SVGAView.setImageDrawable(SVGADrawable(videoItem))