|
|
@@ -1,5 +1,7 @@
|
|
|
package com.example.jhpapp;
|
|
|
import androidx.annotation.RequiresApi;
|
|
|
+import androidx.lifecycle.LifecycleEventObserver;
|
|
|
+
|
|
|
import android.annotation.SuppressLint;
|
|
|
import android.content.Context;
|
|
|
import android.content.SharedPreferences;
|
|
|
@@ -35,15 +37,64 @@ import pl.droidsonroids.gif.GifImageView;
|
|
|
|
|
|
public class MainActivity extends BaseActivity {
|
|
|
|
|
|
- private static Context context;
|
|
|
- static WebView webIDs;
|
|
|
+ private WebView webIDs;
|
|
|
+ private Context context;
|
|
|
Button call_Js,but1,but02,but2,but3;
|
|
|
TextView js_info;
|
|
|
- static TextView textValue;
|
|
|
+ TextView textValue;
|
|
|
GifImageView webViewLoading;
|
|
|
|
|
|
- //播放提示音模块使用
|
|
|
- private SimpleExoPlayer player;
|
|
|
+ // ❷ 播放模块:全局监听器(避免重复创建)+ volatile保障线程可见性
|
|
|
+ private volatile SimpleExoPlayer player;
|
|
|
+ private final Player.Listener globalPlayerListener = new Player.Listener() {
|
|
|
+ @Override
|
|
|
+ public void onPlaybackStateChanged(int playbackState) {
|
|
|
+ switch (playbackState) {
|
|
|
+ case Player.STATE_IDLE:
|
|
|
+ Log.i("mediaplay","状态: 空闲");
|
|
|
+ break;
|
|
|
+ case Player.STATE_BUFFERING:
|
|
|
+ Log.i("mediaplay","状态: 缓冲中");
|
|
|
+ break;
|
|
|
+ case Player.STATE_READY:
|
|
|
+ Log.i("mediaplay","状态: 就绪");
|
|
|
+ // 就绪后自动播放(仅全局监听器处理,避免重复)
|
|
|
+ if (player != null && !player.isPlaying()) {
|
|
|
+ player.play();
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case Player.STATE_ENDED:
|
|
|
+ Log.i("mediaplay","状态: 播放完成");
|
|
|
+ // ❸ 封装通知JS方法,增加Activity状态检查
|
|
|
+ notifyJs("mediaPlayOver", "success");
|
|
|
+ // 播放完成后重置播放器,避免下次播放异常
|
|
|
+ if (player != null) {
|
|
|
+ player.stop();
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onPlayerError(PlaybackException error) {
|
|
|
+ Log.e("mediaplay", "播放错误: " + error.getMessage());
|
|
|
+ switch (error.errorCode) {
|
|
|
+ case PlaybackException.ERROR_CODE_DECODING_FAILED:
|
|
|
+ Log.e("mediaplay", "解码失败");
|
|
|
+ break;
|
|
|
+ case PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED:
|
|
|
+ Log.e("mediaplay", "网络连接失败");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ // ❹ 播放错误通知前端JS
|
|
|
+ notifyJs("mediaPlayOver", "error");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onIsPlayingChanged(boolean isPlaying) {
|
|
|
+ Log.i("mediaplay","播放状态: " + isPlaying);
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
//项目安装包下载链接
|
|
|
public static final String APP_APKDOWN = "https://**?type=apk&target=selfprint";
|
|
|
@@ -53,7 +104,33 @@ public class MainActivity extends BaseActivity {
|
|
|
SharedPreferences.Editor mEditor;
|
|
|
|
|
|
|
|
|
- private Handler handlerWebErr = new Handler();
|
|
|
+ // ❺ 绑定生命周期的Handler(避免泄漏)
|
|
|
+ private Handler handlerWebErr;
|
|
|
+
|
|
|
+ // ❻ 生命周期观察者(管理播放器暂停/恢复)
|
|
|
+ private final LifecycleEventObserver lifecycleObserver = (source, event) -> {
|
|
|
+ if (player == null) return;
|
|
|
+ switch (event) {
|
|
|
+ case ON_PAUSE:
|
|
|
+ // 后台暂停播放
|
|
|
+ if (player.isPlaying()) {
|
|
|
+ player.pause();
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case ON_RESUME:
|
|
|
+ // 前台恢复播放(仅就绪状态)
|
|
|
+ if (player.getPlaybackState() == Player.STATE_READY && !player.isPlaying()) {
|
|
|
+ player.play();
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case ON_DESTROY:
|
|
|
+ // 销毁时清理Handler
|
|
|
+ if (handlerWebErr != null) {
|
|
|
+ handlerWebErr.removeCallbacksAndMessages(null);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
|
|
@SuppressLint({"SetJavaScriptEnabled", "JavascriptInterface"})
|
|
|
@@ -63,6 +140,12 @@ public class MainActivity extends BaseActivity {
|
|
|
|
|
|
context = this;
|
|
|
|
|
|
+ // ❶ 绑定生命周期观察者
|
|
|
+ getLifecycle().addObserver(lifecycleObserver);
|
|
|
+
|
|
|
+ // 初始化Handler(与主线程绑定,避免泄漏)
|
|
|
+ handlerWebErr = new Handler(Looper.getMainLooper());
|
|
|
+
|
|
|
//初始化
|
|
|
init();
|
|
|
|
|
|
@@ -128,7 +211,7 @@ public class MainActivity extends BaseActivity {
|
|
|
webIDs.addJavascriptInterface(this,"JavaClientCall");
|
|
|
webIDs.setWebViewClient(webViewClient);
|
|
|
webIDs.loadUrl("http://10.1.34.29:8090/callapp/");
|
|
|
-// webIDs.loadUrl("https://172.16.1.6/callapp/");
|
|
|
+// webIDs.loadUrl("http://10.1.19.20:8100");
|
|
|
// webIDs.loadUrl("https://np.h03.p0551.com/callapp/");
|
|
|
|
|
|
|
|
|
@@ -157,7 +240,6 @@ public class MainActivity extends BaseActivity {
|
|
|
// case R.id.but2:
|
|
|
// break;
|
|
|
case R.id.but3:
|
|
|
-
|
|
|
// mediaFilePlay("http://10.1.34.29:8090/images29/wav/ffd029b9983de18e2017042a3d7c393b.wav");
|
|
|
// mediaFilePlay("https://np.h03.p0551.com/images/wav/eba7ea72748b33c07d55ca7c443d6146.wav");
|
|
|
break;
|
|
|
@@ -230,52 +312,28 @@ public class MainActivity extends BaseActivity {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+
|
|
|
public void initMedAudio(){
|
|
|
- // 创建ExoPlayer实例
|
|
|
- player = new SimpleExoPlayer.Builder(this).build();
|
|
|
+ if (player == null) {
|
|
|
+ player = new SimpleExoPlayer.Builder(this).build();
|
|
|
+ // 仅添加一次全局监听器
|
|
|
+ player.addListener(globalPlayerListener);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // 监听播放状态
|
|
|
- player.addListener(new Player.Listener() {
|
|
|
- @Override
|
|
|
- public void onPlaybackStateChanged(int playbackState) {
|
|
|
- switch (playbackState) {
|
|
|
- case Player.STATE_IDLE:
|
|
|
- Log.i("mediaplay","状态: 空闲");
|
|
|
- break;
|
|
|
- case Player.STATE_BUFFERING:
|
|
|
- Log.i("mediaplay","状态: 缓冲中");
|
|
|
- break;
|
|
|
- case Player.STATE_READY:
|
|
|
- Log.i("mediaplay","状态: 就绪");
|
|
|
- break;
|
|
|
- case Player.STATE_ENDED:
|
|
|
- Log.i("mediaplay","状态: 播放完成");
|
|
|
- String status = "success";
|
|
|
- runOnUiThread(new Runnable() {
|
|
|
- @Override
|
|
|
- public void run() {
|
|
|
- webIDs.loadUrl("javascript:mediaPlayOver('"+ status +"')");
|
|
|
- }
|
|
|
- });
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- @Override
|
|
|
- public void onPlayerError(PlaybackException error) {
|
|
|
- Log.e("mediaplay", "播放错误: " + error.getMessage());
|
|
|
- switch (error.errorCode) {
|
|
|
- case PlaybackException.ERROR_CODE_DECODING_FAILED:
|
|
|
- Log.e("mediaplay", "解码失败");
|
|
|
- break;
|
|
|
- case PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED:
|
|
|
- Log.e("mediaplay", "网络连接失败");
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- @Override
|
|
|
- public void onIsPlayingChanged(boolean isPlaying) {
|
|
|
- // 播放状态处理代码保持不变
|
|
|
- Log.i("mediaplay","播放状态: " +isPlaying);
|
|
|
+ // ❷ 封装通知JS方法(统一处理线程+状态检查)
|
|
|
+ private void notifyJs(String method, String param) {
|
|
|
+ // 检查Activity状态,避免销毁后调用
|
|
|
+ if (isFinishing() || isDestroyed() || webIDs == null) {
|
|
|
+ Log.w("mediaplay", "Activity已销毁,跳过JS通知");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 确保在主线程执行
|
|
|
+ runOnUiThread(() -> {
|
|
|
+ try {
|
|
|
+ webIDs.loadUrl("javascript:" + method + "('" + param + "')");
|
|
|
+ } catch (Exception e) {
|
|
|
+ Log.e("mediaplay", "通知JS失败: " + e.getMessage());
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
@@ -300,41 +358,51 @@ public class MainActivity extends BaseActivity {
|
|
|
// 播放音频文件
|
|
|
@JavascriptInterface
|
|
|
public void mediaFilePlay(String audioInfo){
|
|
|
- // 设置本地媒体源
|
|
|
-// int resId = R.raw.dwgm;
|
|
|
-// Uri uri = Uri.parse("android.resource://" + context.getPackageName() + "/" + resId);
|
|
|
-// MediaItem mediaItem = MediaItem.fromUri(uri);
|
|
|
|
|
|
- MediaItem mediaItem = MediaItem.fromUri(audioInfo);
|
|
|
- // 检查播放器是否初始化
|
|
|
- if (player == null) {
|
|
|
- initMedAudio();
|
|
|
+ if (audioInfo == null || audioInfo.trim().isEmpty()) {
|
|
|
+ Log.e("mediaplay", "音频Uri为空");
|
|
|
+ notifyJs("mediaPlayOver", "error");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ❸ 切换到主线程操作播放器(使用ExoPlayer工具类判断主线程)
|
|
|
+ // 改用原生API判断主线程(无依赖风险)
|
|
|
+ if (Looper.getMainLooper() == Looper.myLooper()) {
|
|
|
+ doPlayAudio(audioInfo);
|
|
|
+ } else {
|
|
|
+ new Handler(Looper.getMainLooper()).post(() -> doPlayAudio(audioInfo));
|
|
|
}
|
|
|
- // 使用Handler确保在主线程调用
|
|
|
- Handler mainHandler = new Handler(Looper.getMainLooper());
|
|
|
- mainHandler.post(new Runnable() {
|
|
|
- @Override
|
|
|
- public void run() {
|
|
|
- player.setMediaItem(mediaItem);
|
|
|
- // 准备播放器
|
|
|
- player.prepare();
|
|
|
- // 不立即调用play(),而是等待STATE_READY状态
|
|
|
- player.addListener(new Player.Listener() {
|
|
|
- @Override
|
|
|
- public void onPlaybackStateChanged(int state) {
|
|
|
- if (state == Player.STATE_READY) {
|
|
|
- // 播放器准备就绪,此时可以安全播放
|
|
|
- player.play();
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
}
|
|
|
/**
|
|
|
* 以上方法提供给webview中的JS调用————————————————————————————————————————————————————————————————————end
|
|
|
*/
|
|
|
|
|
|
+ // ❹ 实际播放逻辑(主线程执行)
|
|
|
+ private void doPlayAudio(String audioInfo) {
|
|
|
+ // 双重检查播放器是否初始化
|
|
|
+ if (player == null) {
|
|
|
+ initMedAudio();
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ // 重置播放器:清空旧媒体源,避免冲突
|
|
|
+ player.stop();
|
|
|
+ player.clearMediaItems();
|
|
|
+
|
|
|
+ // 设置本地媒体源
|
|
|
+// int resId = R.raw.dwgm;
|
|
|
+// Uri uri = Uri.parse("android.resource://" + context.getPackageName() + "/" + resId);
|
|
|
+// MediaItem mediaItem = MediaItem.fromUri(uri);
|
|
|
+
|
|
|
+ // 设置新媒体源并准备
|
|
|
+ MediaItem mediaItem = MediaItem.fromUri(audioInfo);
|
|
|
+ player.setMediaItem(mediaItem);
|
|
|
+ player.prepare();
|
|
|
+ } catch (Exception e) {
|
|
|
+ Log.e("mediaplay", "播放准备失败: " + e.getMessage());
|
|
|
+ notifyJs("mediaPlayOver", "error");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
/**
|
|
|
* 重写按键操作处理方法
|
|
|
@@ -353,9 +421,27 @@ public class MainActivity extends BaseActivity {
|
|
|
protected void onDestroy() {
|
|
|
// TODO Auto-generated method stub
|
|
|
super.onDestroy();
|
|
|
+ // 1. 移除生命周期观察者
|
|
|
+ getLifecycle().removeObserver(lifecycleObserver);
|
|
|
+
|
|
|
+ // 2. 释放播放器(核心:移除监听器+释放+置空)
|
|
|
if (player != null) {
|
|
|
- player.release(); // 释放所有资源
|
|
|
- player = null; // 置空引用,避免误操作
|
|
|
+ player.removeListener(globalPlayerListener); // 移除监听器,切断引用
|
|
|
+ player.release();
|
|
|
+ player = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 清理Handler,避免延迟任务执行
|
|
|
+ if (handlerWebErr != null) {
|
|
|
+ handlerWebErr.removeCallbacksAndMessages(null);
|
|
|
+ handlerWebErr = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 释放WebView(避免泄漏)
|
|
|
+ if (webIDs != null) {
|
|
|
+ webIDs.removeAllViews();
|
|
|
+ webIDs.destroy();
|
|
|
+ webIDs = null;
|
|
|
}
|
|
|
}
|
|
|
}
|