|
|
@@ -1,27 +1,31 @@
|
|
|
package xn.xxp.main.monitor;
|
|
|
|
|
|
+import static com.blankj.utilcode.util.ViewUtils.runOnUiThread;
|
|
|
+
|
|
|
import android.content.DialogInterface;
|
|
|
-import android.graphics.SurfaceTexture;
|
|
|
import android.os.Bundle;
|
|
|
+import android.os.Handler;
|
|
|
+import android.os.Looper;
|
|
|
import android.util.Log;
|
|
|
-import android.view.TextureView;
|
|
|
import android.view.View;
|
|
|
+import android.view.ViewGroup;
|
|
|
import android.view.ViewTreeObserver;
|
|
|
import android.widget.GridLayout;
|
|
|
+import android.widget.TextView;
|
|
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
import androidx.fragment.app.Fragment;
|
|
|
|
|
|
import com.blankj.utilcode.util.LogUtils;
|
|
|
import com.blankj.utilcode.util.ThreadUtils;
|
|
|
-import com.hikvision.hatomplayer.DefaultHatomPlayer;
|
|
|
-import com.hikvision.hatomplayer.HatomPlayer;
|
|
|
-import com.hikvision.hatomplayer.PlayCallback;
|
|
|
-import com.hikvision.hatomplayer.PlayConfig;
|
|
|
-import com.hikvision.hatomplayer.core.Quality;
|
|
|
+import cn.nodemedia.NodePlayer;
|
|
|
+import cn.nodemedia.NodePlayerDelegate;
|
|
|
+import cn.nodemedia.NodePlayerView;
|
|
|
|
|
|
+import java.lang.reflect.Method;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.List;
|
|
|
+import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
|
|
import core.ui.activity.BaseCountDownActivity;
|
|
|
import core.util.FastClickDelegate;
|
|
|
@@ -43,6 +47,9 @@ public class MonitorActivity extends BaseCountDownActivity<ActivityMonitorBindin
|
|
|
private ActivityMonitorBinding binding;
|
|
|
private List<MonitorInfo> monitorInfoList = new ArrayList<>();
|
|
|
private MonitorDialog monitorDialog;
|
|
|
+ private volatile boolean isExiting = false;
|
|
|
+ private volatile boolean isDialogShowing = false;
|
|
|
+ private Handler mainHandler = new Handler(Looper.getMainLooper());
|
|
|
|
|
|
@Override
|
|
|
public ITitleBar getMTitleBar() {
|
|
|
@@ -70,28 +77,7 @@ public class MonitorActivity extends BaseCountDownActivity<ActivityMonitorBindin
|
|
|
|
|
|
@Override
|
|
|
public void onBackViewClicked() {
|
|
|
- showLoading("正在退出...");
|
|
|
- ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Object>() {
|
|
|
- @Override
|
|
|
- public Object doInBackground() throws Throwable {
|
|
|
- if (null != monitorInfoList) {
|
|
|
- for (int i = 0; i < monitorInfoList.size(); i++) {
|
|
|
- MonitorInfo monitorInfo = monitorInfoList.get(i);
|
|
|
- HatomPlayer hatomPlayer = monitorInfo.getHatomPlayer();
|
|
|
- if (null != hatomPlayer) {
|
|
|
- hatomPlayer.stop();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onSuccess(Object result) {
|
|
|
- dismissLoading();
|
|
|
- finish();
|
|
|
- }
|
|
|
- });
|
|
|
+ safeExitActivity();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -100,9 +86,8 @@ public class MonitorActivity extends BaseCountDownActivity<ActivityMonitorBindin
|
|
|
}
|
|
|
});
|
|
|
|
|
|
-
|
|
|
LabConfig labConfig = RoomTool.getInstance().labConfigDao().getLabConfig();
|
|
|
- showLoading("加载中...");
|
|
|
+ showLoading("加载监控...");
|
|
|
addDisposable(ApiRepository.INSTANCE.cameraBySubjectId(String.valueOf(labConfig.getLabId()), LabApp.userVo.userId, LabApp.userVo.userName, 3).subscribe(new Consumer<List<MonitorVo>>() {
|
|
|
@Override
|
|
|
public void accept(List<MonitorVo> monitorVos) throws Throwable {
|
|
|
@@ -114,87 +99,172 @@ public class MonitorActivity extends BaseCountDownActivity<ActivityMonitorBindin
|
|
|
int width = binding.gridLayout.getWidth();
|
|
|
int height = binding.gridLayout.getHeight();
|
|
|
|
|
|
+ // 计数器
|
|
|
+ final AtomicInteger loadingCounter = new AtomicInteger(0);
|
|
|
+ final int totalPlayers = monitorVos.size();
|
|
|
+
|
|
|
+ // 立即关闭主loading
|
|
|
+ dismissLoading();
|
|
|
+
|
|
|
for (int i = 0; i < monitorVos.size(); i++) {
|
|
|
MonitorVo monitorVo = monitorVos.get(i);
|
|
|
MonitorInfo monitorInfo = new MonitorInfo();
|
|
|
- TextureView textureView = new TextureView(MonitorActivity.this);
|
|
|
+
|
|
|
+ // 创建 NodePlayerView
|
|
|
+ NodePlayerView nodePlayerView = new NodePlayerView(MonitorActivity.this);
|
|
|
GridLayout.LayoutParams params = new GridLayout.LayoutParams();
|
|
|
params.width = width / 3;
|
|
|
params.height = height / 3;
|
|
|
- params.setMargins(5, 5, 5, 5); // 设置间距
|
|
|
- textureView.setLayoutParams(params);
|
|
|
- textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
|
|
|
- @Override
|
|
|
- public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
|
|
|
- for (int j = 0; j < monitorInfoList.size(); j++) {
|
|
|
- MonitorInfo monitorInfo = monitorInfoList.get(j);
|
|
|
- SurfaceTexture surfaceTexture = monitorInfo.getTextureView().getSurfaceTexture();
|
|
|
- if (surfaceTexture == surface) {
|
|
|
- play(monitorInfo.getHatomPlayer(), surface, monitorInfo.getMonitorVo().streamUrl);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ params.setMargins(5, 5, 5, 5);
|
|
|
+ nodePlayerView.setLayoutParams(params);
|
|
|
+
|
|
|
+ // 加载提示
|
|
|
+ TextView loadingText = new TextView(MonitorActivity.this);
|
|
|
+ loadingText.setLayoutParams(new android.widget.FrameLayout.LayoutParams(
|
|
|
+ android.widget.FrameLayout.LayoutParams.WRAP_CONTENT,
|
|
|
+ android.widget.FrameLayout.LayoutParams.WRAP_CONTENT,
|
|
|
+ android.view.Gravity.CENTER
|
|
|
+ ));
|
|
|
+ loadingText.setText("连接中");
|
|
|
+ loadingText.setTextColor(0xFFFFFFFF);
|
|
|
+ loadingText.setTextSize(10);
|
|
|
+ loadingText.setBackgroundColor(0x80000000);
|
|
|
+ loadingText.setPadding(10, 5, 10, 5);
|
|
|
+ loadingText.setVisibility(View.VISIBLE);
|
|
|
+
|
|
|
+ // 容器布局
|
|
|
+ android.widget.FrameLayout container = new android.widget.FrameLayout(MonitorActivity.this);
|
|
|
+ container.setLayoutParams(params);
|
|
|
+ container.addView(nodePlayerView);
|
|
|
+ container.addView(loadingText);
|
|
|
+
|
|
|
+ // 保存引用
|
|
|
+ monitorInfo.setLoadingText(loadingText);
|
|
|
+ monitorInfo.setNodePlayerView(nodePlayerView);
|
|
|
+ monitorInfo.setMonitorVo(monitorVo);
|
|
|
+ monitorInfo.setContainerView(container);
|
|
|
+ monitorInfoList.add(monitorInfo);
|
|
|
|
|
|
- @Override
|
|
|
- public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {
|
|
|
+ // 立即添加到布局
|
|
|
+ binding.gridLayout.addView(container);
|
|
|
|
|
|
- }
|
|
|
+ // 立即开始配置播放器
|
|
|
+ final int index = i;
|
|
|
+ final NodePlayer nodePlayer = new NodePlayer(MonitorActivity.this);
|
|
|
|
|
|
- @Override
|
|
|
- public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
|
|
|
- return false;
|
|
|
- }
|
|
|
+ // 基础配置
|
|
|
+ nodePlayer.setPlayerView(nodePlayerView);
|
|
|
+
|
|
|
+ try {
|
|
|
+ nodePlayer.setRtspTransport("1");
|
|
|
+ nodePlayer.setVideoEnable(true);
|
|
|
+ nodePlayer.setAudioEnable(false);
|
|
|
+ nodePlayer.setBufferTime(100);
|
|
|
+ nodePlayer.setMaxBufferTime(300);
|
|
|
+ } catch (Exception e) {
|
|
|
+ LogUtils.e("配置失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 硬件解码
|
|
|
+ try {
|
|
|
+ Method setHWEnableMethod = nodePlayer.getClass()
|
|
|
+ .getMethod("setHWEnable", boolean.class);
|
|
|
+ setHWEnableMethod.invoke(nodePlayer, true);
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 忽略
|
|
|
+ }
|
|
|
+
|
|
|
+ // 地址优化
|
|
|
+ String streamUrl = monitorVo.streamUrl;
|
|
|
+ String optimizedUrl = streamUrl;
|
|
|
+
|
|
|
+ if (streamUrl.contains("stream=0")) {
|
|
|
+ optimizedUrl = streamUrl.replace("stream=0", "stream=1");
|
|
|
+ } else if (streamUrl.contains("main")) {
|
|
|
+ optimizedUrl = streamUrl.replace("main", "sub");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置地址
|
|
|
+ try {
|
|
|
+ nodePlayer.setInputUrl(optimizedUrl);
|
|
|
+ } catch (Exception e) {
|
|
|
+ LogUtils.e("设置地址失败: " + e.getMessage());
|
|
|
+ nodePlayer.setInputUrl(streamUrl);
|
|
|
+ }
|
|
|
|
|
|
+ // 保存播放器
|
|
|
+ monitorInfo.setNodePlayer(nodePlayer);
|
|
|
+
|
|
|
+ // 立即启动播放器
|
|
|
+ try {
|
|
|
+ nodePlayer.start();
|
|
|
+ } catch (Exception e) {
|
|
|
+ LogUtils.e("启动失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置点击事件 - 修复版
|
|
|
+ final MonitorInfo finalMonitorInfo = monitorInfo;
|
|
|
+ container.setOnClickListener(new View.OnClickListener() {
|
|
|
@Override
|
|
|
- public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {
|
|
|
+ public void onClick(View v) {
|
|
|
+ if (isExiting || isDialogShowing) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
+ for (int j = 0; j < monitorInfoList.size(); j++) {
|
|
|
+ MonitorInfo info = monitorInfoList.get(j);
|
|
|
+ if (info.getContainerView() == v) {
|
|
|
+ showFullScreenDialog(info);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- textureView.setOnClickListener(new FastClickDelegate(new Function1<View, Unit>() {
|
|
|
+ // 状态监听
|
|
|
+ final TextView finalLoadingText = loadingText;
|
|
|
+ nodePlayer.setNodePlayerDelegate(new NodePlayerDelegate() {
|
|
|
@Override
|
|
|
- public Unit invoke(View view) {
|
|
|
- for (int j = 0; j < monitorInfoList.size(); j++) {
|
|
|
- MonitorInfo monitorInfo = monitorInfoList.get(j);
|
|
|
- if (monitorInfo.getTextureView() == view) {
|
|
|
- monitorDialog = new MonitorDialog(MonitorActivity.this, monitorInfo, new DialogInterface.OnDismissListener() {
|
|
|
+ public void onEventCallback(NodePlayer player, int event, String msg) {
|
|
|
+ {
|
|
|
+ if (event == 1000 || event == 1001 || event == 2000 || event == 2001) {
|
|
|
+ runOnUiThread(new Runnable() {
|
|
|
@Override
|
|
|
- public void onDismiss(DialogInterface dialog) {
|
|
|
- dialog.dismiss();
|
|
|
- monitorDialog = null;
|
|
|
+ public void run() {
|
|
|
+ if (finalLoadingText.getVisibility() == View.VISIBLE) {
|
|
|
+ finalLoadingText.setVisibility(View.GONE);
|
|
|
+ loadingCounter.incrementAndGet();
|
|
|
+ if (loadingCounter.get() == totalPlayers) {
|
|
|
+ LogUtils.d("所有播放器加载完成");
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
});
|
|
|
- monitorDialog.show();
|
|
|
- break;
|
|
|
}
|
|
|
}
|
|
|
- return null;
|
|
|
- }
|
|
|
- }));
|
|
|
-
|
|
|
- HatomPlayer hatomPlayer = new DefaultHatomPlayer();
|
|
|
- PlayConfig playConfig = new PlayConfig();
|
|
|
- playConfig.hardDecode = true;
|
|
|
- playConfig.privateData = true;
|
|
|
- hatomPlayer.setPlayConfig(playConfig);
|
|
|
- hatomPlayer.setPlayStatusCallback(new PlayCallback.PlayStatusCallback() {
|
|
|
- @Override
|
|
|
- public void onPlayerStatus(@NonNull PlayCallback.Status status, String s) {
|
|
|
- LogUtils.d(status, s);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- monitorInfo.setTextureView(textureView);
|
|
|
- monitorInfo.setMonitorVo(monitorVo);
|
|
|
- monitorInfo.setHatomPlayer(hatomPlayer);
|
|
|
-
|
|
|
- monitorInfoList.add(monitorInfo);
|
|
|
+ // 快速超时机制
|
|
|
+ mainHandler.postDelayed(new Runnable() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ if (finalLoadingText != null && finalLoadingText.getVisibility() == View.VISIBLE) {
|
|
|
+ finalLoadingText.setText("正在连接");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, 500);
|
|
|
|
|
|
- binding.gridLayout.addView(textureView);
|
|
|
+ mainHandler.postDelayed(new Runnable() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ if (finalLoadingText != null && finalLoadingText.getVisibility() == View.VISIBLE) {
|
|
|
+ finalLoadingText.setVisibility(View.GONE);
|
|
|
+ loadingCounter.incrementAndGet();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, 1500);
|
|
|
}
|
|
|
-
|
|
|
- dismissLoading();
|
|
|
}
|
|
|
});
|
|
|
} else {
|
|
|
@@ -207,34 +277,317 @@ public class MonitorActivity extends BaseCountDownActivity<ActivityMonitorBindin
|
|
|
public void accept(Throwable throwable) throws Throwable {
|
|
|
dismissLoading();
|
|
|
LogUtils.e(Log.getStackTraceString(throwable));
|
|
|
+ showToast("加载监控失败");
|
|
|
}
|
|
|
}));
|
|
|
}
|
|
|
|
|
|
- private void play(HatomPlayer hatomPlayer, SurfaceTexture surfaceTexture, String url) {
|
|
|
- ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Object>() {
|
|
|
+ /**
|
|
|
+ * 显示全屏Dialog
|
|
|
+ */
|
|
|
+ private void showFullScreenDialog(final MonitorInfo monitorInfo) {
|
|
|
+ if (isExiting || isDialogShowing || monitorInfo == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ isDialogShowing = true;
|
|
|
+
|
|
|
+ // 先暂停小窗口播放(避免切换时的闪烁)
|
|
|
+ NodePlayer player = monitorInfo.getNodePlayer();
|
|
|
+ if (player != null) {
|
|
|
+ try {
|
|
|
+ player.pause();
|
|
|
+ } catch (Exception e) {
|
|
|
+ LogUtils.e("暂停播放失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ monitorDialog = new MonitorDialog(MonitorActivity.this, monitorInfo, new DialogInterface.OnDismissListener() {
|
|
|
@Override
|
|
|
- public Object doInBackground() throws Throwable {
|
|
|
- hatomPlayer.setSurfaceTexture(surfaceTexture);
|
|
|
- hatomPlayer.setDataSource(url, null);
|
|
|
- hatomPlayer.changeStream(Quality.SUB_STREAM_LOW);
|
|
|
- hatomPlayer.start();
|
|
|
- return null;
|
|
|
+ public void onDismiss(DialogInterface dialog) {
|
|
|
+ LogUtils.d("Dialog已关闭,恢复小窗口播放");
|
|
|
+
|
|
|
+ // 延迟恢复小窗口播放
|
|
|
+ mainHandler.postDelayed(new Runnable() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ restoreSmallWindow(monitorInfo);
|
|
|
+ }
|
|
|
+ }, 100);
|
|
|
+
|
|
|
+ isDialogShowing = false;
|
|
|
+ monitorDialog = null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 显示Dialog
|
|
|
+ try {
|
|
|
+ monitorDialog.show();
|
|
|
+ } catch (Exception e) {
|
|
|
+ LogUtils.e("显示Dialog失败: " + e.getMessage());
|
|
|
+ isDialogShowing = false;
|
|
|
+ monitorDialog = null;
|
|
|
+
|
|
|
+ // 失败时恢复小窗口
|
|
|
+ restoreSmallWindow(monitorInfo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 恢复小窗口播放
|
|
|
+ */
|
|
|
+ private void restoreSmallWindow(MonitorInfo monitorInfo) {
|
|
|
+ if (monitorInfo == null || isExiting) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ NodePlayer player = monitorInfo.getNodePlayer();
|
|
|
+ NodePlayerView originalView = monitorInfo.getNodePlayerView();
|
|
|
+
|
|
|
+ if (player != null && originalView != null) {
|
|
|
+ try {
|
|
|
+ // 确保播放器视图是原始小窗口视图
|
|
|
+ player.setPlayerView(originalView);
|
|
|
+
|
|
|
+ // 延迟后开始播放
|
|
|
+ mainHandler.postDelayed(new Runnable() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ if (!isExiting) {
|
|
|
+ try {
|
|
|
+ player.start();
|
|
|
+ LogUtils.d("小窗口播放已恢复");
|
|
|
+ } catch (Exception e) {
|
|
|
+ LogUtils.e("恢复播放失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, 200);
|
|
|
+ } catch (Exception e) {
|
|
|
+ LogUtils.e("恢复小窗口视图失败: " + e.getMessage());
|
|
|
}
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 安全退出Activity
|
|
|
+ */
|
|
|
+ private void safeExitActivity() {
|
|
|
+ if (isExiting) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ isExiting = true;
|
|
|
+
|
|
|
+ LogUtils.d("开始安全退出流程");
|
|
|
+ showLoading("正在退出...");
|
|
|
+
|
|
|
+ // 1. 先关闭Dialog(如果有)
|
|
|
+ if (isDialogShowing && monitorDialog != null && monitorDialog.isShowing()) {
|
|
|
+ LogUtils.d("正在关闭全屏Dialog");
|
|
|
+ monitorDialog.dismiss();
|
|
|
+ monitorDialog = null;
|
|
|
+ isDialogShowing = false;
|
|
|
+
|
|
|
+ // 等待Dialog完全关闭
|
|
|
+ mainHandler.postDelayed(new Runnable() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ executeExitProcedure();
|
|
|
+ }
|
|
|
+ }, 300);
|
|
|
+ } else {
|
|
|
+ // 直接执行退出流程
|
|
|
+ executeExitProcedure();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行退出流程
|
|
|
+ */
|
|
|
+ private void executeExitProcedure() {
|
|
|
+ LogUtils.d("执行退出流程");
|
|
|
+
|
|
|
+ // 1. 停止所有播放器
|
|
|
+ stopAllPlayers();
|
|
|
|
|
|
+ // 2. 清理UI并释放资源
|
|
|
+ runOnUiThread(new Runnable() {
|
|
|
@Override
|
|
|
- public void onSuccess(Object result) {
|
|
|
+ public void run() {
|
|
|
+ try {
|
|
|
+ // 清理网格布局
|
|
|
+ binding.gridLayout.removeAllViews();
|
|
|
+
|
|
|
+ // 释放播放器资源
|
|
|
+ releaseAllPlayers();
|
|
|
+
|
|
|
+ dismissLoading();
|
|
|
+
|
|
|
+ // 延迟finish确保资源释放完成
|
|
|
+ mainHandler.postDelayed(new Runnable() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ if (!isFinishing()) {
|
|
|
+ finish();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, 200);
|
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
+ LogUtils.e("退出流程异常: " + e.getMessage());
|
|
|
+ // 即使异常也要finish
|
|
|
+ dismissLoading();
|
|
|
+ if (!isFinishing()) {
|
|
|
+ finish();
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 停止所有播放器
|
|
|
+ */
|
|
|
+ private void stopAllPlayers() {
|
|
|
+ if (monitorInfoList != null) {
|
|
|
+ for (MonitorInfo monitorInfo : monitorInfoList) {
|
|
|
+ NodePlayer nodePlayer = monitorInfo.getNodePlayer();
|
|
|
+ if (nodePlayer != null) {
|
|
|
+ try {
|
|
|
+ nodePlayer.stop();
|
|
|
+ LogUtils.d("停止播放器");
|
|
|
+ } catch (Exception e) {
|
|
|
+ LogUtils.e("停止播放器失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 释放所有播放器资源
|
|
|
+ */
|
|
|
+ private void releaseAllPlayers() {
|
|
|
+ LogUtils.d("开始释放播放器资源");
|
|
|
+ if (monitorInfoList != null) {
|
|
|
+ for (MonitorInfo monitorInfo : monitorInfoList) {
|
|
|
+ NodePlayer nodePlayer = monitorInfo.getNodePlayer();
|
|
|
+ if (nodePlayer != null) {
|
|
|
+ try {
|
|
|
+ nodePlayer.release();
|
|
|
+ monitorInfo.setNodePlayer(null);
|
|
|
+ LogUtils.d("释放播放器成功");
|
|
|
+ } catch (Exception e) {
|
|
|
+ LogUtils.e("释放播放器失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清理其他引用
|
|
|
+ monitorInfo.setNodePlayerView(null);
|
|
|
+ monitorInfo.setContainerView(null);
|
|
|
+ monitorInfo.setLoadingText(null);
|
|
|
+ monitorInfo.setMonitorVo(null);
|
|
|
+ }
|
|
|
+ monitorInfoList.clear();
|
|
|
+ }
|
|
|
+ LogUtils.d("资源释放完成");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected void onResume() {
|
|
|
+ super.onResume();
|
|
|
+ LogUtils.d("onResume - isExiting:" + isExiting);
|
|
|
+
|
|
|
+ // 恢复播放(如果不在退出状态)
|
|
|
+ if (!isExiting && monitorInfoList != null) {
|
|
|
+ // 只恢复前几个播放器,避免同时启动
|
|
|
+ int count = Math.min(monitorInfoList.size(), 4);
|
|
|
+ for (int i = 0; i < count; i++) {
|
|
|
+ MonitorInfo monitorInfo = monitorInfoList.get(i);
|
|
|
+ NodePlayer nodePlayer = monitorInfo.getNodePlayer();
|
|
|
+ if (nodePlayer != null) {
|
|
|
+ try {
|
|
|
+ // 第一个立即恢复,其他的延迟一点
|
|
|
+ if (i == 0) {
|
|
|
+ nodePlayer.start();
|
|
|
+ } else {
|
|
|
+ final int index = i;
|
|
|
+ mainHandler.postDelayed(new Runnable() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ if (!isExiting && index < monitorInfoList.size()) {
|
|
|
+ NodePlayer player = monitorInfoList.get(index).getNodePlayer();
|
|
|
+ if (player != null) {
|
|
|
+ try {
|
|
|
+ player.start();
|
|
|
+ } catch (Exception e) {
|
|
|
+ LogUtils.e("恢复播放失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, i * 100);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ LogUtils.e("恢复播放失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected void onPause() {
|
|
|
+ super.onPause();
|
|
|
+ LogUtils.d("onPause - isExiting:" + isExiting);
|
|
|
+
|
|
|
+ // 暂停播放但不停止(如果不在退出状态且没有显示Dialog)
|
|
|
+ if (!isExiting && !isDialogShowing && monitorInfoList != null) {
|
|
|
+ for (int i = 0; i < monitorInfoList.size(); i++) {
|
|
|
+ MonitorInfo monitorInfo = monitorInfoList.get(i);
|
|
|
+ NodePlayer nodePlayer = monitorInfo.getNodePlayer();
|
|
|
+ if (null != nodePlayer) {
|
|
|
+ try {
|
|
|
+ nodePlayer.pause();
|
|
|
+ } catch (Exception e) {
|
|
|
+ LogUtils.e("暂停播放失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
protected void onDestroy() {
|
|
|
+ LogUtils.d("onDestroy - 开始清理资源");
|
|
|
+ isExiting = true;
|
|
|
+ isDialogShowing = false;
|
|
|
+
|
|
|
+ // 清理Handler
|
|
|
+ if (mainHandler != null) {
|
|
|
+ mainHandler.removeCallbacksAndMessages(null);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 关闭Dialog
|
|
|
+ if (monitorDialog != null && monitorDialog.isShowing()) {
|
|
|
+ try {
|
|
|
+ monitorDialog.dismiss();
|
|
|
+ } catch (Exception e) {
|
|
|
+ LogUtils.e("关闭Dialog失败: " + e.getMessage());
|
|
|
+ }
|
|
|
+ monitorDialog = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 释放所有资源
|
|
|
+ releaseAllPlayers();
|
|
|
+
|
|
|
+ // 清理Fragment
|
|
|
Fragment fragment = getSupportFragmentManager().findFragmentByTag("video_dialog");
|
|
|
if (fragment instanceof VideoDialogFragment) {
|
|
|
((VideoDialogFragment) fragment).dismissAllowingStateLoss();
|
|
|
}
|
|
|
+
|
|
|
super.onDestroy();
|
|
|
+ LogUtils.d("onDestroy - 资源清理完成");
|
|
|
}
|
|
|
}
|