添加了清除缓存,存在BUG

This commit is contained in:
2025-08-20 21:23:22 +08:00
parent 96e3c86cd4
commit ae088a1a7a
93 changed files with 283918 additions and 281546 deletions

View File

@@ -10,6 +10,7 @@ import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
import android.util.LruCache;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.View;
@@ -19,6 +20,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import com.blankj.utilcode.util.LogUtils;
import com.blankj.utilcode.util.PathUtils;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaItem;
@@ -39,6 +41,7 @@ import com.qxcm.moduleutil.R;
import com.qxcm.moduleutil.databinding.RoomViewSvgaAnimBinding;
import com.qxcm.moduleutil.utils.DownloadUtil;
import com.qxcm.moduleutil.utils.Md5Utils;
import com.qxcm.moduleutil.utils.MemoryOptimizationUtils;
import com.qxcm.moduleutil.utils.SpUtil;
import com.qxcm.moduleutil.utils.logger.Logger;
import com.tencent.qgame.animplayer.AnimConfig;
@@ -46,13 +49,15 @@ import com.tencent.qgame.animplayer.inter.IAnimListener;
import com.tencent.qgame.animplayer.inter.IFetchResource;
import java.io.File;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
public class AvatarFrameView extends FrameLayout implements IAnimListener {
@Override
public void onFailed(int i, @Nullable String s) {
@@ -75,15 +80,37 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
@Override
public void onVideoComplete() {
// if (mType == 1) {
// mBinding.playView.startPlay(mFile); // 循环播放
// } else {
// isPlaying = false;
// playNextFromQueue(); // 播放下一项
// }
if (isDestroyed) return;
// 确保在主线程中执行
if (Looper.myLooper() == Looper.getMainLooper()) {
handleVideoComplete();
} else {
mainHandler.post(() -> {
if (!isDestroyed) {
handleVideoComplete();
}
});
}
}
private void handleVideoComplete() {
if (isDestroyed) return;
if (mType == 1) {
mBinding.playView.startPlay(mFile); // 循环播放
if (mBinding != null && mFile != null) {
mBinding.playView.startPlay(mFile); // 循环播放
}
} else {
isPlaying = false;
playNextFromQueue(); // 播放下一项
}
}
@Override
public void onVideoDestroy() {
@@ -101,11 +128,19 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
private int mType;//1:循环播放 2:播放一次停止播放
private File mFile;
private final Queue<PlayItem> playQueue = new LinkedList<>();
private boolean isPlaying = false;
// 添加销毁标记
private boolean isDestroyed = false;
private static final String TAG = "AvatarFrameView";
private RoomViewSvgaAnimBinding mBinding;
// 内存监控
private static final long MAX_MEMORY_THRESHOLD = 300 * 1024 * 1024; // 300MB
private static final int MAX_SVGA_CACHE_SIZE = 3;
private final Map<String, WeakReference<SVGAVideoEntity>> svgaCache = new LruCache<>(MAX_SVGA_CACHE_SIZE);
public AvatarFrameView(@NonNull Context context) {
this(context, null);
}
@@ -121,6 +156,7 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
}
private void initViews() {
if (isDestroyed) return;
// 初始化 ExoPlayer View
playerView = new PlayerView(getContext());
playerView.setUseController(false);
@@ -142,10 +178,18 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
// addView(glSurfaceView);
// 初始化 ExoPlayer
exoPlayer = new ExoPlayer.Builder(getContext()).build();
playerView.setPlayer(exoPlayer);
if (!isDestroyed) {
try {
exoPlayer = new ExoPlayer.Builder(getContext()).build();
playerView.setPlayer(exoPlayer);
} catch (Exception e) {
LogUtils.e("AvatarFrameView", "Failed to initialize ExoPlayer: " + e.getMessage());
}
}
mBinding.playView.setAnimListener(this);
if (mBinding != null) {
mBinding.playView.setAnimListener(this);
}
}
private String getFileExtension(String url) {
if (url == null || url.isEmpty()) return "";
@@ -156,7 +200,18 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
return "";
}
private void playNextFromQueue() {
if (isDestroyed) return;
// 确保在主线程中执行
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(this::playNextFromQueue);
return;
}
// 再次检查内存状态
if (isMemoryLow()) {
LogUtils.w(TAG, "Low memory, clearing queue");
clearQueue();
return;
}
// 检查特效是否开启
if (SpUtil.getOpenEffect() != 1) {
clearQueue();
@@ -202,6 +257,18 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
}
}
public void setSource(String url, int type2) {
if (isDestroyed) return;
// 确保在主线程中执行
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(() -> setSource(url, type2));
return;
}
// 检查内存状态
if (isMemoryLow()) {
LogUtils.w(TAG, "Low memory, skipping animation");
clearQueue();
return;
}
// 检查特效是否开启
if (SpUtil.getOpenEffect() != 1) {
// 特效关闭时清空队列并停止播放
@@ -219,7 +286,15 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
playNextFromQueue();
}
}
private boolean isMemoryLow() {
Runtime runtime = Runtime.getRuntime();
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
long maxMemory = runtime.maxMemory();
double memoryUsage = (double) usedMemory / maxMemory;
// 内存使用超过80%或绝对使用量超过阈值
return memoryUsage > 0.8 || usedMemory > MAX_MEMORY_THRESHOLD;
}
// public void setSource(String url, int type2) {
// if (SpUtil.getOpenEffect()==1) {
@@ -323,7 +398,157 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
}
}
private void handleSVGAComplete(SVGAVideoEntity videoItem, String url) {
if (isDestroyed || svgaSurface == null) return;
try {
// 缓存实体(使用弱引用)
svgaCache.put(url, new WeakReference<>(videoItem));
SVGADrawable drawable = new SVGADrawable(videoItem, new SVGADynamicEntity());
svgaSurface.setImageDrawable(drawable);
svgaSurface.setCallback(new SVGACallback() {
@Override
public void onStep(int i, double v) {
}
@Override
public void onRepeat() {
}
@Override
public void onPause() {
}
@Override
public void onFinished() {
if (isDestroyed) return;
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(() -> {
isPlaying = false;
playNextFromQueue();
});
} else {
isPlaying = false;
playNextFromQueue();
}
}
});
svgaSurface.startAnimation();
} catch (Exception e) {
LogUtils.e(TAG, "Error handling SVGA completion: " + e.getMessage());
isPlaying = false;
playNextFromQueue();
}
}
private void playCachedSVGA(SVGAVideoEntity videoItem) {
if (isDestroyed || svgaSurface == null) return;
try {
SVGADrawable drawable = new SVGADrawable(videoItem, new SVGADynamicEntity());
svgaSurface.setImageDrawable(drawable);
svgaSurface.setCallback(new SVGACallback() {
@Override
public void onStep(int i, double v) {
}
@Override
public void onRepeat() {
}
@Override
public void onPause() {
}
@Override
public void onFinished() {
if (isDestroyed) return;
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(() -> {
isPlaying = false;
playNextFromQueue();
});
} else {
isPlaying = false;
playNextFromQueue();
}
}
});
svgaSurface.startAnimation();
} catch (Exception e) {
LogUtils.e(TAG, "Error playing cached SVGA: " + e.getMessage());
isPlaying = false;
playNextFromQueue();
}
}
private void loadNewSVGA(String url) {
if (isDestroyed) return;
try {
new SVGAParser(getContext()).parse(new URL(url), new SVGAParser.ParseCompletion() {
@Override
public void onComplete(SVGAVideoEntity videoItem) {
if (isDestroyed) return;
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(() -> handleSVGAComplete(videoItem, url));
} else {
handleSVGAComplete(videoItem, url);
}
}
@Override
public void onError() {
if (isDestroyed) return;
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(() -> {
isPlaying = false;
playNextFromQueue();
});
} else {
isPlaying = false;
playNextFromQueue();
}
}
});
} catch (Exception e) {
LogUtils.e(TAG, "Error parsing SVGA: " + e.getMessage());
isPlaying = false;
playNextFromQueue();
}
}
private void loadSVGA(String url) {
if (isDestroyed || svgaSurface == null) return;
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(() -> loadSVGA(url));
return;
}
try {
svgaSurface.setVisibility(View.VISIBLE);
// 检查缓存
WeakReference<SVGAVideoEntity> cachedRef = svgaCache.get(url);
SVGAVideoEntity cachedEntity = cachedRef != null ? cachedRef.get() : null;
if (cachedEntity != null) {
// 使用缓存的实体
playCachedSVGA(cachedEntity);
} else {
// 加载新的SVGA
loadNewSVGA(url);
}
} catch (Exception e) {
LogUtils.e(TAG, "Error loading SVGA: " + e.getMessage());
isPlaying = false;
playNextFromQueue();
}
svgaSurface.setVisibility(View.VISIBLE);
try {
new SVGAParser(getContext()).parse(new URL(url), new SVGAParser.ParseCompletion() {
@@ -406,38 +631,201 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
}
private void clearPrevious() {
// if (exoPlayer != null) {
// exoPlayer.stop();
// exoPlayer.clearVideoSurface();
// }
if (svgaSurface.getDrawable() instanceof SVGADrawable) {
((SVGADrawable) svgaSurface.getDrawable()).stop();
if (isDestroyed) return;
// 确保在主线程中执行
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(() -> {
if (!isDestroyed) {
clearPrevious();
}
});
return;
}
try {
// 停止并清理 ExoPlayer
if (exoPlayer != null) {
try {
exoPlayer.stop(); // 这里可能会在错误线程中调用
exoPlayer.clearVideoSurface();
} catch (Exception e) {
Logger.e("Error stopping ExoPlayer: " + e.getMessage());
// 如果在错误线程中,切换到主线程重试
mainHandler.post(() -> {
try {
if (exoPlayer != null) {
exoPlayer.stop();
exoPlayer.clearVideoSurface();
}
} catch (Exception ex) {
Logger.e("Error stopping ExoPlayer on main thread: " + ex.getMessage());
}
});
}
}
// 停止并清理 SVGA 动画
if (svgaSurface != null && svgaSurface.getDrawable() instanceof SVGADrawable) {
SVGADrawable drawable = (SVGADrawable) svgaSurface.getDrawable();
if (drawable != null) {
drawable.stop();
// 清理 SVGADrawable 中的资源
svgaSurface.clearAnimation();
svgaSurface.setImageDrawable(null);
}
}
// 隐藏所有视图
if (playerView != null) playerView.setVisibility(View.GONE);
if (svgaSurface != null) svgaSurface.setVisibility(View.GONE);
mBinding.playView.setVisibility(View.GONE);
// 停止播放器
if (mBinding.playView != null) {
mBinding.playView.stopPlay();
}
} catch (Exception e) {
LogUtils.e(TAG, "Error in clearPrevious: " + e.getMessage());
}
// if (svgaSurface.getDrawable() instanceof SVGADrawable) {
// ((SVGADrawable) svgaSurface.getDrawable()).stop();
// }
// if (playerView != null) playerView.setVisibility(View.GONE);
// if (svgaSurface != null) svgaSurface.setVisibility(View.GONE);
// if (glSurfaceView != null) glSurfaceView.setVisibility(View.GONE);
// mBinding.playView.setVisibility(View.GONE);
}
// 简单的 LRU Cache 实现
private static class LruCache<K, V> extends LinkedHashMap<K, V> {
private final int maxSize;
public void release() {
// if (exoPlayer != null) {
// exoPlayer.release();
// }
if (svgaSurface.getDrawable() instanceof SVGADrawable) {
((SVGADrawable) svgaSurface.getDrawable()).stop();
public LruCache(int maxSize) {
super(16, 0.75f, true);
this.maxSize = maxSize;
}
if (glSurfaceView != null) {
glSurfaceView.onPause(); // 必须调用生命周期方法
// glSurfaceView.requestRender();
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxSize;
}
}
/**
* 释放所有资源
*/
private void releaseResources() {
LogUtils.d(TAG, "Releasing all resources");
if (isDestroyed) return;
try {
// 清理 SVGA 资源
if (svgaSurface != null && svgaSurface.getDrawable() instanceof SVGADrawable) {
SVGADrawable drawable = (SVGADrawable) svgaSurface.getDrawable();
if (drawable != null) {
try {
drawable.stop();
svgaSurface.clearAnimation();
svgaSurface.setImageDrawable(null);
} catch (Exception e) {
LogUtils.e(TAG, "Error releasing SVGA resources: " + e.getMessage());
}
}
}
// 停止并清理播放器
if (mBinding != null && mBinding.playView != null) {
mBinding.playView.stopPlay();
}
// 清理 ExoPlayer 资源
if (exoPlayer != null) {
try {
exoPlayer.stop();
exoPlayer.clearVideoSurface();
} catch (Exception e) {
LogUtils.e(TAG, "Error releasing ExoPlayer resources: " + e.getMessage());
}
}
} catch (Exception e) {
LogUtils.e(TAG, "Error in releaseResources: " + e.getMessage());
}
}
/**
* 公共释放方法,用于外部主动释放资源
*/
public void release() {
Logger.d("AvatarFrameView", "Public release called");
if (isDestroyed) return;
// 确保在主线程中执行
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(this::release);
return;
}
isDestroyed = true;
try {
// 清空播放队列
clearQueue();
// 释放所有资源
releaseResources();
// 释放 ExoPlayer
if (exoPlayer != null) {
try {
exoPlayer.stop();
exoPlayer.release();
} catch (Exception e) {
LogUtils.e(TAG, "Error releasing ExoPlayer: " + e.getMessage());
}
exoPlayer = null;
}
// 清理 PlayerView
if (playerView != null) {
try {
playerView.setPlayer(null);
} catch (Exception e) {
LogUtils.e(TAG, "Error releasing PlayerView: " + e.getMessage());
}
playerView = null;
}
// 清理 SVGAImageView
if (svgaSurface != null) {
try {
svgaSurface.pauseAnimation();
svgaSurface.clearAnimation();
svgaSurface.setImageDrawable(null);
} catch (Exception e) {
LogUtils.e(TAG, "Error releasing SVGAImageView: " + e.getMessage());
}
svgaSurface = null;
}
// 清理 binding
if (mBinding != null) {
mBinding = null;
}
// 清理队列
playQueue.clear();
} catch (Exception e) {
LogUtils.e(TAG, "Error in AvatarFrameView release: " + e.getMessage());
} finally {
// 建议进行垃圾回收
MemoryOptimizationUtils.forceGC();
}
}
public void clearQueue() {
if (isDestroyed) return;
playQueue.clear();
isPlaying = false;
// 清理当前正在播放的内容
clearPrevious();
}
@@ -454,8 +842,13 @@ public class AvatarFrameView extends FrameLayout implements IAnimListener {
* 关闭特效
*/
public void closeEffect() {
// 清空队列
clearQueue();
// 释放资源
releaseResources();
//清空队列
playQueue.clear();
// playQueue.clear();
//关闭动画
// if (mBinding.image != null && isPlaying && mBinding.image.isAnimating()) {
// mBinding.image.setAnimation(null);