فهرست منبع

修复播控中心收藏显示异常

chendeben 1 سال پیش
والد
کامیت
3bf30c416f
3فایلهای تغییر یافته به همراه282 افزوده شده و 35 حذف شده
  1. 129 11
      entry/src/main/ets/common/service/UnifiedPlayerService.ets
  2. 12 24
      entry/src/main/ets/view/LocalMusic.ets
  3. 141 0
      收藏功能修复验证.md

+ 129 - 11
entry/src/main/ets/common/service/UnifiedPlayerService.ets

@@ -230,6 +230,16 @@ export interface IPlayerService {
    * 释放资源
    * 释放资源
    */
    */
   release(): void;
   release(): void;
+
+  /**
+   * 更新收藏列表
+   */
+  updateFavoriteList(): Promise<void>;
+
+  /**
+   * 强制刷新收藏状态(用于收藏/取消收藏操作后)
+   */
+  refreshFavoriteStatus(): Promise<void>;
 }
 }
 
 
 /**
 /**
@@ -260,7 +270,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
   private avMetadataUpdateTimer: number = -1; // 新增:元数据更新防抖定时器 
   private avMetadataUpdateTimer: number = -1; // 新增:元数据更新防抖定时器 
   private lastUpdateTime: number=0;
   private lastUpdateTime: number=0;
   private favList: VideoItem[]=[];
   private favList: VideoItem[]=[];
-  table: undefined;
+  private table: MediaTable | undefined = undefined;
   private lastAutoPlayTime: number = 0; // 新增:上次自动播放时间,用于防抖
   private lastAutoPlayTime: number = 0; // 新增:上次自动播放时间,用于防抖
   private autoPlayDebounceMs: number = 1000; // 自动播放防抖间隔(毫秒)
   private autoPlayDebounceMs: number = 1000; // 自动播放防抖间隔(毫秒)
 
 
@@ -378,8 +388,13 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
 
         this.initializeAvSession();
         this.initializeAvSession();
 
 
-        // 完整的数据恢复(从持久化存储)
-        await this.restorePersistedState();
+        // 并行初始化收藏列表和恢复数据,提高效率
+        const initPromises2 = [
+          this.restorePersistedState(),
+          this.initializeFavoriteList()
+        ];
+        
+        await Promise.all(initPromises2);
 
 
         console.log("Heanup2 UnifiedPlayerService: 异步组件初始化完成");
         console.log("Heanup2 UnifiedPlayerService: 异步组件初始化完成");
         LogUtils.getInstance().LOGI('UnifiedPlayerService: Heavy components initialized');
         LogUtils.getInstance().LOGI('UnifiedPlayerService: Heavy components initialized');
@@ -840,16 +855,119 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
     return this.playlistModel.getCurrentSong();
     return this.playlistModel.getCurrentSong();
   }
   }
   getFav(): Array<VideoItem>{
   getFav(): Array<VideoItem>{
+    if (this.favList.length>0){
+      console.log("Heanup UnifiedPlayerService favList cached:"+json.stringify( this.favList));
+      return this.favList;
+    }
+    // 如果缓存为空,异步更新但返回空数组,避免阻塞
     this.getTable().queryByisFav(1, async (result: VideoItem[]) => {
     this.getTable().queryByisFav(1, async (result: VideoItem[]) => {
-      this.favList = result
+      console.log("Heanup UnifiedPlayerService favList:"+json.stringify( result));
+      this.favList = result;
+      // 更新收藏列表后,重新更新AVSession状态以反映正确的收藏状态
+      if (result.length > 0) {
+        setTimeout(() => {
+          this.updateSessionPlayState();
+        }, 100);
+      }
     })
     })
     return this.favList;
     return this.favList;
   }
   }
-  private getTable():MediaTable{
-    if (this.table!=undefined) {
+
+  /**
+   * 获取当前歌曲的收藏状态
+   */
+  private getCurrentSongFavoriteState(): boolean {
+    try {
+      let currentSong = this.playlistModel.getCurrentSong();
+      if (!currentSong) {
+        LogUtils.getInstance().LOGI(`UnifiedPlayerService: 当前歌曲为空`);
+        return false;
+      }
+      
+      let favList = this.getFav();
+      if (!favList || favList.length === 0) {
+        // 如果收藏列表为空,可能还在加载中,先返回false
+        // 但不记录为错误,因为这是正常的初始化状态
+        return false;
+      }
+      
+      // 检查当前歌曲是否在收藏列表中
+      for (let i = 0; i < favList.length; i++) {
+        if (favList[i].filePath === currentSong.filePath && favList[i].isFav === 1) {
+          LogUtils.getInstance().LOGI(`UnifiedPlayerService: 收藏歌曲:${currentSong.filePath}`);
+          return true;
+        }
+      }
+      LogUtils.getInstance().LOGI(`UnifiedPlayerService: 未收藏歌曲:${currentSong.filePath}`);
+      return false;
+    } catch (error) {
+      LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to get favorite state: ${error}`);
+      return false;
+    }
+  }
+
+  /**
+   * 初始化收藏列表
+   */
+  private async initializeFavoriteList(): Promise<void> {
+    try {
+      return new Promise<void>((resolve) => {
+        this.getTable().queryByisFav(1, (result: VideoItem[]) => {
+          this.favList = result;
+          LogUtils.getInstance().LOGI(`UnifiedPlayerService: Favorite list initialized with ${this.favList.length} items`);
+          resolve();
+        });
+      });
+    } catch (error) {
+      LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to initialize favorite list: ${error}`);
+    }
+  }
+
+  /**
+   * 更新收藏列表
+   */
+  public async updateFavoriteList(): Promise<void> {
+    try {
+      return new Promise<void>((resolve) => {
+        this.getTable().queryByisFav(1, (result: VideoItem[]) => {
+          this.favList = result;
+          LogUtils.getInstance().LOGI(`UnifiedPlayerService: Favorite list updated with ${this.favList.length} items`);
+          
+          // 更新收藏列表后,立即更新AVSession状态以反映正确的收藏状态
+          setTimeout(() => {
+            this.updateSessionPlayState();
+          }, 100);
+          
+          resolve();
+        });
+      });
+    } catch (error) {
+      LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to update favorite list: ${error}`);
+    }
+  }
+
+  /**
+   * 强制刷新收藏状态(用于收藏/取消收藏操作后)
+   */
+  public async refreshFavoriteStatus(): Promise<void> {
+    try {
+      // 清空缓存,强制重新加载
+      this.favList = [];
+      await this.updateFavoriteList();
+      LogUtils.getInstance().LOGI('UnifiedPlayerService: Favorite status refreshed');
+    } catch (error) {
+      LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to refresh favorite status: ${error}`);
+    }
+  }
+  private getTable(): MediaTable {
+    if (this.table) {
+      return this.table;
+    }
+    if (this.context) {
+      this.table = new MediaTable(this.context);
       return this.table;
       return this.table;
     }
     }
