Browse Source

优化统一播放器服务和入口能力,使用微任务调度提升响应速度,增强防抖保护,避免重复更新,确保状态一致性,改善用户体验。

chendeben 1 year ago
parent
commit
2f1975ab77

+ 64 - 20
entry/src/main/ets/common/service/UnifiedPlayerService.ets

@@ -485,6 +485,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
   // 图片缓存相关属性
   private imageCacheDir: string = ''; // 图片缓存目录
   private lastImageCache: ImageCacheInfo | null = null; // 图片缓存优化
+  private lastFormUpdateTime: number = 0; // 上次卡片更新时间,用于防抖
   private readonly CACHE_EXPIRY_DAYS: number = 7; // 缓存有效期:7天
   private readonly MAX_CACHE_SIZE_MB: number = 50; // 最大缓存大小:50MB
   private hasChecked: boolean=false;
@@ -1065,13 +1066,13 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
       // 更新播放列表状态
       await this.updatePlaylistStateInModel();
 
-      // 优化:减少延迟,立即更新卡片状态,避免重复更新AVSession
-      setTimeout(async () => {
+      // 优化:立即更新卡片状态,使用微任务调度避免阻塞
+      Promise.resolve().then(async () => {
         try {
           // 确保播放列表状态是最新的
           this.updatePlaylistStateInModelSync();
 
-          // 强制更新所有桌面卡片状态
+          // 强制更新所有桌面卡片状态(微任务优先级高于宏任务)
           await this.updateAllForms(true);
           
           // 移除此处的AVSession更新,避免与onPrepared中的更新冲突
@@ -1081,7 +1082,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
         } catch (error) {
           LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to update widgets after track change: ${error}`);
         }
-      }, 50); // 减少延迟到50ms,提高响应速度
+      });
 
     } catch (error) {
       await this.handlePlaybackError(error as Error, PlayerErrorType.PLAYBACK_ERROR);
@@ -2052,6 +2053,9 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
     }
   }
 
+  // 缓存播放器是否已初始化配置,避免重复设置
+  private isPlayerConfigured: boolean = false;
+
   private async setupPlayerForSong(song: VideoItem): Promise<void> {
     const ijkPlayer = this.playerManager.getIjkPlayer();
     if (!ijkPlayer) {
@@ -2070,8 +2074,14 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
     ijkPlayer.setAudioId('unifiedPlayer');
     LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Reset audio mode for ${song.name}`);
 
-    // 重新设置配置
-    this.playerManager.setupIjkPlayerOptions();
+    // 优化:只在首次或配置变更时重新设置所有选项
+    if (!this.isPlayerConfigured) {
+      this.playerManager.setupIjkPlayerOptions();
+      this.isPlayerConfigured = true;
+    } else {
+      // 快速重置关键配置项,避免完整重新配置
+      this.setupEssentialPlayerOptions();
+    }
 
     // 设置音量(针对dsf格式特殊处理)
     const volume = this.stateModel.getState().volume;
@@ -2085,16 +2095,19 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
     ijkPlayer.setDataSource(song.filePath);
     LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Set data source to ${song.filePath}`);
 
-    // 设置HTTP请求头
-    const headers = new Map([
-      ["user_agent", "Mozilla/5.0 BiliDroid/7.30.0 (bbcallen@gmail.com)"],
-      ["referer", "https://www.bilibili.com"]
-    ]);
-    ijkPlayer.setDataSourceHeader(headers);
+    // 优化:异步设置HTTP请求头和播放速度,不阻塞主流程
+    Promise.resolve().then(() => {
+      // 设置HTTP请求头
+      const headers = new Map([
+        ["user_agent", "Mozilla/5.0 BiliDroid/7.30.0 (bbcallen@gmail.com)"],
+        ["referer", "https://www.bilibili.com"]
+      ]);
+      ijkPlayer.setDataSourceHeader(headers);
 
-    // 设置播放速度
-    const speed = this.stateModel.getState().speed;
-    this.playerManager.setPlaybackSpeed(speed.toString() + 'f');
+      // 设置播放速度
+      const speed = this.stateModel.getState().speed;
+      this.playerManager.setPlaybackSpeed(speed.toString() + 'f');
+    });
 
     // 设置消息监听器(重要:用于处理播放器内部事件)
     ijkPlayer.setMessageListener();
@@ -2103,6 +2116,19 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
     console.log("Heanup2 UnifiedPlayerService: 播放器重新设置完成,准备开始新的播放");
   }
 
+  /**
+   * 快速设置关键播放器选项,避免完整重新配置的性能开销
+   */
+  private setupEssentialPlayerOptions(): void {
+    const ijkPlayer = this.playerManager.getIjkPlayer();
+    if (!ijkPlayer) return;
+
+    // 仅设置切换歌曲时必须重置的关键选项
+    ijkPlayer.native_setup(); // 音频模式初始化
+    ijkPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", "0");
+    ijkPlayer.setScreenOnWhilePlaying(true);
+  }
+
   private setupProgressTimer(): void {
     this.progressTimer = setInterval(() => {
       this.updateProgress();
@@ -2726,7 +2752,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
   }
 
   /**
-   * 更新状态模型中的播放列表状态(同步版本)
+   * 更新状态模型中的播放列表状态(同步版本)- 优化防抖
    */
   private updatePlaylistStateInModelSync(): void {
     try {
@@ -2737,10 +2763,19 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
       const hasNext = this.playlistModel.hasNext(playMode);
       const hasPrevious = this.playlistModel.hasPreviousSync(playMode);
 
-      this.stateModel.updatePlaylistState(hasNext, hasPrevious, totalCount);
-
-      LogUtils.getInstance()
-        .LOGI(`Heanup UnifiedPlayerService: Playlist state updated sync - hasNext=${hasNext}, hasPrevious=${hasPrevious}, totalCount=${totalCount}, playMode=${playMode}`);
+      // 检查状态是否真的发生了变化,避免频繁的无效更新
+      const currentState = this.stateModel.getState();
+      const stateChanged = (
+        currentState.hasNext !== hasNext ||
+        currentState.hasPrevious !== hasPrevious ||
+        currentState.totalCount !== totalCount
+      );
+
+      if (stateChanged) {
+        this.stateModel.updatePlaylistState(hasNext, hasPrevious, totalCount);
+        LogUtils.getInstance()
+          .LOGI(`Heanup UnifiedPlayerService: Playlist state updated sync - hasNext=${hasNext}, hasPrevious=${hasPrevious}, totalCount=${totalCount}, playMode=${playMode}`);
+      }
     } catch (error) {
       LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService updatePlaylistStateInModelSync error: ${error}`);
     }
@@ -3003,11 +3038,20 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
    * 将VideoItem和PlayerState数据传入桌面卡片,控制卡片的显示
    */
   async updateAllForms(forceUpdate: boolean = false) {
+    // 增强防抖保护,特别是在APP前台运行时
+    const now = Date.now();
+    if (!forceUpdate && this.lastFormUpdateTime && (now - this.lastFormUpdateTime < 200)) {
+      LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: Skipping due to debounce protection (200ms)');
+      return;
+    }
+    
     // 防抖和重入保护,防止短时间内重复执行更新
     if (this.isUpdatingForms && !forceUpdate) {
       LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: Update already in progress, skipping.');
       return;
     }
+    
+    this.lastFormUpdateTime = now;
 
     try {
       this.isUpdatingForms = true;

+ 17 - 15
entry/src/main/ets/entryability/EntryAbility.ets

@@ -448,12 +448,12 @@ export default class EntryAbility extends UIAbility {
             }
           }
 
-          // 优化:立即返回成功状态,异步处理业务逻辑
-          setTimeout(() => {
+          // 优化:立即返回成功状态,使用异步微任务优先处理业务逻辑  
+          Promise.resolve().then(() => {
             this.sendWidgetControlEvent('PLAY_PAUSE', params).catch((error: Error) => {
               hilog.error(0x0000, 'Heanup2', `❌ playPause async error: ${error}`);
             });
-          }, 0);
+          });
 
           return new MyParcelable(1, 'playPause_success');
         } catch (error) {
@@ -479,12 +479,12 @@ export default class EntryAbility extends UIAbility {
             }
           }
 
-          // 优化:立即返回成功状态,异步处理业务逻辑
-          setTimeout(() => {
+          // 优化:立即返回成功状态,使用异步微任务优先处理业务逻辑
+          Promise.resolve().then(() => {
             this.sendWidgetControlEvent('NEXT_SONG', params).catch((error: Error) => {
               hilog.error(0x0000, 'Heanup2', `❌ nextSong async error: ${error}`);
             });
-          }, 0);
+          });
 
           return new MyParcelable(2, 'nextSong_success');
         } catch (error) {
@@ -509,12 +509,12 @@ export default class EntryAbility extends UIAbility {
             }
           }
 
-          // 优化:立即返回成功状态,异步处理业务逻辑
-          setTimeout(() => {
+          // 优化:立即返回成功状态,使用异步微任务优先处理业务逻辑
+          Promise.resolve().then(() => {
             this.sendWidgetControlEvent('PREV_SONG', params).catch((error: Error) => {
               hilog.error(0x0000, 'Heanup2', `❌ prevSong async error: ${error}`);
             });
-          }, 0);
+          });
 
           return new MyParcelable(3, 'prevSong_success');
         } catch (error) {
@@ -628,12 +628,14 @@ export default class EntryAbility extends UIAbility {
       // 执行命令
       await commandPromise;
       
-      // 优化:异步广播状态更新,不阻塞命令执行
-      setTimeout(() => {
-        this.unifiedService.broadcastCurrentState().catch((error: Error) => {
-          hilog.error(0x0000, 'Heanup2', `❌ 状态广播失败: ${error.message}`);
-        });
-      }, 0);
+      // 优化:使用低优先级异步广播,避免阻塞主线程
+      Promise.resolve().then(() => {
+        setTimeout(() => {
+          this.unifiedService.broadcastCurrentState().catch((error: Error) => {
+            hilog.error(0x0000, 'Heanup2', `❌ 状态广播失败: ${error.message}`);
+          });
+        }, 50); // 小延迟确保命令执行完成
+      });
       
       hilog.info(0x0000, 'Heanup2', `✅ Widget命令执行完成: ${command}`);
 

+ 31 - 18
entry/src/main/ets/view/LocalMusic.ets

@@ -940,25 +940,29 @@ export struct LocalMusic {
 
   /**
    * 页面显示时同步播放状态,修复音频中断后的状态不一致问题
+   * 优化:使用异步执行,避免阻塞页面显示
    */
   private syncPlaybackStateOnShow(): void {
-    try {
-      const currentState = this.unifiedPlayerService.getCurrentState();
-      const actualPlaying = this.unifiedPlayerService.getActualPlayingState();
+    // 异步执行状态同步,避免阻塞页面显示
+    Promise.resolve().then(() => {
+      try {
+        const currentState = this.unifiedPlayerService.getCurrentState();
+        const actualPlaying = this.unifiedPlayerService.getActualPlayingState();
 
-      LogUtils.getInstance().LOGI(`onPageShow state sync - StateModel: isPlaying=${currentState.isPlaying}, isPaused=${currentState.isPaused}, Actual: ${actualPlaying}`);
+        LogUtils.getInstance().LOGI(`onPageShow state sync - StateModel: isPlaying=${currentState.isPlaying}, isPaused=${currentState.isPaused}, Actual: ${actualPlaying}`);
 
-      // 如果状态不一致,强制同步
-      if (currentState.isPlaying !== actualPlaying) {
-        LogUtils.getInstance().LOGI(`State inconsistency detected, forcing sync`);
-        this.setIsPlaying(actualPlaying);
-        this.CONTROL_PlayStatus = actualPlaying ? PlayStatus.PLAY : PlayStatus.INIT;
-        this.playChange();
-        this.watchStatus();
+        // 如果状态不一致,强制同步
+        if (currentState.isPlaying !== actualPlaying) {
+          LogUtils.getInstance().LOGI(`State inconsistency detected, forcing sync`);
+          this.setIsPlaying(actualPlaying);
+          this.CONTROL_PlayStatus = actualPlaying ? PlayStatus.PLAY : PlayStatus.INIT;
+          this.playChange();
+          this.watchStatus(); // 现在已优化为异步执行
+        }
+      } catch (error) {
+        LogUtils.getInstance().LOGI(`syncPlaybackStateOnShow error: ${error}`);
       }
-    } catch (error) {
-      LogUtils.getInstance().LOGI(`syncPlaybackStateOnShow error: ${error}`);
-    }
+    });
   }
 
   //开启线程查看各个数据库
@@ -12087,16 +12091,25 @@ export struct LocalMusic {
     return false;
   }
 
+  // 缓存当前屏幕常亮状态,避免重复调用系统API
+  private currentKeepScreenOn: boolean = false;
+
   /**
    * Sets whether the screen is a constant based on the playback status.
+   * 优化:缓存状态,避免频繁的系统API调用
    */
   watchStatus() {
     // let windowClass = GlobalContext.getContext().getObject('windowClass') as window.Window;
     if (this.windowClass) {
-      if (this.CONTROL_PlayStatus === PlayStatus.PLAY) {
-        this.windowClass.setWindowKeepScreenOn(true);
-      } else {
-        this.windowClass.setWindowKeepScreenOn(false);
+      const shouldKeepScreenOn = (this.CONTROL_PlayStatus === PlayStatus.PLAY);
+      
+      // 只有当状态真正改变时才调用系统API
+      if (this.currentKeepScreenOn !== shouldKeepScreenOn) {
+        this.currentKeepScreenOn = shouldKeepScreenOn;
+        // 使用异步调用避免阻塞UI线程
+        Promise.resolve().then(() => {
+          this.windowClass.setWindowKeepScreenOn(shouldKeepScreenOn);
+        });
       }
     }
   }