chendeben 1 год назад
Родитель
Сommit
931cae4ad1
1 измененных файлов с 89 добавлено и 77 удалено
  1. 89 77
      entry/src/main/ets/common/service/UnifiedPlayerService.ets

+ 89 - 77
entry/src/main/ets/common/service/UnifiedPlayerService.ets

@@ -135,6 +135,24 @@ export interface WidgetFormData {
   formImages: Record<string, number>;
 }
 
+/**
+ * 缓存的静态卡片数据接口
+ * 用于缓存不会随播放状态改变的数据,避免重复计算和IO
+ */
+interface CachedWidgetData {
+  // 歌曲的基本信息
+  id: string;
+  name: string;
+  artist: string;
+  album: string;
+  pixelMapPath: string; // 原始封面路径
+  duration: number;
+  filePath: string;
+  imageColorHex: string;
+  // 处理后的最终图片路径(本地或缓存),用于快速打开
+  finalImagePath: string | null;
+}
+
 /**
  * 统一播放器服务接口
  * 提供统一的播放控制接口,替代LocalMusic中的播放器逻辑
@@ -451,6 +469,9 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
   private readonly MAX_CACHE_SIZE_MB: number = 50; // 最大缓存大小:50MB
   private hasChecked: boolean=false;
 
+  // 卡片数据缓存
+  private widgetDataCache: CachedWidgetData | null = null;
+
   private constructor() {
     this.playerManager = PlayerManager.getInstance();
     this.stateModel = new PlayerStateModel();
@@ -3007,19 +3028,20 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
   }
 
   public async getWidgetFormData(loadImage: boolean = true): Promise<widgeData | undefined> {
-    let imgMap: Record<string, number> = {};
     this.updatePlaylistStateInModelSync();
-    const formData: VideoItem = this.getCurrentSong() as VideoItem; // VideoItem
+    const formData: VideoItem = this.getCurrentSong()!; // VideoItem
     const songState: PlayerState = this.getCurrentState(); // PlayerState
-    
+    let imgMap: Record<string, number> = {};
+
     // 从持久化存储获取所有formId - 使用新的getContext方法
     let context = this.getContext();
-    
+
     // 如果还是没有context,尝试从AppStorage获取
     if (!context) {
       context = AppStorage.get('context') as common.UIAbilityContext;
     }
 
+    // 1. 准备动态数据 (这部分总是实时的)
     // 构建卡片数据,包含VideoItem和PlayerState信息
     const widgetPlayerState: WidgetPlayerState = {
       isPlaying: songState.isPlaying || false,
@@ -3044,7 +3066,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
     };
 
     if (!formData) {
-      console.log('UnifiedPlayerService getWidgetFormData: No formData available - formData:'+json.stringify(formData))
+      LogUtils.getInstance().LOGI('UnifiedPlayerService getWidgetFormData: No current song, returning default data.');
       const defaultData: widgeData = {
         id: '',
         name: '未知歌曲',
@@ -3075,96 +3097,87 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
       }
       return defaultData;
     }
-    console.log('UnifiedPlayerService getWidgetFormData -  context: '+json.stringify(context)+" - formData:"+json.stringify(formData))
-    // 处理封面图片
-    let imgName: string = '';
-
-    // 如果歌曲有封面图片路径,尝试处理图片
-    if (loadImage && formData.pixelMapPath && formData.pixelMapPath.trim() !== '') {
-      try {
-        // 生成唯一的图片名称,确保每次更新时图片能正确刷新
-        imgName = 'songCover_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
 
-        // 判断是否为网络图片
-        if (formData.pixelMapPath.startsWith('http://') || formData.pixelMapPath.startsWith('https://')) {
-          // 处理网络图片:使用缓存机制
-          LogUtils.getInstance()
-            .LOGI(`UnifiedPlayerService updateAllForms: Processing network image ${formData.pixelMapPath}`);
+    // 2. 处理静态数据(利用缓存)
+    // 如果歌曲已切换,则清空缓存
+    if (this.widgetDataCache?.filePath !== formData.filePath) {
+      this.widgetDataCache = null;
+    }
 
+    if (!this.widgetDataCache) {
+      LogUtils.getInstance().LOGI(`UnifiedPlayerService: Cache miss for [${formData.name}], generating static data.`);
+      // 缓存未命中,生成并缓存静态数据
+      let finalImagePath: string | null = null;
+      if (loadImage && formData.pixelMapPath && formData.pixelMapPath.trim() !== '') {
+        if (formData.pixelMapPath.startsWith('http')) {
+          // 网络图片,走缓存/下载逻辑
           if (this.imageCacheDir) {
             const cacheFileName = this.generateCacheFileName(formData.pixelMapPath);
-            const cacheFilePath = this.imageCacheDir + '/' + cacheFileName;
-
-            // 检查缓存是否存在且有效
+            const cacheFilePath = `${this.imageCacheDir}/${cacheFileName}`;
             if (await this.isCacheValid(cacheFilePath)) {
-              // 使用缓存文件
-              LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Using cached image: ${cacheFilePath}`);
-              try {
-                const file = fileIo.openSync(cacheFilePath, fileIo.OpenMode.READ_ONLY);
-                imgMap[imgName] = file.fd;
-                LogUtils.getInstance()
-                  .LOGI(`UnifiedPlayerService updateAllForms: Opened cached image with fd ${file.fd}, imgName: ${imgName}`);
-              } catch (error) {
-                LogUtils.getInstance()
-                  .LOGI(`UnifiedPlayerService updateAllForms: Failed to open cached image: ${error}`);
-              }
+              finalImagePath = cacheFilePath;
             } else {
-              // 下载到缓存
-              LogUtils.getInstance()
-                .LOGI(`UnifiedPlayerService updateAllForms: Downloading image to cache: ${cacheFilePath}`);
               const downloadSuccess = await this.downloadImageToCache(formData.pixelMapPath, cacheFilePath);
-
               if (downloadSuccess) {
-                try {
-                  const file = fileIo.openSync(cacheFilePath, fileIo.OpenMode.READ_ONLY);
-                  imgMap[imgName] = file.fd;
-                  LogUtils.getInstance()
-                    .LOGI(`UnifiedPlayerService updateAllForms: Opened downloaded image with fd ${file.fd}, imgName: ${imgName}`);
-                } catch (error) {
-                  LogUtils.getInstance()
-                    .LOGI(`UnifiedPlayerService updateAllForms: Failed to open downloaded image: ${error}`);
-                }
-              } else {
-                LogUtils.getInstance()
-                  .LOGI(`UnifiedPlayerService updateAllForms: Failed to download network image ${formData.pixelMapPath}`);
+                finalImagePath = cacheFilePath;
               }
             }
-          } else {
-            LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: Image cache directory not available`);
           }
         } else {
-          // 处理本地图片文件
-          let file = fileIo.openSync(formData.pixelMapPath, fileIo.OpenMode.READ_ONLY);
-          imgMap[imgName] = file.fd;
-
-          LogUtils.getInstance()
-            .LOGI(`UnifiedPlayerService updateAllForms: Opened local cover image ${formData.pixelMapPath} with fd ${file.fd}, imgName: ${imgName}`);
+          // 本地图片
+          finalImagePath = formData.pixelMapPath;
         }
+      }
+
+      this.widgetDataCache = {
+        id: formData.id || '',
+        name: formData.name || '',
+        artist: formData.artist || '',
+        album: formData.album || '',
+        pixelMapPath: formData.pixelMapPath || '',
+        duration: typeof formData.duration === 'string' ? parseInt(formData.duration || '0') : (formData.duration || 0),
+        filePath: formData.filePath || '',
+        imageColorHex: '2A2A2A', // 默认颜色,UI侧会根据图片重新提取
+        finalImagePath: finalImagePath
+      };
+    } else {
+      LogUtils.getInstance().LOGI(`UnifiedPlayerService: Cache hit for [${formData.name}].`);
+    }
+
+    // 3. 组合静态和动态数据,并处理图片文件描述符
+    const cachedData = this.widgetDataCache;
+
+    let imgName: string = '';
+
+    if (cachedData.finalImagePath) {
+      try {
+        // 每次都重新打开文件获取新的fd,这是最安全的方式
+        const file = fileIo.openSync(cachedData.finalImagePath, fileIo.OpenMode.READ_ONLY);
+        // 使用时间戳和随机数生成唯一名称,强制卡片刷新图片
+        imgName = `songCover_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
+        imgMap[imgName] = file.fd;
+        // 注意:我们不关闭这个fd,系统会在处理完卡片数据后关闭它
       } catch (error) {
-        LogUtils.getInstance()
-          .LOGI(`UnifiedPlayerService updateAllForms: Failed to process cover image ${formData.pixelMapPath}: ${JSON.stringify(error as BusinessError)}`);
-        // 如果处理图片失败,使用空的imgMap
+        LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to open image file [${cachedData.finalImagePath}]: ${JSON.stringify(error)}`);
         imgMap = {};
         imgName = '';
       }
-    } else {
-      LogUtils.getInstance().LOGI(`UnifiedPlayerService updateAllForms: No cover image path available`);
     }
 
-    // 平铺数据结构,确保桌面卡片能正确接收
+    // 4. 构建最终的完整数据对象
     const widgetData: widgeData = {
-      // VideoItem数据平铺
-      id: formData.id || '',
-      name: formData.name || '',
-      artist: formData.artist || '',
-      album: formData.album || '',
-      pixelMapPath: formData.pixelMapPath || '',
-      duration: typeof formData.duration === 'string' ? parseInt(formData.duration || '0') : (formData.duration || 0),
-      filePath: formData.filePath || '',
+      // 从缓存读取静态数据
+      id: cachedData.id,
+      name: cachedData.name,
+      artist: cachedData.artist,
+      album: cachedData.album,
+      pixelMapPath: cachedData.pixelMapPath,
+      duration: cachedData.duration,
+      filePath: cachedData.filePath,
       imgName: imgName,
-      imageColorHex: '2A2A2A',
+      imageColorHex: cachedData.imageColorHex,
 
-      // PlayerState数据平铺
+      // PlayerState数据平铺 (实时)
       isPlaying: widgetPlayerState.isPlaying,
       isPaused: widgetPlayerState.isPaused,
       isLoading: widgetPlayerState.isLoading,
@@ -3173,15 +3186,15 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
       hasPrevious: widgetPlayerState.hasPrevious,
       playMode: widgetPlayerState.playMode,
 
-      // 播放列表信息平铺
+      // 播放列表信息平铺 (实时)
       currentIndex: widgetPlaylistInfo.currentIndex,
       totalCount: widgetPlaylistInfo.totalCount,
 
-      // 时间信息平铺
+      // 时间信息平铺 (实时)
       currentTimeText: widgetTimeInfo.currentTimeText,
       totalTimeText: widgetTimeInfo.totalTimeText,
       progressPercentage: widgetTimeInfo.progressPercentage,
-
+      isFavorite: this.getCurrentSongFavoriteState(), // 收藏状态 (实时)
       // 保持嵌套结构兼容性
       playerState: widgetPlayerState,
       playlistInfo: widgetPlaylistInfo,
@@ -3189,7 +3202,6 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
       // 卡片图片显示必填字段
       formImages: imgMap,
-      isFavorite: this.getCurrentSongFavoriteState(),
     };
     return widgetData;
   }