-    return new MediaTable(getContext(this))
+    return new MediaTable(getContext(this) as common.UIAbilityContext);
   }
   }
 
 
   getPlaylist(): VideoItem[] {
   getPlaylist(): VideoItem[] {
@@ -2561,9 +2679,9 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
         return;
         return;
       }
       }
 
 
-      // 避免过于频繁的更新(最小间隔500ms
+      // 避免过于频繁的更新(最小间隔1000ms,增加防抖时间
       const now = Date.now();
       const now = Date.now();
-      if (now - this.lastAvSessionUpdate < 500) {
+      if (now - this.lastAvSessionUpdate < 1000) {
         LogUtils.getInstance().LOGI('UnifiedPlayerService: Skipping AVSession update due to rate limit');
         LogUtils.getInstance().LOGI('UnifiedPlayerService: Skipping AVSession update due to rate limit');
         return;
         return;
       }
       }
@@ -2595,7 +2713,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
         loopMode: this.convertPlayModeToLoopMode(currentState.playMode),
         loopMode: this.convertPlayModeToLoopMode(currentState.playMode),
 
 
         // 收藏状态
         // 收藏状态
-        isFavorite: false // 可以根据实际收藏状态设置
+        isFavorite: this.getCurrentSongFavoriteState()
       };
       };
 
 
       // 只有当duration > 0时才设置位置信息,避免进度条显示问题
       // 只有当duration > 0时才设置位置信息,避免进度条显示问题
