Files
yusheng-android/moduleUtil/src/main/java/com/xscm/moduleutil/widget/AvatarFrameView.java

1494 lines
53 KiB
Java
Raw Normal View History

2025-10-20 10:16:44 +08:00
package com.xscm.moduleutil.widget;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
2025-10-24 17:52:11 +08:00
import com.blankj.utilcode.util.LogUtils;
2025-10-20 10:16:44 +08:00
import com.liulishuo.okdownload.DownloadTask;
import com.liulishuo.okdownload.core.cause.EndCause;
import com.liulishuo.okdownload.core.cause.ResumeFailedCause;
import com.liulishuo.okdownload.core.listener.DownloadListener1;
import com.liulishuo.okdownload.core.listener.assist.Listener1Assist;
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.SVGAVideoEntity;
2025-10-24 17:52:11 +08:00
import com.tencent.qgame.animplayer.inter.IAnimListener;
2025-10-20 10:16:44 +08:00
import com.xscm.moduleutil.R;
import com.xscm.moduleutil.databinding.RoomViewSvgaAnimBinding;
import com.xscm.moduleutil.utils.SpUtil;
import com.xscm.moduleutil.utils.logger.Logger;
import java.io.File;
2025-10-24 17:52:11 +08:00
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
2025-10-20 10:16:44 +08:00
import java.net.URL;
2025-10-24 17:52:11 +08:00
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
2025-10-20 10:16:44 +08:00
import java.util.LinkedList;
2025-10-24 17:52:11 +08:00
import java.util.Map;
2025-10-20 10:16:44 +08:00
import java.util.Queue;
2025-10-24 17:52:11 +08:00
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
public class AvatarFrameView extends FrameLayout {
private PlaybackManager playbackManager;
2025-10-20 10:16:44 +08:00
private void handleVideoComplete() {
2025-10-24 17:52:11 +08:00
// if (isDestroyed) return;
2025-10-20 10:16:44 +08:00
if (mType == 1) {
if (mBinding != null && mFile != null) {
mBinding.playView.startPlay(mFile); // 循环播放
}
} else {
isPlaying = false;
2025-10-24 17:52:11 +08:00
// playNextFromQueue(); // 播放下一项
currentProcessingCount = Math.max(0, currentProcessingCount - 1);
// 内存检查和清理
if (playQueue.size() % 1 == 0) { // 每处理10个动画检查一次内存
performLightCleanup();
}
// 延迟处理下一个,避免过于频繁
mainHandler.postDelayed(() -> {
smartCheckAndStartPlayback();
}, PROCESSING_DELAY);
// mainHandler.postDelayed(this::playNextFromQueue, 50);
2025-10-20 10:16:44 +08:00
}
}
2025-10-24 17:52:11 +08:00
private void performLightCleanup() {
// 只清理不需要的缓存,保留正在使用的资源
Runtime runtime = Runtime.getRuntime();
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
long maxMemory = runtime.maxMemory();
double memoryUsage = (double) usedMemory / maxMemory;
// 内存使用超过70%时进行轻量级清理
if (memoryUsage > 0.7) {
// 清理SVGA缓存中较旧的项目
if (svgaCache.size() > 1) {
// 保留最近使用的1个缓存项
String oldestKey = null;
for (String key : svgaCache.keySet()) {
if (oldestKey == null) {
oldestKey = key;
}
}
if (oldestKey != null) {
svgaCache.remove(oldestKey);
}
}
// 建议进行垃圾回收
System.gc();
}
2025-10-20 10:16:44 +08:00
}
public enum RenderType {SVGA, MP4}
private RenderType renderType;
2025-10-24 17:52:11 +08:00
// private ExoPlayer exoPlayer;
// private PlayerView playerView;
2025-10-20 10:16:44 +08:00
private SVGAImageView svgaSurface;
2025-10-24 17:52:11 +08:00
private SVGAImageView svgaSurface2;
2025-10-20 10:16:44 +08:00
private GLSurfaceView glSurfaceView;
private final Handler mainHandler = new Handler(Looper.getMainLooper());
private ChannelSplitRenderer1 renderer;
private int mType;//1:循环播放 2:播放一次停止播放
private File mFile;
2025-10-24 17:52:11 +08:00
private final BlockingQueue<PlayItem> playQueue = new LinkedBlockingQueue<>();
2025-10-20 10:16:44 +08:00
private boolean isPlaying = false;
2025-10-24 17:52:11 +08:00
// 添加销毁标记
private boolean isDestroyed = false;
private static final String TAG = "AvatarFrameView";
private RoomViewSvgaAnimBinding mBinding;
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
// 内存监控
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);
2025-10-20 10:16:44 +08:00
public AvatarFrameView(@NonNull Context context) {
this(context, null);
}
public AvatarFrameView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public AvatarFrameView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.room_view_svga_anim, this, true);
initViews();
2025-10-24 17:52:11 +08:00
2025-10-20 10:16:44 +08:00
}
private void initViews() {
2025-10-24 17:52:11 +08:00
// if (isDestroyed) return;
2025-10-20 10:16:44 +08:00
// 初始化 ExoPlayer View
2025-10-24 17:52:11 +08:00
// playerView = new PlayerView(getContext());
// playerView.setUseController(false);
// playerView.setVisibility(View.GONE);
// addView(playerView);
2025-10-20 10:16:44 +08:00
// 初始化 SVGA View
svgaSurface = new SVGAImageView(getContext());
svgaSurface.setVisibility(View.GONE);
addView(svgaSurface);
2025-10-24 17:52:11 +08:00
svgaSurface2 = new SVGAImageView(getContext());
svgaSurface2.setVisibility(View.GONE);
addView(svgaSurface2);
// 初始化播放管理器
playbackManager = new PlaybackManager(mainHandler);
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
// 获取 MP4PlaybackCallback 单例实例并设置引用
// MP4PlaybackCallback callback = MP4PlaybackCallback.getInstance();
// callback.setAvatarFrameView(this);
// 设置播放完成监听
// mBinding.playView.setAnimListener(callback);
// if (mBinding != null) {
// mBinding.playView.setAnimListener(this);
// }
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
// MP4PlaybackCallback = new IAnimListener() {
// @Override
// public boolean onVideoConfigReady(@NonNull AnimConfig animConfig) {
// return false;
// }
//
// @Override
// public void onVideoStart() {
//
// }
//
// @Override
// public void onVideoRender(int i, @Nullable AnimConfig animConfig) {
//
// }
//
// @Override
// public void onVideoComplete() {
// onPlaybackComplete();
// }
//
// @Override
// public void onVideoDestroy() {
//
// }
//
// @Override
// public void onFailed(int i, @Nullable String s) {
// onPlaybackComplete();
// }
// };
// // 设置播放完成监听
// mBinding.playView.setAnimListener(MP4PlaybackCallback);
2025-10-20 10:16:44 +08:00
}
2025-10-24 17:52:11 +08:00
2025-10-20 10:16:44 +08:00
private String getFileExtension(String url) {
if (url == null || url.isEmpty()) return "";
int dotIndex = url.lastIndexOf(".");
if (dotIndex > 0 && dotIndex < url.length() - 1) {
return url.substring(dotIndex + 1).toLowerCase(); // 返回 "mp4", "svga" 等
}
return "";
}
2025-10-24 17:52:11 +08:00
public void playNextFromQueue() {
// if (isDestroyed) return;
2025-10-20 10:16:44 +08:00
// 确保在主线程中执行
2025-10-24 17:52:11 +08:00
// if (Looper.myLooper() != Looper.getMainLooper()) {
// mainHandler.post(this::playNextFromQueue);
// return;
// }
2025-10-20 10:16:44 +08:00
// 检查特效是否开启
2025-10-24 17:52:11 +08:00
// if (SpUtil.getOpenEffect() != 1) {
// clearQueue();
// return;
// }
// 检查是否可以开始新的播放
// if (!playbackManager.canStartNewPlayback()) {
// Logger.d("AvatarFrameView", "Max concurrent playbacks reached, waiting...");
// return;
// }
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
if (!isPlaying || !isActuallyPlaying()) {
PlayItem item = playQueue.poll();
if (item != null) {
// 通知开始播放
playbackManager.onStartPlayback();
isPlaying = true;
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
// 处理播放项目
processPlayItem(item);
} else {
2025-10-20 10:16:44 +08:00
isPlaying = false;
2025-10-24 17:52:11 +08:00
Logger.d("AvatarFrameView", "Queue is empty, stop playing");
2025-10-20 10:16:44 +08:00
}
2025-10-24 17:52:11 +08:00
}
}
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
// 添加统一的播放完成处理方法
public void onPlaybackComplete() {
if (isDestroyed) return;
mainHandler.post(() -> {
// 再次检查是否已销毁
if (isDestroyed) return;
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
// // 通知播放管理器播放完成
// playbackManager.onFinishPlayback();
// 重置播放状态
isPlaying = false;
// 内存清理检查
// if (playQueue.size() % 5 == 0) {
// performLightMemoryCleanup();
// }
// 继续处理队列中的下一个项目
playNextFromQueue();
});
}
private void processPlayItem(PlayItem item) {
try {
// clearPrevious();
String ext = getFileExtension(item.url);
if ("svga".equalsIgnoreCase(ext)) {
mainHandler.post(() -> {
renderType = RenderType.SVGA;
mType = item.type;
if (mBinding != null && mBinding.playView != null) {
mBinding.playView.setVisibility(View.GONE);
}
2025-10-20 10:16:44 +08:00
loadSVGA(item.url);
2025-10-24 17:52:11 +08:00
});
} else if ("mp4".equalsIgnoreCase(ext)) {
mainHandler.post(() -> {
renderType = RenderType.MP4;
mType = item.type;
if (mBinding != null && mBinding.playView != null) {
mBinding.playView.setVisibility(View.VISIBLE);
downloadAndPlayMp4(item.url);
}else {
mBinding = DataBindingUtil.inflate(LayoutInflater.from(getContext()), R.layout.room_view_svga_anim, this, true);
mBinding.playView.setVisibility(View.VISIBLE);
downloadAndPlayMp4(item.url);
}
});
} else {
// 不支持的格式,直接完成
handlePlaybackComplete();
2025-10-20 10:16:44 +08:00
}
2025-10-24 17:52:11 +08:00
} catch (Exception e) {
LogUtils.e(TAG, "Error processing play item: " + e.getMessage());
handlePlaybackComplete();
}
}
// 添加实际播放状态检查方法
private boolean isActuallyPlaying() {
if (renderType == RenderType.SVGA && svgaSurface != null) {
return svgaSurface.isAnimating();
} else if (renderType == RenderType.MP4 && mBinding != null) {
// 检查播放器状态
return mBinding.playView.isRunning();
}
return false;
}
public boolean isPlaying() {
if (mBinding != null && mBinding.playView != null) {
return mBinding.playView.isRunning();
2025-10-20 10:16:44 +08:00
}
2025-10-24 17:52:11 +08:00
return true;
2025-10-20 10:16:44 +08:00
}
2025-10-24 17:52:11 +08:00
// 在 AvatarFrameView 类中添加以下代码
private static final int MAX_CONCURRENT_PROCESSING = 3; // 同时处理的最大动画数
private static final int PROCESSING_DELAY = 100; // 处理间隔(毫秒)
private int currentProcessingCount = 0;
2025-10-20 10:16:44 +08:00
public void setSource(String url, int type2) {
2025-10-24 17:52:11 +08:00
// if (isDestroyed) return;
2025-10-20 10:16:44 +08:00
// 确保在主线程中执行
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(() -> setSource(url, type2));
return;
}
2025-10-24 17:52:11 +08:00
// 添加到播放队列
playQueue.add(new PlayItem(url, type2));
Logger.d("AvatarFrameView", "Added to queue, queue size: " + playQueue.size() + ", url: " + url);
if (type2 == 3 || type2 == 1) {
// playNextFromQueue();
loadSVGA(url);
} else {
// 如果当前没有在播放,则开始播放
if (!isPlaying || !isActuallyPlaying()) {
playNextFromQueue();
}
2025-10-20 10:16:44 +08:00
}
2025-10-24 17:52:11 +08:00
// 异步处理URL解析等耗时操作
// ThreadUtils.executeByIo(new ThreadUtils.SimpleTask<String>() {
// @Override
// public String doInBackground() throws Throwable {
// // 在后台线程进行URL有效性检查或其他预处理
// if (url == null || url.isEmpty()) {
// return null;
// }
// return url; // 返回处理后的URL
// }
//
// @Override
// public void onSuccess(String processedUrl) {
// if (processedUrl != null) {
// // 使用post方法确保在下一个UI循环中执行
// mainHandler.post(() -> {
// // 再次检查状态
// if (!isDestroyed && !isPlaying) {
// playQueue.add(new PlayItem(processedUrl, type2));
//// checkAndStartPlayback();
// // 智能检查并开始播放
// smartCheckAndStartPlayback();
// } else {
// // 如果正在播放,添加到队列中
// playQueue.add(new PlayItem(processedUrl, type2));
// }
// });
// }
// }
//
// @Override
// public void onFail(Throwable e) {
// LogUtils.e("Error processing gift URL: " + e.getMessage());
// }
// });
2025-10-20 10:16:44 +08:00
// 添加到播放队列
// playQueue.offer(new PlayItem(url, type2));
2025-10-24 17:52:11 +08:00
// playQueue.add(new PlayItem(url, type2));
2025-10-20 10:16:44 +08:00
Logger.d("AvatarFrameView", "Added to queue, queue size: " + playQueue.size() + ", url: " + url);
// 如果当前没有在播放,则开始播放
2025-10-24 17:52:11 +08:00
// if (!isPlaying) {
// playNextFromQueue();
// }
// 改进播放检查逻辑
// checkAndStartPlayback();
}
private void smartCheckAndStartPlayback() {
// 检查是否可以开始新的播放任务
if (!playQueue.isEmpty() &&
(!isPlaying || !isActuallyPlaying()) &&
currentProcessingCount < MAX_CONCURRENT_PROCESSING) {
currentProcessingCount++;
2025-10-20 10:16:44 +08:00
playNextFromQueue();
}
}
2025-10-24 17:52:11 +08:00
private void checkAndStartPlayback() {
// 如果队列不为空且当前没有在播放,则开始播放
if (!playQueue.isEmpty() && (!isPlaying || !isActuallyPlaying())) {
playNextFromQueue();
}
}
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
private boolean isMemoryLow() {
Runtime runtime = Runtime.getRuntime();
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
long maxMemory = runtime.maxMemory();
double memoryUsage = (double) usedMemory / maxMemory;
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
// 内存使用超过80%或绝对使用量超过阈值
return memoryUsage > 0.8 || usedMemory > MAX_MEMORY_THRESHOLD;
}
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
boolean isTxk = false;
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
public void downloadAndPlayMp4(String url) {
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
// 提取文件名
String fileName = url.substring(url.lastIndexOf("/"));
String filePath = getContext().getCacheDir().getAbsolutePath() + fileName;
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
LogUtils.e("@@@@@filePath: " + filePath.toString());
File file = new File(filePath);
if (!file.exists()) {
LogUtils.e("无缓存");
// 使用OkHttp进行下载
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
LogUtils.e("MP4下载失败: " + e.toString());
mainHandler.post(() -> {
// 检查是否已销毁
if (!isDestroyed) {
onPlaybackComplete();
}
});
}
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
// 更简单的优化版本
@Override
public void onResponse(Call call, Response response) throws IOException {
// 在异步回调中首先检查是否已销毁
if (isDestroyed) {
LogUtils.w(TAG, "View destroyed before download completed");
return;
}
LogUtils.d("@@@@", "onResponse" + Thread.currentThread().getName());
if (response.isSuccessful()) {
try (ResponseBody responseBody = response.body()) {
if (responseBody != null) {
// 在后台线程处理文件保存
String fileName = url.substring(url.lastIndexOf("/"));
String filePath = getContext().getCacheDir().getAbsolutePath() + fileName;
File downloadedFile = new File(filePath);
// 使用流式传输避免大文件卡顿
try (InputStream inputStream = responseBody.byteStream();
FileOutputStream fos = new FileOutputStream(downloadedFile)) {
// 定义缓冲区大小8KB
byte[] buffer = new byte[1024 * 1024];
int bytesRead;
// 流式读取并写入文件
while ((bytesRead = inputStream.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
fos.flush();
isTxk = true;
mainHandler.post(() -> {
// 关键在执行UI操作前再次检查是否已销毁
if (downloadedFile.exists()) {
LogUtils.d("@@@@Thread", Thread.currentThread().getName());
playMp4File(downloadedFile); // 使用正确的文件引用
} else {
LogUtils.w(TAG, "View destroyed or file not exist after download");
onPlaybackComplete();
}
});
} catch (IOException e) {
LogUtils.e("MP4文件保存失败: " + e.getMessage());
mainHandler.post(() -> {
if (!isDestroyed) {
onPlaybackComplete();
}
});
}
} else {
mainHandler.post(() -> {
if (!isDestroyed) {
onPlaybackComplete();
}
});
}
} catch (Exception e) {
LogUtils.e("MP4文件保存失败: " + e.getMessage());
mainHandler.post(() -> {
if (!isDestroyed) {
onPlaybackComplete();
}
});
2025-10-20 10:16:44 +08:00
}
2025-10-24 17:52:11 +08:00
} else {
LogUtils.e("MP4下载响应失败");
mainHandler.post(() -> {
if (!isDestroyed) {
onPlaybackComplete();
}
});
2025-10-20 10:16:44 +08:00
}
2025-10-24 17:52:11 +08:00
}
});
} else {
isTxk = true;
LogUtils.e("有缓存");
mainHandler.post(() -> {
// 检查是否已销毁
if (file.exists()) {
playMp4File(file);
} else {
LogUtils.w(TAG, "有缓存2222222222222");
// onPlaybackComplete();
}
});
// 直接播放缓存文件
// if (isTxk) {
// mBinding.playView.setLoop(1);
2025-10-20 10:16:44 +08:00
// }
2025-10-24 17:52:11 +08:00
// mBinding.playView.startPlay(file);
}
}
private void playMp4File(File file) {
try {
// 双重检查确保组件未被销毁
if (isDestroyed) {
LogUtils.w(TAG, "Attempt to play MP4 file after view destroyed");
onPlaybackComplete();
return;
}
if (mBinding == null || mBinding.playView == null) {
LogUtils.w(TAG, "PlayView is null");
onPlaybackComplete();
return;
}
if (file != null && file.exists()) {
// 设置循环次数根据mType决定
if (mType == 1 || mType == 3) {
mBinding.playView.setLoop(Integer.max(1, 999999999)); // 无限循环
} else {
mBinding.playView.setLoop(1); // 播放一次
}
// 开始播放前检查视图状态
if (!isDestroyed && mBinding != null && mBinding.playView != null) {
mBinding.playView.startPlay(file);
} else {
LogUtils.w(TAG, "View was destroyed before MP4 playback started");
onPlaybackComplete();
}
} else {
LogUtils.e("播放MP4文件出错: 文件不存在或已损坏");
onPlaybackComplete();
}
} catch (Exception e) {
LogUtils.e("播放MP4文件出错: " + e.getMessage());
onPlaybackComplete();
2025-10-20 10:16:44 +08:00
}
}
2025-10-24 17:52:11 +08:00
// private void downloadAndPlayMp4(String url) {
// String filePath = PathUtils.getInternalAppCachePath() + Md5Utils.getStringMD5(url) + ".mp4";
// File file = new File(filePath);
//
// if (file.exists() && file.length() > 0) {
// playMp4(file);
// mFile = file;
// } else {
// // 删除可能存在的损坏文件
//// if (file.exists()) {
//// file.delete();
//// }
//
// DownloadTask task = new DownloadTask.Builder(url, PathUtils.getInternalAppCachePath()
// , Md5Utils.getStringMD5(url) + ".mp4")
// .setMinIntervalMillisCallbackProcess(300)
// .setPassIfAlreadyCompleted(true)
// .setAutoCallbackToUIThread(true)
// .setConnectionCount(3) // 增加连接数提高稳定性
// .setReadBufferSize(1024 * 8) // 增大缓冲区
// .build();
// if (StatusUtil.isCompleted(task)) {
// playMp4(task.getFile());
// mFile = task.getFile();
// } else if (StatusUtil.isSameTaskPendingOrRunning(task)) {
// Logger.d(TAG, "Task is pending or running, checking if it's stuck");
// // 检查任务是否可能卡住了
// // 添加超时机制,如果任务长时间没有进展,则重新开始
// checkAndHandleStuckTask(task, url);
// } else {
// Logger.d(TAG, "Starting new download task");
// startNewDownload(task, url);
// }
//
//// else if (StatusUtil.isSameTaskPendingOrRunning(task)) {
//// // 如果任务正在进行中,等待完成
//// // 可以通过监听器处理
//// attachToExistingTask(task);
//// } else {
//// task.enqueue(new DownloadListener1() {
//// @Override
//// public void taskStart(@NonNull DownloadTask task, @NonNull Listener1Assist.Listener1Model model) {
//// Logger.e("AvatarFrameView1", model);
//// }
////
//// @Override
//// public void retry(@NonNull DownloadTask task, @NonNull ResumeFailedCause cause) {
//// Logger.e("AvatarFrameView2", cause);
////
//// }
////
//// @Override
//// public void connected(@NonNull DownloadTask task, int blockCount, long currentOffset, long totalLength) {
//// Logger.e("AvatarFrameView3", blockCount);
//// }
////
//// @Override
//// public void progress(@NonNull DownloadTask task, long currentOffset, long totalLength) {
//// Logger.e("AvatarFrameView4", currentOffset);
//// }
////
//// @Override
//// public void taskEnd(@NonNull DownloadTask task, @NonNull EndCause cause, @Nullable Exception realCause, @NonNull Listener1Assist.Listener1Model model) {
//// Logger.e("AvatarFrameView5", model);
////// playMp4(task.getFile());
////// mFile = task.getFile();
////// if (cause != null && cause != EndCause.COMPLETED) {
//////// CrashReport.postCatchedException(new RuntimeException("下载任务结束:" + cause == null ? "" : cause.name() + "_realCause:" + realCause == null ? "" : realCause.getMessage()));
////// }
////
//// if (cause == EndCause.COMPLETED) {
//// File downloadedFile = task.getFile();
//// if (downloadedFile != null && downloadedFile.exists() && downloadedFile.length() > 0) {
//// playMp4(downloadedFile);
//// mFile = downloadedFile;
//// } else {
//// Logger.e(TAG, "Downloaded file is invalid");
//// handleDownloadFailure(url, cause, new IOException("Downloaded file is invalid"));
//// }
//// } else {
//// handleDownloadFailure(url, cause, realCause);
//// }
//// }
//// });
//// }
// }
// }
// 添加检查进行中任务的方法
private void attachToExistingTask(DownloadTask task) {
// 为已经在队列中的任务附加监听器
task.enqueue(new DownloadListener1() {
@Override
public void taskEnd(@NonNull DownloadTask task, @NonNull EndCause cause, @Nullable Exception realCause, @NonNull Listener1Assist.Listener1Model model) {
if (cause == EndCause.COMPLETED) {
playMp4(task.getFile());
mFile = task.getFile();
} else {
isPlaying = false;
playNextFromQueue();
}
}
// 其他回调方法保持空实现或按需处理
@Override
public void taskStart(@NonNull DownloadTask task, @NonNull Listener1Assist.Listener1Model model) {
}
@Override
public void retry(@NonNull DownloadTask task, @NonNull ResumeFailedCause cause) {
}
@Override
public void connected(@NonNull DownloadTask task, int blockCount, long currentOffset, long totalLength) {
}
@Override
public void progress(@NonNull DownloadTask task, long currentOffset, long totalLength) {
}
});
}
2025-10-20 10:16:44 +08:00
private void playMp4(File file) {
if (file != null) {
mBinding.playView.startPlay(file);
} else {
// showAnim();
// playMp4(file);
// CrashReport.postCatchedException(new RuntimeException("播放MP4失败:File is null"));
}
}
2025-10-24 17:52:11 +08:00
private void handleSVGAComplete(SVGAVideoEntity videoItem, String url) {
if (svgaSurface == null) {
onPlaybackComplete();
return;
}
try {
// 缓存实体(使用弱引用)
// 缓存实体(使用弱引用)
if (svgaCache.size() < MAX_SVGA_CACHE_SIZE) {
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() {
// 循环播放处理
if (mType != 1) { // 非循环播放
svgaSurface.stopAnimation();
svgaSurface.clearAnimation();
svgaSurface.setImageDrawable(null);
onPlaybackComplete();
}
}
@Override
public void onPause() {
}
@Override
public void onFinished() {
// if (isDestroyed) return;
if (mType == 1) { // 循环播放
// 继续循环播放
} else {
onPlaybackComplete();
}
// if (Looper.myLooper() != Looper.getMainLooper()) {
// mainHandler.post(() -> {
// isPlaying = false;
// playNextFromQueue();
// });
// } else {
// isPlaying = false;
// playNextFromQueue();
// }
}
});
// 设置循环次数
if (mType == 1 || mType == 3) {
svgaSurface.setLoops(0); // 无限循环
} else {
svgaSurface.setLoops(1); // 播放一次
}
svgaSurface.startAnimation();
} catch (Exception e) {
LogUtils.e(TAG, "Error handling SVGA completion: " + e.getMessage());
// isPlaying = false;
// playNextFromQueue();
handlePlaybackComplete();
}
}
public void stopAll() {
if (svgaSurface != null) {
svgaSurface.stopAnimation();
svgaSurface.clearAnimation();
svgaSurface.setImageDrawable(null);
}
if (svgaSurface2 != null) {
svgaSurface2.stopAnimation();
svgaSurface2.clearAnimation();
svgaSurface.setImageDrawable(null);
}
// 增加空值检查
if (mBinding != null && mBinding.playView != null) {
mBinding.playView.stopPlay();
}
}
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;
// 添加延迟确保状态更新
mainHandler.postDelayed(AvatarFrameView.this::playNextFromQueue, 50);
// playNextFromQueue();
});
} else {
isPlaying = false;
// playNextFromQueue();
mainHandler.postDelayed(AvatarFrameView.this::playNextFromQueue, 50);
}
}
});
// 设置循环次数
if (mType == 1 || mType == 3) {
svgaSurface.setLoops(0); // 无限循环
} else {
svgaSurface.setLoops(1); // 播放一次
}
svgaSurface.startAnimation();
} catch (Exception e) {
LogUtils.e(TAG, "Error playing cached SVGA: " + e.getMessage());
isPlaying = false;
mainHandler.postDelayed(AvatarFrameView.this::playNextFromQueue, 50);
// 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();
}
}
2025-10-20 10:16:44 +08:00
private void loadSVGA(String url) {
2025-10-24 17:52:11 +08:00
// 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();
}
2025-10-20 10:16:44 +08:00
svgaSurface.setVisibility(View.VISIBLE);
try {
new SVGAParser(getContext()).parse(new URL(url), new SVGAParser.ParseCompletion() {
@Override
public void onComplete(SVGAVideoEntity 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() {
isPlaying = false;
playNextFromQueue();
}
});
// svgaSurface.setCallback(new SVGAImageViewCallback() {
// @Override
// public void onAnimationFinished() {
// isPlaying = false;
// playNextFromQueue();
// }
// });
svgaSurface.startAnimation();
}
@Override
public void onError() {
isPlaying = false;
playNextFromQueue();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
2025-10-24 17:52:11 +08:00
// private void loadMP4(String url) {
// svgaSurface.setVisibility(View.GONE);
// playerView.setVisibility(View.GONE);
//
// glSurfaceView.setVisibility(View.VISIBLE);
// glSurfaceView.onResume();
// glSurfaceView.requestRender();
// // 使用 post 确保 GLSurfaceView 已完成 layout
// glSurfaceView.post(() -> {
// Log.d("@@@", "GLSurfaceView size after layout: " + glSurfaceView.getWidth() + "x" + glSurfaceView.getHeight());
//
// glSurfaceView.onResume();
// glSurfaceView.requestRender();
//
// renderer.setOnSurfaceTextureReadyListener(surfaceTexture -> {
// mainHandler.post(() -> {
// Surface surface = new Surface(surfaceTexture);
//
// MediaItem mediaItem = MediaItem.fromUri(Uri.parse(url));
// exoPlayer.setMediaItem(mediaItem);
// exoPlayer.setVideoSurface(surface);
// exoPlayer.prepare();
// exoPlayer.play();
// Log.d("@@@", "ExoPlayer state after play: " + exoPlayer.getPlaybackState());
// });
// });
// });
// }
2025-10-20 10:16:44 +08:00
private void clearPrevious() {
// 确保在主线程中执行
2025-10-24 17:52:11 +08:00
// if (Looper.myLooper() != Looper.getMainLooper()) {
// mainHandler.post(() -> {
// 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);
}
2025-10-20 10:16:44 +08:00
}
2025-10-24 17:52:11 +08:00
// 隐藏所有视图
// if (playerView != null) playerView.setVisibility(View.GONE);
if (svgaSurface != null) svgaSurface.setVisibility(View.GONE);
mBinding.playView.setVisibility(View.GONE);
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
// 停止播放器
if (mBinding != null && mBinding.playView != null) {
mBinding.playView.stopPlay();
}
} catch (Exception e) {
LogUtils.e(TAG, "Error in clearPrevious: " + e.getMessage());
2025-10-20 10:16:44 +08:00
}
// 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);
}
2025-10-24 17:52:11 +08:00
// 简单的 LRU Cache 实现
private static class LruCache<K, V> extends LinkedHashMap<K, V> {
private final int maxSize;
public LruCache(int maxSize) {
super(16, 0.75f, true);
this.maxSize = maxSize;
}
@Override
protected boolean removeEldestEntry(Entry<K, V> eldest) {
return size() > maxSize;
}
}
2025-10-20 10:16:44 +08:00
/**
* 释放所有资源
*/
private void releaseResources() {
2025-10-24 17:52:11 +08:00
LogUtils.d(TAG, "Releasing all resources");
// if (isDestroyed) return;
// 使用异步线程处理耗时操作
new Thread(() -> {
try {
// 在后台线程处理文件操作和大对象清理
// performHeavyCleanup();
// 回到主线程处理 UI 相关的清理
mainHandler.post(() -> {
performUICleanup();
});
} catch (Exception e) {
Logger.e(TAG, "Error in async releaseResources: " + e.getMessage());
// 出错时仍在主线程清理 UI 资源
mainHandler.post(() -> {
performUICleanup();
});
2025-10-20 10:16:44 +08:00
}
2025-10-24 17:52:11 +08:00
}).start();
// 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());
// }
}
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
/**
* 在后台线程执行耗时的清理操作
*/
private void performHeavyCleanup() {
try {
// 清理缓存文件(如果需要)
// 清理大对象引用等
// clearCacheFiles();
} catch (Exception e) {
Logger.e(TAG, "Error in performHeavyCleanup: " + e.getMessage());
2025-10-20 10:16:44 +08:00
}
2025-10-24 17:52:11 +08:00
}
/**
* 在主线程执行 UI 相关的清理操作
*/
private void performUICleanup() {
try {
// 停止并清理播放器
if (mBinding != null && mBinding.playView != null) {
try {
mBinding.playView.stopPlay();
} catch (Exception e) {
Logger.e(TAG, "Error stopping playView: " + e.getMessage());
}
}
// 清理 ExoPlayer 资源
// if (exoPlayer != null) {
// try {
// // 使用异步停止避免阻塞
// exoPlayer.stop();
// exoPlayer.clearVideoSurface();
// } catch (Exception e) {
// Logger.e(TAG, "Error releasing ExoPlayer resources: " + e.getMessage());
// }
// }
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
// 清理 SVGA 资源
if (svgaSurface != null) {
try {
// svgaSurface.pauseAnimation();
// svgaSurface.clearAnimation();
// svgaSurface.setImageDrawable(null);
svgaSurface.stopAnimation(true);
svgaSurface.clear();
svgaSurface.clearAnimation();
svgaSurface.setImageDrawable(null);
} catch (Exception e) {
Logger.e(TAG, "Error releasing SVGA resources: " + e.getMessage());
}
}
} catch (Exception e) {
Logger.e(TAG, "Error in performUICleanup: " + e.getMessage());
2025-10-20 10:16:44 +08:00
}
}
2025-10-24 17:52:11 +08:00
2025-10-20 10:16:44 +08:00
/**
* 公共释放方法用于外部主动释放资源
*/
public void release() {
Logger.d("AvatarFrameView", "Public release called");
2025-10-24 17:52:11 +08:00
// if (isDestroyed) return;
2025-10-20 10:16:44 +08:00
// 确保在主线程中执行
2025-10-24 17:52:11 +08:00
// if (Looper.myLooper() != Looper.getMainLooper()) {
// mainHandler.post(this::release);
// return;
// }
isDestroyed = true;
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
try {
// 清空播放队列
clearQueue();
// 清理播放管理器
if (playbackManager != null) {
playbackManager.reset();
}
// 释放所有资源
releaseResources();
2025-10-20 10:16:44 +08:00
2025-10-24 17:52:11 +08:00
// 延迟清理其他资源
mainHandler.postDelayed(() -> {
// 清理 binding
if (mBinding != null) {
mBinding = null;
2025-10-20 10:16:44 +08:00
}
2025-10-24 17:52:11 +08:00
}, 100);
// 清理 binding
if (mBinding != null) {
mBinding = null;
2025-10-20 10:16:44 +08:00
}
2025-10-24 17:52:11 +08:00
} catch (Exception e) {
LogUtils.e(TAG, "Error in AvatarFrameView release: " + e.getMessage());
} finally {
// 建议进行垃圾回收
// MemoryOptimizationUtils.forceGC();
2025-10-20 10:16:44 +08:00
}
}
2025-10-24 17:52:11 +08:00
2025-10-20 10:16:44 +08:00
public void clearQueue() {
2025-10-24 17:52:11 +08:00
// if (isDestroyed) return;
2025-10-20 10:16:44 +08:00
playQueue.clear();
isPlaying = false;
2025-10-24 17:52:11 +08:00
// 清理播放管理器中的任务
if (playbackManager != null) {
playbackManager.reset();
}
2025-10-20 10:16:44 +08:00
// 清理当前正在播放的内容
clearPrevious();
}
2025-10-24 17:52:11 +08:00
// 在类成员变量中添加
private static final int PLAYBACK_TIMEOUT = 10000; // 10秒超时
private Map<String, Long> playbackStartTimeMap = new HashMap<>();
// 添加超时检查方法
private void startPlaybackTimeout(String url) {
playbackStartTimeMap.put(url, System.currentTimeMillis());
mainHandler.postDelayed(() -> checkPlaybackTimeout(url), PLAYBACK_TIMEOUT);
}
private void checkPlaybackTimeout(String url) {
Long startTime = playbackStartTimeMap.get(url);
if (startTime != null && System.currentTimeMillis() - startTime > PLAYBACK_TIMEOUT) {
LogUtils.w(TAG, "Playback timeout: " + url);
playbackStartTimeMap.remove(url);
// 强制结束当前播放并继续下一个
handlePlaybackComplete();
}
}
private void cancelPlaybackTimeout(String url) {
playbackStartTimeMap.remove(url);
}
2025-10-20 10:16:44 +08:00
private static class PlayItem {
String url;
int type;
PlayItem(String url, int type) {
this.url = url;
this.type = type;
}
}
2025-10-24 17:52:11 +08:00
2025-10-20 10:16:44 +08:00
/**
* 关闭特效
*/
public void closeEffect() {
2025-10-24 17:52:11 +08:00
// 清空队列
clearQueue();
2025-10-20 10:16:44 +08:00
// 释放资源
2025-10-24 17:52:11 +08:00
// releaseResources();
// 清空队列
// playQueue.clear();
// 关闭动画
// if (mBinding.playView != null && isPlaying && mBinding.playView.isRunning()) {
// mBinding.playView.setAnimation(null);
// mBinding.playView.clearAnimation();
// mBinding.playView.stopPlay();
2025-10-20 10:16:44 +08:00
// }
}
2025-10-24 17:52:11 +08:00
/**
* 开始循环播放SVGA动画
*/
public void startLoopingSvga(String assetName) {
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(() -> startLoopingSvga(assetName));
return;
}
try {
// clearPrevious(); // 清除之前的动画
svgaSurface.setVisibility(View.VISIBLE);
new SVGAParser(getContext()).decodeFromAssets(assetName, new SVGAParser.ParseCompletion() {
@Override
public void onComplete(SVGAVideoEntity svgaVideoEntity) {
SVGADrawable drawable = new SVGADrawable(svgaVideoEntity);
svgaSurface.setImageDrawable(drawable);
svgaSurface.setLoops(0); // 0表示无限循环
svgaSurface.startAnimation();
}
@Override
public void onError() {
Log.e(TAG, "解析SVGA文件失败: " + assetName);
}
});
} catch (Exception e) {
Log.e(TAG, "播放SVGA动画出错", e);
}
}
/**
* 停止并销毁当前SVGA动画
*/
public void stopSvga() {
if (Looper.myLooper() != Looper.getMainLooper()) {
mainHandler.post(() -> stopSvga());
return;
}
try {
if (svgaSurface2 != null) {
svgaSurface2.stopAnimation(true);
svgaSurface2.setImageDrawable(null);
svgaSurface2.setVisibility(View.GONE);
}
} catch (Exception e) {
Log.e(TAG, "停止SVGA动画出错", e);
}
}
// 在 AvatarFrameView 类中添加以下代码
// 播放任务管理器
// 替换现有的 PlaybackManager 类
private static class PlaybackManager {
private static final int MAX_CONCURRENT_PLAYBACKS = 3; // 增加并发数
private int currentPlaybackCount = 0;
private final Handler handler;
public PlaybackManager(Handler handler) {
this.handler = handler;
}
public boolean canStartNewPlayback() {
return currentPlaybackCount < MAX_CONCURRENT_PLAYBACKS;
}
public void onStartPlayback() {
currentPlaybackCount++;
}
public void onFinishPlayback() {
currentPlaybackCount = Math.max(0, currentPlaybackCount - 1);
}
public void reset() {
currentPlaybackCount = 0;
}
}
// 播放任务接口
private interface PlaybackTask {
void execute();
}
// 在 AvatarFrameView 类中添加以下代码
// private IAnimListener MP4PlaybackCallback;
// 在 AvatarFrameView 类的成员变量区域添加单例实例
public void setAnimListener(IAnimListener mInstance) {
mBinding.playView.setAnimListener(mInstance);
}
// 添加统一的播放完成处理方法
private void handlePlaybackComplete() {
mainHandler.post(() -> {
if (isDestroyed) return;
isPlaying = false;
// 通知播放管理器任务完成
if (playbackManager != null) {
playbackManager.reset();
}
// 内存检查
if (playQueue.size() % 5 == 0) {
// performLightMemoryCleanup();
}
// 播放下一个
playNextFromQueue();
});
}
// 添加轻量级内存清理方法
private void performLightMemoryCleanup() {
Runtime runtime = Runtime.getRuntime();
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
long maxMemory = runtime.maxMemory();
double memoryUsage = (double) usedMemory / maxMemory;
// 内存使用超过70%时进行清理
if (memoryUsage > 0.7) {
// 清理SVGA缓存
if (svgaCache.size() > 1) {
// 保留最新的缓存项
Iterator<Map.Entry<String, WeakReference<SVGAVideoEntity>>> iterator =
svgaCache.entrySet().iterator();
if (iterator.hasNext()) {
iterator.next(); // 跳过最新的
if (iterator.hasNext()) {
iterator.remove(); // 移除较旧的
}
}
}
// 建议进行垃圾回收
System.gc();
}
}
2025-10-20 10:16:44 +08:00
}