@@ -2614,7 +2732,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
       // 记录上次更新时间
       // 记录上次更新时间
       this.lastAvSessionUpdate = now;
       this.lastAvSessionUpdate = now;
 
 
-      LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession play state updated successfully`);
+      LogUtils.getInstance().LOGI(`UnifiedPlayerService: AVSession play state updated successfully`+JSON.stringify(playbackState));
 
 
     } catch (error) {
     } catch (error) {
       LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to update AVSession play state: ${error}`);
       LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to update AVSession play state: ${error}`);

+ 12 - 24
entry/src/main/ets/view/LocalMusic.ets

@@ -2655,6 +2655,8 @@ export struct LocalMusic {
         } else {
         } else {
           ToastUtil.showToast('取消收藏成功');
           ToastUtil.showToast('取消收藏成功');
         }
         }
+        
+        // 先更新收藏列表和其他UI
         this.deleteCache(this.currentPath)
         this.deleteCache(this.currentPath)
         this.getFavList(false)
         this.getFavList(false)
         if (this.modeType === 0 && !this.isFavMusic) {
         if (this.modeType === 0 && !this.isFavMusic) {
@@ -2664,6 +2666,9 @@ export struct LocalMusic {
         workerInstance.postMessage({ code: 2, data: this.context });
         workerInstance.postMessage({ code: 2, data: this.context });
         workerInstance.postMessage({ code: 3, data: this.context });
         workerInstance.postMessage({ code: 3, data: this.context });
         workerInstance.postMessage({ code: 4, data: this.context });
         workerInstance.postMessage({ code: 4, data: this.context });
+        
+        // 刷新UnifiedPlayerService的收藏状态,这会自动更新AVSession
+        await this.unifiedPlayerService.refreshFavoriteStatus();
       }
       }
     })
     })
   }
   }
@@ -9990,7 +9995,7 @@ export struct LocalMusic {
       // 更新播放状态
       // 更新播放状态
       this.CONTROL_PlayStatus = PlayStatus.PLAY;
       this.CONTROL_PlayStatus = PlayStatus.PLAY;
       this.setIsPlaying(true);
       this.setIsPlaying(true);
-      this.updateSessionPlayState(true);
+      // 注意:AVSession状态更新由UnifiedPlayerService统一处理,避免冲突
 
 
       LogUtils.getInstance().LOGI("LocalMusic: startPlayOrResumePlay completed via UnifiedPlayerService");
       LogUtils.getInstance().LOGI("LocalMusic: startPlayOrResumePlay completed via UnifiedPlayerService");
     } catch (error) {
     } catch (error) {
@@ -10006,7 +10011,7 @@ export struct LocalMusic {
   private handlePlaybackError() {
   private handlePlaybackError() {
     this.CONTROL_PlayStatus = PlayStatus.INIT;
     this.CONTROL_PlayStatus = PlayStatus.INIT;
     this.setIsPlaying(false);
     this.setIsPlaying(false);
-    this.updateSessionPlayState(false);
+    // 注意:AVSession状态更新由UnifiedPlayerService统一处理,避免冲突
     this.mDestroyPage = true;
     this.mDestroyPage = true;
   }
   }
 
 
@@ -10303,13 +10308,11 @@ export struct LocalMusic {
     if (this.isCurrentTime) {
     if (this.isCurrentTime) {
       // 如果歌曲刚刚切换,强制重置并使用新时间
       // 如果歌曲刚刚切换,强制重置并使用新时间
       if (this.justSwitched) {
       if (this.justSwitched) {
-        console.log(`Heanup stringForTime歌曲刚切换 - 强制重置oldSeconds: ${this.oldSeconds} -> ${seconds}, 强制使用新时间`);
         this.oldSeconds = seconds; // 直接使用新时间
         this.oldSeconds = seconds; // 直接使用新时间
         this.justSwitched = false; // 重置标志
         this.justSwitched = false; // 重置标志
         this.lastSwitchTime = Date.now(); // 记录切换时间
         this.lastSwitchTime = Date.now(); // 记录切换时间
       } else if (this.isSeekTo) {
       } else if (this.isSeekTo) {
         // 如果是拖动进度条,直接使用新时间
         // 如果是拖动进度条,直接使用新时间
-        console.log(`Heanup stringForTime拖动进度条 - 直接使用新时间: ${seconds}`);
         this.oldSeconds = seconds;
         this.oldSeconds = seconds;
       } else {
       } else {
         // 正常播放时的防抖逻辑
         // 正常播放时的防抖逻辑
@@ -10323,20 +10326,16 @@ export struct LocalMusic {
         if (timeDiff > 10) {
         if (timeDiff > 10) {
           // 如果在冷却期内,允许更大的时间跳跃(可能是新歌曲开始播放)
           // 如果在冷却期内,允许更大的时间跳跃(可能是新歌曲开始播放)
           if (isInCooldown) {
           if (isInCooldown) {
-            console.log(`Heanup stringForTime切换后冷却期内检测到时间跳跃 - oldSeconds: ${this.oldSeconds}, newSeconds: ${seconds}, diff: ${timeDiff}, 允许跳跃`);
             this.oldSeconds = seconds;
             this.oldSeconds = seconds;
           } else {
           } else {
             // 不在冷却期内,可能是异常情况,记录但使用新时间
             // 不在冷却期内,可能是异常情况,记录但使用新时间
-            console.log(`Heanup stringForTime检测到异常时间跳跃 - oldSeconds: ${this.oldSeconds}, newSeconds: ${seconds}, diff: ${timeDiff},直接使用新时间`);
             this.oldSeconds = seconds;
             this.oldSeconds = seconds;
           }
           }
         } else if (this.oldSeconds <= seconds || seconds === 0) {
         } else if (this.oldSeconds <= seconds || seconds === 0) {
           // 正常时间递进或重置为0
           // 正常时间递进或重置为0
-          console.log(`Heanup stringForTime正常更新 - oldSeconds: ${this.oldSeconds} -> ${seconds}`);
           this.oldSeconds = seconds;
           this.oldSeconds = seconds;
         } else {
         } else {
           // 时间倒退,使用防抖
           // 时间倒退,使用防抖
-          console.log(`Heanup stringForTime防抖 - oldSeconds: ${this.oldSeconds}, newSeconds: ${seconds}, 使用oldSeconds`);
           seconds = this.oldSeconds;
           seconds = this.oldSeconds;
         }
         }
       }
       }
@@ -10632,21 +10631,10 @@ export struct LocalMusic {
 
 
   private sessionToggleFavoriteCallback = (assetId: string) => {
   private sessionToggleFavoriteCallback = (assetId: string) => {
     if (this.currentSong) {
     if (this.currentSong) {
-      console.info(`on toggleFavorite `);
+      console.info(`on toggleFavorite - 播控中心收藏按钮被点击`);
       // 应用收到收藏命令,进行收藏处理。
       // 应用收到收藏命令,进行收藏处理。
-
-      this.isFac = Utility.getIsFav(this.favList, this.currentSong)
-      this.doFav(this.currentSong)
-      // 应用内完成或者取消收藏,把新的收藏状态设置给AVSession。
-      let playbackState: avSession.AVPlaybackState = {
-        isFavorite: !this.isFac,
-      };
-      // this.avSessionController.setAvSessionPlayState(playbackState);
-      // this.avSessionController.getAvSession()?.setAVPlaybackState(playbackState).then(() => {
-      //   console.info(`SetAVPlaybackState successfully`+assetId+this.isFac);
-      // }).catch((err: BusinessError) => {
-      //   console.info(`SetAVPlaybackState BusinessError: code: ${err.code}, message: ${err.message}`);
-      // });
+      // 注意:这里不需要立即更新AVSession状态,因为doFav方法内部会处理状态更新
+      this.doFav(this.currentSong);
     }
     }
   }
   }
   private sessionOutputDeviceChange = async (connectState: avSession.ConnectionState,
   private sessionOutputDeviceChange = async (connectState: avSession.ConnectionState,
@@ -11359,7 +11347,7 @@ export struct LocalMusic {
       this.setProgress();
       this.setProgress();
       this.mDestroyPage = true;
       this.mDestroyPage = true;
       this.CONTROL_PlayStatus = PlayStatus.PAUSE;
       this.CONTROL_PlayStatus = PlayStatus.PAUSE;
-      this.updateSessionPlayState(false)
+      // 注意:AVSession状态更新由UnifiedPlayerService统一处理,避免冲突
       this.playChange()
       this.playChange()
       if (this.pipController) {
       if (this.pipController) {
         this.pipController.updatePiPControlStatus(PiPWindow.PiPControlType.VIDEO_PLAY_PAUSE,
         this.pipController.updatePiPControlStatus(PiPWindow.PiPControlType.VIDEO_PLAY_PAUSE,
@@ -11400,7 +11388,7 @@ export struct LocalMusic {
       this.stopProgressTask();
       this.stopProgressTask();
       this.CONTROL_PlayStatus = PlayStatus.INIT;
       this.CONTROL_PlayStatus = PlayStatus.INIT;
       this.setIsPlaying(false);
       this.setIsPlaying(false);
-      this.updateSessionPlayState(false)
+      // 注意:AVSession状态更新由UnifiedPlayerService统一处理,避免冲突
       this.playChange()
       this.playChange()
       this.watchStatus();
       this.watchStatus();
 
 

+ 141 - 0
收藏功能修复验证.md

@@ -0,0 +1,141 @@
+# 收藏功能修复验证
+
+## 问题描述
+根据日志分析,发现收藏歌曲列表获取总数为空的问题:
+```
+UnifiedPlayerService: 当前歌曲为空 - favList:[]
+```
+
+虽然实际上是有收藏歌曲的,但系统播控中心显示的收藏状态始终为 `isFavorite: false`。
+
+## 问题根因分析
+
+### 1. 异步数据加载问题
+`UnifiedPlayerService.getFav()` 方法存在异步处理问题:
+- 方法立即返回 `this.favList`(可能为空)
+- 数据库查询是异步的,但方法不等待查询完成
+- 导致 `getCurrentSongFavoriteState()` 总是获取到空的收藏列表
+
+### 2. 竞态条件问题
+在 `LocalMusic.doFav()` 方法中:
+- 调用 `unifiedPlayerService.updateFavoriteList()` 更新收藏列表
+- 然后手动更新 AVSession 的收藏状态
+- 这与 UnifiedPlayerService 自己的 AVSession 更新产生竞态条件
+
+## 修复方案
+
+### 1. 优化收藏列表获取逻辑
+**文件**: `entry/src/main/ets/common/service/UnifiedPlayerService.ets`
+
+```typescript
+getFav(): Array<VideoItem>{
+  if (this.favList.length>0){
+    console.log("Heanup UnifiedPlayerService favList cached:"+json.stringify( this.favList));
+    return this.favList;
+  }
+  // 如果缓存为空,异步更新但返回空数组,避免阻塞
+  this.getTable().queryByisFav(1, async (result: VideoItem[]) => {
+    console.log("Heanup UnifiedPlayerService favList:"+json.stringify( result));
+    this.favList = result;
+    // 更新收藏列表后,重新更新AVSession状态以反映正确的收藏状态
+    if (result.length > 0) {
+      setTimeout(() => {
+        this.updateSessionPlayState();
+      }, 100);
+    }
+  })
+  return this.favList;
+}
+```
+
+### 2. 改进收藏状态检查逻辑
+```typescript
+private getCurrentSongFavoriteState(): boolean {
+  try {
+    let currentSong = this.playlistModel.getCurrentSong();
+    if (!currentSong) {
+      LogUtils.getInstance().LOGI(`UnifiedPlayerService: 当前歌曲为空`);
+      return false;
+    }
+    
+    let favList = this.getFav();
+    if (!favList || favList.length === 0) {
+      // 如果收藏列表为空,可能还在加载中,先返回false
+      // 但不记录为错误,因为这是正常的初始化状态
+      return false;
+    }
+    
+    // 检查当前歌曲是否在收藏列表中
+    for (let i = 0; i < favList.length; i++) {
+      if (favList[i].filePath === currentSong.filePath && favList[i].isFav === 1) {
+        LogUtils.getInstance().LOGI(`UnifiedPlayerService: 收藏歌曲:${currentSong.filePath}`);
+        return true;
+      }
+    }
+    LogUtils.getInstance().LOGI(`UnifiedPlayerService: 未收藏歌曲:${currentSong.filePath}`);
+    return false;
+  } catch (error) {
+    LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to get favorite state: ${error}`);
+    return false;
+  }
+}
+```
+
+### 3. 添加收藏状态刷新方法
+```typescript
+/**
+ * 强制刷新收藏状态(用于收藏/取消收藏操作后)
+ */
+public async refreshFavoriteStatus(): Promise<void> {
+  try {
+    // 清空缓存,强制重新加载
+    this.favList = [];
+    await this.updateFavoriteList();
+    LogUtils.getInstance().LOGI('UnifiedPlayerService: Favorite status refreshed');
+  } catch (error) {
+    LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to refresh favorite status: ${error}`);
+  }
+}
+```
+
+### 4. 优化收藏操作处理
+**文件**: `entry/src/main/ets/view/LocalMusic.ets`
+
+将原来的手动 AVSession 更新替换为统一的刷新方法:
+```typescript
+// 刷新UnifiedPlayerService的收藏状态,这会自动更新AVSession
+await this.unifiedPlayerService.refreshFavoriteStatus();
+```
+
+### 5. 并行初始化收藏列表
+在服务初始化时并行加载收藏列表:
+```typescript
+// 并行初始化收藏列表和恢复数据,提高效率
+const initPromises2 = [
+  this.restorePersistedState(),
+  this.initializeFavoriteList()
+];
+
+await Promise.all(initPromises2);
+```
+
+## 预期效果
+
+修复后应该能够:
+1. 正确加载收藏歌曲列表
+2. 在系统播控中心正确显示收藏状态
+3. 收藏/取消收藏操作后立即更新播控状态
+4. 避免竞态条件和重复的 AVSession 更新
+
+## 验证方法
+
+1. 启动应用,播放一首已收藏的歌曲
+2. 检查系统播控中心是否显示心形图标(收藏状态)
+3. 在应用中取消收藏,检查播控中心是否立即更新
+4. 重新收藏,检查播控中心是否立即显示收藏状态
+5. 查看日志,确认不再出现 "当前歌曲为空 - favList:[]" 的错误信息
+
+## 相关文件
+- `entry/src/main/ets/common/service/UnifiedPlayerService.ets`
+- `entry/src/main/ets/view/LocalMusic.ets`
+- `entry/src/main/ets/common/util/MediaTable.ets`