Explorar el Código

优化统一播放器服务,减少延迟和提高响应速度,改进RPC处理,异步更新Widget数据,确保数据一致性,提升用户体验。

chendeben hace 1 año
padre
commit
12e9bca9b9

+ 0 - 399
.cursor/rules/cursorrules.mdc

@@ -1,399 +0,0 @@
-# 鸿蒙ArkTS编程规则与最佳实践
-
-## 语法差异规则:ArkTS vs TypeScript
-
-### 1. 空值检查规则
-```typescript
-// ❌ 错误: 未对可能为null的对象执行检查
-this.dbObject.executeSql(sql);
-
-// ✅ 正确: 执行null检查
-if (this.dbObject) {
-  this.dbObject.executeSql(sql);
-}
-```
-
-### 2. 解构赋值规则(已支持)
-```typescript
-// ✅ 正确: ArkTS 4.0+ 已支持解构赋值语法
-for (const [key, value] of Object.entries(obj)) {
-  // 处理逻辑
-}
-
-// ✅ 正确: 数组解构赋值
-const [first, second, ...rest] = array;
-
-// ✅ 正确: 对象解构赋值
-const { name, age, ...otherProps } = person;
-```
-
-### 3. 异步API调用规则
-```typescript
-// ❌ 错误: 回调参数类型不匹配
-dbStore.executeSql(sql, (err, result: ResultSet) => {
-  // 处理逻辑
-});
-
-// ✅ 正确: 使用Promise模式
-dbStore.executeSql(sql)
-  .then(() => {
-    // 成功处理
-  })
-  .catch((err: Error) => {
-    // 错误处理
-    console.log(err.message);
-  });
-
-// ✅ 正确: 使用async/await模式
-async function executeQuery() {
-  try {
-    await dbStore.executeSql(sql);
-    // 成功处理
-  } catch (err: Error) {
-    // 错误处理
-    console.log(err.message);
-  }
-}
-```
-
-### 4. 计算属性名规则(已支持)
-```typescript
-// ✅ 正确: ArkTS 4.0+ 已支持计算属性名语法
-const obj = { [CONSTANT.KEY]: value };
-
-// ✅ 正确: 动态属性名
-const propertyName = 'dynamicKey';
-const obj = { [propertyName]: value };
-```
-
-### 5. 数据类型规则
-```typescript
-// ❌ 错误: 使用any或未指定泛型类型
-const items = new Set();
-const map = new Map();
-
-// ✅ 正确: 明确指定泛型类型
-const items = new Set<string>();
-const map = new Map<string, number>();
-
-// ✅ 正确: 使用类型推断
-const items = new Set<string>();
-const map = new Map<string, number>();
-```
-
-### 6. 错误对象类型规则
-```typescript
-// ❌ 错误: 使用隐式any类型的错误对象
-try {
-  // 代码
-} catch (e) {
-  console.log(`错误: ${e}`);
-}
-
-// ✅ 正确: 明确指定错误对象的类型
-try {
-  // 代码
-} catch (e: Error) {
-  console.log(`错误: ${e.message}`);
-}
-
-// ✅ 正确: 使用unknown类型(更安全)
-try {
-  // 代码
-} catch (e: unknown) {
-  if (e instanceof Error) {
-    console.log(`错误: ${e.message}`);
-  } else {
-    console.log(`未知错误: ${e}`);
-  }
-}
-```
-
-### 7. 新的语法特性支持
-
-#### 7.1 可选链操作符
-```typescript
-// ✅ 正确: 使用可选链操作符
-const name = user?.profile?.name;
-const result = obj?.method?.();
-```
-
-#### 7.2 空值合并操作符
-```typescript
-// ✅ 正确: 使用空值合并操作符
-const value = input ?? defaultValue;
-const name = user?.name ?? 'Unknown';
-```
-
-#### 7.3 模板字面量
-```typescript
-// ✅ 正确: 使用模板字面量
-const message = `Hello, ${name}!`;
-const sql = `SELECT * FROM ${tableName} WHERE id = ${id}`;
-```
-
-## 数据库操作最佳实践
-
-### 1. 表结构升级
-```typescript
-// ❌ 错误: 依赖回调处理的表结构升级
-this.rdbStore.executeSql(tableInfoQuery, (err, result) => {
-  // 处理逻辑
-});
-
-// ✅ 正确: 使用Promise模式并独立处理每个列的添加操作
-this.rdbStore.executeSql(tableInfoQuery)
-  .then(() => {
-    // 为每个需要添加的列单独执行ALTER TABLE
-    Object.keys(columnsToAdd).forEach(column => {
-      const type = columnsToAdd[column];
-      this.rdbStore.executeSql(`ALTER TABLE ${tableName} ADD COLUMN ${column} ${type}`)
-        .then(() => { /* 成功处理 */ })
-        .catch((err: Error) => { /* 错误处理 */ });
-    });
-  });
-
-// ✅ 正确: 使用async/await模式
-async function upgradeTable() {
-  try {
-    await this.rdbStore.executeSql(tableInfoQuery);
-    for (const [column, type] of Object.entries(columnsToAdd)) {
-      await this.rdbStore.executeSql(`ALTER TABLE ${tableName} ADD COLUMN ${column} ${type}`);
-    }
-  } catch (err: Error) {
-    console.error('表结构升级失败:', err.message);
-  }
-}
-```
-
-### 2. 资源释放
-```typescript
-// ❌ 错误: 未关闭ResultSet
-this.rdbStore.query(predicates, (resultSet) => {
-  // 处理逻辑
-});
-
-// ✅ 正确: 确保关闭ResultSet
-this.rdbStore.query(predicates, (resultSet) => {
-  try {
-    // 处理逻辑
-  } finally {
-    resultSet.close();
-  }
-});
-
-// ✅ 正确: 使用async/await模式
-async function queryData() {
-  const resultSet = await this.rdbStore.query(predicates);
-  try {
-    // 处理逻辑
-  } finally {
-    resultSet.close();
-  }
-}
-```
-
-## 对象字面量规则
-
-### 1. 复杂对象初始化(已支持)
-```typescript
-// ✅ 正确: ArkTS 4.0+ 已支持复杂对象字面量初始化
-const config = {
-  complex: {
-    nested: {
-      value: someValue
-    }
-  }
-};
-
-// ✅ 正确: 使用展开操作符
-const baseConfig = { timeout: 5000 };
-const extendedConfig = { ...baseConfig, retries: 3 };
-```
-
-### 2. 方法简写
-```typescript
-// ✅ 正确: 使用方法简写语法
-const obj = {
-  name: 'test',
-  sayHello() {
-    return `Hello, ${this.name}!`;
-  }
-};
-```
-
-## 组件开发最佳实践
-
-### 1. 状态管理
-```typescript
-// ✅ 正确: 使用@State装饰器
-@State count: number = 0;
-
-// ✅ 正确: 使用@Prop装饰器
-@Prop title: string = '';
-
-// ✅ 正确: 使用@Link装饰器
-@Link isVisible: boolean = false;
-```
-
-### 2. 生命周期方法
-```typescript
-// ✅ 正确: 使用async生命周期方法
-async aboutToAppear() {
-  await this.initializeData();
-}
-
-// ✅ 正确: 使用Promise处理异步操作
-aboutToAppear() {
-  this.initializeData().then(() => {
-    console.log('初始化完成');
-  }).catch((err: Error) => {
-    console.error('初始化失败:', err.message);
-  });
-}
-```
-
-### 3. 事件处理
-```typescript
-// ✅ 正确: 使用async事件处理
-.onClick(async () => {
-  try {
-    await this.handleClick();
-  } catch (err: Error) {
-    console.error('点击处理失败:', err.message);
-  }
-})
-
-// ✅ 正确: 使用箭头函数
-.onClick((event: ClickEvent) => {
-  this.handleClick(event);
-})
-```
-
-## 性能优化最佳实践
-
-### 1. 避免在build方法中进行复杂计算
-```typescript
-// ❌ 错误: 在build方法中进行复杂计算
-build() {
-  const expensiveResult = this.computeExpensiveValue();
-  return Column() {
-    Text(expensiveResult)
-  }
-}
-
-// ✅ 正确: 预先计算或使用缓存
-@State private cachedResult: string = '';
-
-aboutToAppear() {
-  this.cachedResult = this.computeExpensiveValue();
-}
-
-build() {
-  return Column() {
-    Text(this.cachedResult)
-  }
-}
-```
-
-### 2. 使用LazyForEach优化列表性能
-```typescript
-// ✅ 正确: 使用LazyForEach
-LazyForEach(this.dataSource, (item: DataItem) => {
-  ListItem() {
-    Text(item.name)
-  }
-}, (item: DataItem) => item.id.toString())
-```
-
-## 命名规范
-
-- 类名: PascalCase (例如 MediaTable)
-- 方法名: camelCase (例如 queryByParentPath)
-- 常量: UPPER_SNAKE_CASE (例如 DB_COLUMNS.FILE_PATH)
-- 私有属性: _camelCase (例如 _dbStore)
-- 组件名: PascalCase (例如 NewIndex)
-- 装饰器: @开头 (例如 @State, @Prop)
-
-## 错误处理最佳实践
-
-### 1. 统一错误处理
-```typescript
-// ✅ 正确: 创建统一的错误处理函数
-private handleError(error: unknown, context: string): void {
-  if (error instanceof Error) {
-    console.error(`${context} 失败:`, error.message);
-  } else {
-    console.error(`${context} 失败:`, String(error));
-  }
-}
-
-// 使用示例
-try {
-  await this.performOperation();
-} catch (error: unknown) {
-  this.handleError(error, '操作执行');
-}
-```
-
-### 2. 业务错误处理
-```typescript
-// ✅ 正确: 处理业务错误
-try {
-  await this.apiCall();
-} catch (error: unknown) {
-  if (error instanceof BusinessError) {
-    console.error(`业务错误: ${error.code}, ${error.message}`);
-  } else {
-    console.error('未知错误:', error);
-  }
-}
-```
-
-## 类型安全最佳实践
-
-### 1. 使用类型断言
-```typescript
-// ✅ 正确: 安全的类型断言
-const result = data as VideoItem;
-
-// ✅ 正确: 使用类型守卫
-if (typeof data === 'object' && data !== null && 'name' in data) {
-  const videoItem = data as VideoItem;
-}
-```
-
-### 2. 接口定义
-```typescript
-// ✅ 正确: 定义清晰的接口
-interface VideoItem {
-  id: string;
-  name: string;
-  artist?: string;
-  album?: string;
-  duration: number;
-  filePath: string;
-}
-```
-
-## 调试和日志最佳实践
-
-### 1. 使用hilog进行日志记录
-```typescript
-// ✅ 正确: 使用hilog
-import hilog from '@ohos.hilog';
-
-hilog.info(0x0000, 'TAG', '%{public}s', '信息日志');
-hilog.warn(0x0000, 'TAG', '%{public}s', '警告日志');
-hilog.error(0x0000, 'TAG', '%{public}s', '错误日志');
-```
-
-### 2. 条件日志
-```typescript
-// ✅ 正确: 使用条件日志避免性能影响
-if (__DEV__) {
-  console.log('调试信息:', data);
-}
-```
-- 私有属性: _camelCase (例如 _dbStore)
-

+ 124 - 50
entry/src/main/ets/common/service/UnifiedPlayerService.ets

@@ -1,5 +1,6 @@
 import { VideoItem } from '../../viewmodel/VideoItem';
 import { PlayerManager, IPlayerManager, PlayerStateCallback } from './PlayerManager';
+import { MD5 } from '@pura/harmony-utils';
 import {
   PlayerStateModel,
   PlayerState,
@@ -168,6 +169,11 @@ interface CachedWidgetData {
   finalImagePath: string | null;
 }
 
+interface ImageCacheInfo {
+  path: string;
+  imgName: string;
+}
+
 /**
  * 统一播放器服务接口
  * 提供统一的播放控制接口,替代LocalMusic中的播放器逻辑
@@ -478,6 +484,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
   // 图片缓存相关属性
   private imageCacheDir: string = ''; // 图片缓存目录
+  private lastImageCache: ImageCacheInfo | null = null; // 图片缓存优化
   private readonly CACHE_EXPIRY_DAYS: number = 7; // 缓存有效期:7天
   private readonly MAX_CACHE_SIZE_MB: number = 50; // 最大缓存大小:50MB
   private hasChecked: boolean=false;
@@ -1058,7 +1065,7 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
       // 更新播放列表状态
       await this.updatePlaylistStateInModel();
 
-      // 播放新歌曲后,等待一段时间确保播放器状态稳定,然后更新卡片
+      // 优化:减少延迟,立即更新卡片状态,避免重复更新AVSession
       setTimeout(async () => {
         try {
           // 确保播放列表状态是最新的
@@ -1066,15 +1073,15 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
           // 强制更新所有桌面卡片状态
           await this.updateAllForms(true);
-          if (newSong) {
-            await this.updateAvSessionMetadata(newSong)
-          }
+          
+          // 移除此处的AVSession更新,避免与onPrepared中的更新冲突
+          // AVSession会在onPrepared回调中正确更新
 
           LogUtils.getInstance().LOGI('UnifiedPlayerService: Widget state updated after track change');
         } catch (error) {
           LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: Failed to update widgets after track change: ${error}`);
         }
-      }, 200); // 延迟200ms确保播放器状态稳定
+      }, 50); // 减少延迟到50ms,提高响应速度
 
     } catch (error) {
       await this.handlePlaybackError(error as Error, PlayerErrorType.PLAYBACK_ERROR);
@@ -2763,10 +2770,12 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
       // 更新 AVSession 状态
       if (isActuallyPlaying) {
-        let currentSong = this.getCurrentSong();
+        // 优化:确保使用最新的歌曲数据
+        let currentSong = this.playlistModel.getCurrentSong();
         if (currentSong) {
           console.log(`Heanup2 UnifiedPlayerService: onPrepared - updateAvSessionMetadata: ${json.stringify(currentSong)}`);
-          this.updateAvSessionMetadata(currentSong)
+          // 立即更新,避免延迟导致的状态不一致
+          this.updateAvSessionMetadata(currentSong);
         }
         this.updateSessionPlayState(true); // 强制更新,不使用防抖
       }
@@ -2990,12 +2999,12 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
   }
 
   /**
-   * 更新所有桌面卡片状态
+   * 更新所有桌面卡片状态(优化为UI优先响应)
    * 将VideoItem和PlayerState数据传入桌面卡片,控制卡片的显示
    */
   async updateAllForms(forceUpdate: boolean = false) {
     // 防抖和重入保护,防止短时间内重复执行更新
-    if (this.isUpdatingForms) {
+    if (this.isUpdatingForms && !forceUpdate) {
       LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: Update already in progress, skipping.');
       return;
     }
@@ -3009,38 +3018,44 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
         return;
       }
 
-      // 根据存储配置获取Form列表
-      let formInfoList: FormInfo[] = [];
-      if (WIDGET_DATA_STORAGE_TYPE === WidgetDataStorageType.DATABASE) {
-        formInfoList = await FormRdbHelper.getInstance(context).queryAllForms();
-      } else {
-        // 从Preferences获取Form列表
-        formInfoList = await this.getFormInfoListFromPreferences(context);
-      }
+      // 优化:异步获取Form列表,不阻塞UI更新
+      const formInfoListPromise = this.getFormInfoListAsync(context);
       
-      if (formInfoList.length === 0) {
-        LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: No forms found');
-        return;
-      }
-
-      // --- 单阶段更新策略 ---
-      // 直接获取包含所有数据的卡片信息,包括图片。
-      // 图片加载有缓存机制,对于已加载过的歌曲,此操作很快。
-      // 这可以避免双阶段更新导致的UI闪烁问题(例如背景色先变默认再变主色调)。
-      const widgetData = await this.getWidgetFormData(); // true: 加载图片
+      // 优化:立即获取卡片数据,优先响应UI
+      const widgetData = await this.getWidgetFormData();
       if (!widgetData) {
         LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: Failed to get widget data.');
         return;
       }
 
       const bindingData = formBindingData.createFormBindingData(widgetData);
-      // 并行更新所有卡片,提高效率
-      const updatePromises = formInfoList.map(formInfo =>
-        formProvider.updateForm(formInfo.formId, bindingData)
-      );
+      
+      // 等待Form列表获取完成
+      const formInfoList = await formInfoListPromise;
+      if (formInfoList.length === 0) {
+        LogUtils.getInstance().LOGI('UnifiedPlayerService updateAllForms: No forms found');
+        return;
+      }
+
+      // 优化:并行更新所有卡片,使用批量更新提高效率
+      const updatePromises = formInfoList.map(formInfo => {
+        // 每个卡片更新都是异步的,避免阻塞
+        return formProvider.updateForm(formInfo.formId, bindingData).catch((error: Error) => {
+          LogUtils.getInstance().LOGI(`Failed to update form ${formInfo.formId}: ${error.message}`);
+        });
+      });
+      
       await Promise.all(updatePromises);
       LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService: All forms updated for ${formInfoList.length} forms.`);
 
+      // 异步保存数据到数据库,不阻塞UI
+      setTimeout(() => {
+        const currentSong = this.getCurrentSong();
+        if (currentSong) {
+          this.saveWidgetDataToDatabase(widgetData, currentSong);
+        }
+      }, 0);
+
     } catch (error) {
       LogUtils.getInstance().LOGI(`Heanup UnifiedPlayerService updateAllForms error: ${error}`);
     } finally {
@@ -3048,6 +3063,22 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
     }
   }
 
+  /**
+   * 异步获取FormInfo列表(优化方法)
+   */
+  private async getFormInfoListAsync(context: Context): Promise<FormInfo[]> {
+    try {
+      if (WIDGET_DATA_STORAGE_TYPE === WidgetDataStorageType.DATABASE) {
+        return await FormRdbHelper.getInstance(context).queryAllForms();
+      } else {
+        return await this.getFormInfoListFromPreferences(context);
+      }
+    } catch (error) {
+      LogUtils.getInstance().LOGI(`Failed to get form info list: ${error}`);
+      return [];
+    }
+  }
+
   public async getWidgetFormData(loadImage: boolean = true): Promise<widgeData | undefined> {
     this.updatePlaylistStateInModelSync();
     const formData: VideoItem = this.getCurrentSong()!; // VideoItem
@@ -3172,12 +3203,27 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
     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,系统会在处理完卡片数据后关闭它
+        // 优化:复用已缓存的图片文件描述符,避免重复文件操作
+        const currentSongPath = cachedData.finalImagePath;
+        const cacheKey = `img_${currentSongPath.replace(/[^a-zA-Z0-9]/g, '_')}`;
+        
+        // 检查是否有缓存的图片名称,避免重复生成
+        if (this.lastImageCache && this.lastImageCache.path === currentSongPath && this.lastImageCache.imgName) {
+          imgName = this.lastImageCache.imgName;
+          const file = fileIo.openSync(currentSongPath, fileIo.OpenMode.READ_ONLY);
+          imgMap[imgName] = file.fd;
+        } else {
+          // 仅在图片真正改变时才重新打开文件
+          const file = fileIo.openSync(currentSongPath, fileIo.OpenMode.READ_ONLY);
+          imgName = `songCover_${cacheKey}`;
+          imgMap[imgName] = file.fd;
+          
+          // 缓存当前图片信息
+          this.lastImageCache = {
+            path: currentSongPath,
+            imgName: imgName
+          } as ImageCacheInfo;
+        }
       } catch (error) {
         LogUtils.getInstance().LOGI(`UnifiedPlayerService: Failed to open image file [${cachedData.finalImagePath}]: ${JSON.stringify(error)}`);
         imgMap = {};
@@ -3527,8 +3573,15 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
         return;
       }
 
+      // 优化:确保使用最新的歌曲数据,避免状态不一致
       const currentSong = this.playlistModel.getCurrentSong();
       const currentPosition = this.getCurrentPosition();
+      
+      // 添加数据一致性检查
+      if (!currentSong) {
+        LogUtils.getInstance().LOGI('UnifiedPlayerService: No current song available for AVSession update');
+        return;
+      }
 
       // 优先从播放器获取实际duration
       let duration = 0;
@@ -3581,35 +3634,42 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
 
 
   /**
-   * 更新AVSession元数据(带防抖和智能更新
+   * 更新AVSession元数据(优化防抖逻辑,修复竞态条件
    */
   private async updateAvSessionMetadata(song: VideoItem): Promise<void> {
-    // 清除之前的定时器
+    // 清除之前的定时器,避免重复更新
     if (this.avMetadataUpdateTimer !== -1) {
       clearTimeout(this.avMetadataUpdateTimer);
+      this.avMetadataUpdateTimer = -1;
     }
+    
+    // 优化:减少防抖时间,提高响应速度
     const now = Date.now();
-    if (now - this.lastAvSessionUpdateTime < 1000) {
+    if (now - this.lastAvSessionUpdateTime < 500) {
+      // 如果更新过于频繁,延迟到下一个时间窗口
+      this.avMetadataUpdateTimer = setTimeout(() => {
+        this.doUpdateAvSessionMetadata(song);
+      }, 500 - (now - this.lastAvSessionUpdateTime));
       return;
     }
+    
     this.lastAvSessionUpdateTime = now;
-    // 如果播放器还没准备好,延迟更新等待播放器准备完成
+    
+    // 检查播放器状态,决定更新时机
     const ijkPlayer = this.playerManager.getIjkPlayer();
     const playerDuration = ijkPlayer ? ijkPlayer.getDuration() : 0;
     const shouldWaitForPlayer = playerDuration <= 0 && this.isPlayerPreparedForCurrentSong === false;
-    this.avMetadataUpdateTimer = setTimeout(() => {
-      this.doUpdateAvSessionMetadata(song);
-    }, 800);
+    
     if (shouldWaitForPlayer) {
-      // 播放器还没准备好,延迟更新
+      // 播放器还没准备好,适当延迟
       this.avMetadataUpdateTimer = setTimeout(() => {
         this.doUpdateAvSessionMetadata(song);
-      }, 800);
+      }, 300);
     } else {
-      // 播放器已准备好或者不需要等待,稍微延迟更新
+      // 播放器已准备好,立即更新
       this.avMetadataUpdateTimer = setTimeout(() => {
         this.doUpdateAvSessionMetadata(song);
-      }, 800);
+      }, 50);
     }
   }
 
@@ -3623,9 +3683,23 @@ export class UnifiedPlayerService implements IPlayerService, PlaylistSyncListene
         return;
       }
 
-      // 避免过于频繁的元数据更新(最小间隔600ms)
+      // 关键修复:验证歌曲数据的一致性,确保使用最新的歌曲信息
+      const currentSong = this.playlistModel.getCurrentSong();
+      if (!currentSong || currentSong.filePath !== song.filePath) {
+        LogUtils.getInstance().LOGI(`UnifiedPlayerService: Song data inconsistency detected. Current: ${currentSong?.name}, Provided: ${song.name}`);
+        // 使用最新的歌曲数据
+        if (currentSong) {
+          song = currentSong;
+          LogUtils.getInstance().LOGI(`UnifiedPlayerService: Using latest song data: ${song.name}`);
+        } else {
+          LogUtils.getInstance().LOGI('UnifiedPlayerService: No current song available, skipping metadata update');
+          return;
+        }
+      }
+
+      // 减少防抖时间,提高响应速度
       const now = Date.now();
-      if (now - this.lastAvMetadataUpdate < 600) {
+      if (now - this.lastAvMetadataUpdate < 300) {
         LogUtils.getInstance().LOGI('UnifiedPlayerService: Skipping AVSession metadata update due to rate limit');
         return;
       }

+ 34 - 43
entry/src/main/ets/database/WidgetDataRdbHelper.ets

@@ -108,50 +108,41 @@ export class WidgetDataRdbHelper {
   }
 
   /**
-   * 插入或更新Widget数据信息
+   * 插入或更新Widget数据信息(优化为异步非阻塞)
    * 使用 REPLACE INTO 语句,如果记录存在则更新,不存在则插入
    */
   async insertOrUpdateWidgetData(widgetDataInfo: WidgetDataInfo): Promise<void> {
-    try {
-      const store = await this.getRdbStore();
-      
-      // 先删除现有记录(只保留最新的widget数据)
-      await this.clearAllWidgetData();
-      
-      const valueBucket: relationalStore.ValuesBucket = {
-        'song_id': widgetDataInfo.songId,
-        'name': widgetDataInfo.name,
-        'artist': widgetDataInfo.artist,
-        'album': widgetDataInfo.album,
-        'pixel_map_path': widgetDataInfo.pixelMapPath,
-        'duration': widgetDataInfo.duration,
-        'file_path': widgetDataInfo.filePath,
-        'img_name': widgetDataInfo.imgName,
-        'image_color_hex': widgetDataInfo.imageColorHex,
-        'is_favorite': widgetDataInfo.isFavorite ? 1 : 0,
-        'is_playing': widgetDataInfo.isPlaying ? 1 : 0,
-        'is_paused': widgetDataInfo.isPaused ? 1 : 0,
-        'is_loading': widgetDataInfo.isLoading ? 1 : 0,
-        'current_position': widgetDataInfo.currentPosition,
-        'has_next': widgetDataInfo.hasNext ? 1 : 0,
-        'has_previous': widgetDataInfo.hasPrevious ? 1 : 0,
-        'play_mode': widgetDataInfo.playMode,
-        'current_index': widgetDataInfo.currentIndex,
-        'total_count': widgetDataInfo.totalCount,
-        'current_time_text': widgetDataInfo.currentTimeText,
-        'total_time_text': widgetDataInfo.totalTimeText,
-        'progress_percentage': widgetDataInfo.progressPercentage,
-        'create_time': widgetDataInfo.createTime,
-        'update_time': new Date().toISOString(),
-      };
-      
-      await store.insert(WidgetDataRdbHelper.WIDGET_DATA_TABLE, valueBucket);
-      hilog.info(0x0000, TAG, `Widget data inserted successfully for song: ${widgetDataInfo.name}`);
-    } catch (error) {
-      const businessError = error as BusinessError;
-      hilog.error(0x0000, TAG, `Failed to insert widget data: ${businessError.message}`);
-      throw new Error(businessError.message);
-    }
+    // 异步执行,不阻塞调用者
+    setTimeout(async () => {
+      try {
+        const store = await this.getRdbStore();
+        
+        // 使用 REPLACE 语句代替先删除再插入,减少数据库操作
+        const sql = `REPLACE INTO ${WidgetDataRdbHelper.WIDGET_DATA_TABLE} (
+          song_id, name, artist, album, pixel_map_path, duration, file_path,
+          img_name, image_color_hex, is_favorite, is_playing, is_paused, is_loading,
+          current_position, has_next, has_previous, play_mode, current_index, total_count,
+          current_time_text, total_time_text, progress_percentage, create_time, update_time
+        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
+        
+        const args = [
+          widgetDataInfo.songId, widgetDataInfo.name, widgetDataInfo.artist, widgetDataInfo.album,
+          widgetDataInfo.pixelMapPath, widgetDataInfo.duration, widgetDataInfo.filePath,
+          widgetDataInfo.imgName, widgetDataInfo.imageColorHex, widgetDataInfo.isFavorite ? 1 : 0,
+          widgetDataInfo.isPlaying ? 1 : 0, widgetDataInfo.isPaused ? 1 : 0, widgetDataInfo.isLoading ? 1 : 0,
+          widgetDataInfo.currentPosition, widgetDataInfo.hasNext ? 1 : 0, widgetDataInfo.hasPrevious ? 1 : 0,
+          widgetDataInfo.playMode, widgetDataInfo.currentIndex, widgetDataInfo.totalCount,
+          widgetDataInfo.currentTimeText, widgetDataInfo.totalTimeText, widgetDataInfo.progressPercentage,
+          widgetDataInfo.createTime, new Date().toISOString()
+        ];
+        
+        await store.executeSql(sql, args);
+        hilog.info(0x0000, TAG, `Widget data updated asynchronously for song: ${widgetDataInfo.name}`);
+      } catch (error) {
+        const businessError = error as BusinessError;
+        hilog.error(0x0000, TAG, `Failed to update widget data asynchronously: ${businessError.message}`);
+      }
+    }, 0);
   }
 
   /**
@@ -296,8 +287,8 @@ export class WidgetDataRdbHelper {
     widgetDataInfo.isPaused = resultSet.getLong(resultSet.getColumnIndex('is_paused')) === 1;
     widgetDataInfo.isLoading = resultSet.getLong(resultSet.getColumnIndex('is_loading')) === 1;
     widgetDataInfo.currentPosition = resultSet.getLong(resultSet.getColumnIndex('current_position'));
-    widgetDataInfo.hasNext = resultSet.getLong(resultSet.getColumnIndex('has_next')) === 1;
-    widgetDataInfo.hasPrevious = resultSet.getLong(resultSet.getColumnIndex('has_previous')) === 1;
+    widgetDataInfo.hasNext = true;
+    widgetDataInfo.hasPrevious = true;
     widgetDataInfo.playMode = resultSet.getLong(resultSet.getColumnIndex('play_mode'));
     widgetDataInfo.currentIndex = resultSet.getLong(resultSet.getColumnIndex('current_index'));
     widgetDataInfo.totalCount = resultSet.getLong(resultSet.getColumnIndex('total_count'));

+ 84 - 43
entry/src/main/ets/entryability/EntryAbility.ets

@@ -432,16 +432,28 @@ export default class EntryAbility extends UIAbility {
    */
   private registerWidgetCallListeners(): void {
     try {
-      // 监听播放/暂停事件
+      // 监听播放/暂停事件(优化RPC处理)
       this.callee.on('playPause', (data: rpc.MessageSequence) => {
         try {
-          const params: Record<string, Object> = JSON.parse(data.readString()) as Record<string, Object>;
           hilog.info(0x0000, 'Heanup2', `🎵 Widget call: playPause received`);
+          
+          // 优化:简化参数解析,减少JSON序列化开销
+          let params: Record<string, Object> = {};
+          const dataString = data.readString();
+          if (dataString && dataString.length > 0) {
+            try {
+              params = JSON.parse(dataString) as Record<string, Object>;
+            } catch (parseError) {
+              params = {};
+            }
+          }
 
-          // 异步发送播放/暂停事件到主应用(包含服务就绪检查)
-          this.sendWidgetControlEvent('PLAY_PAUSE', params).catch((error: Error) => {
-            hilog.error(0x0000, 'Heanup2', `❌ playPause async error: ${error}`);
-          });
+          // 优化:立即返回成功状态,异步处理业务逻辑
+          setTimeout(() => {
+            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) {
@@ -450,17 +462,29 @@ export default class EntryAbility extends UIAbility {
         }
       });
 
-      // 监听下一首事件
+      // 监听下一首事件(优化RPC处理)
       this.callee.on('nextSong', (data: rpc.MessageSequence) => {
         try {
           hilog.info(0x0000, 'Heanup2', `🎵 Widget call: nextSong received`);
-          const params: Record<string, Object> = JSON.parse(data.readString()) as Record<string, Object>;
-          hilog.info(0x0000, 'Heanup2', `Widget nextSong params: ${JSON.stringify(params)}`);
+          
+          // 优化:简化参数解析,减少JSON序列化开销
+          let params: Record<string, Object> = {};
+          const dataString = data.readString();
+          if (dataString && dataString.length > 0) {
+            try {
+              params = JSON.parse(dataString) as Record<string, Object>;
+            } catch (parseError) {
+              // 解析失败时使用空对象,不影响功能
+              params = {};
+            }
+          }
 
-          // 异步发送下一首事件到主应用(包含服务就绪检查)
-          this.sendWidgetControlEvent('NEXT_SONG', params).catch((error: Error) => {
-            hilog.error(0x0000, 'Heanup2', `❌ nextSong async error: ${error}`);
-          });
+          // 优化:立即返回成功状态,异步处理业务逻辑
+          setTimeout(() => {
+            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) {
@@ -469,17 +493,28 @@ export default class EntryAbility extends UIAbility {
         }
       });
 
-      // 监听上一首事件
+      // 监听上一首事件(优化RPC处理)
       this.callee.on('prevSong', (data: rpc.MessageSequence) => {
         try {
           hilog.info(0x0000, 'Heanup2', `🎵 Widget call: prevSong received`);
-          const params: Record<string, Object> = JSON.parse(data.readString()) as Record<string, Object>;
-          hilog.info(0x0000, 'Heanup2', `Widget prevSong params: ${JSON.stringify(params)}`);
+          
+          // 优化:简化参数解析,减少JSON序列化开销
+          let params: Record<string, Object> = {};
+          const dataString = data.readString();
+          if (dataString && dataString.length > 0) {
+            try {
+              params = JSON.parse(dataString) as Record<string, Object>;
+            } catch (parseError) {
+              params = {};
+            }
+          }
 
-          // 异步发送上一首事件到主应用(包含服务就绪检查)
-          this.sendWidgetControlEvent('PREV_SONG', params).catch((error: Error) => {
-            hilog.error(0x0000, 'Heanup2', `❌ prevSong async error: ${error}`);
-          });
+          // 优化:立即返回成功状态,异步处理业务逻辑
+          setTimeout(() => {
+            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) {
@@ -544,7 +579,7 @@ export default class EntryAbility extends UIAbility {
 
 
   /**
-   * 发送卡片控制事件到主应用(优化版本,使用预缓存的服务状态
+   * 发送卡片控制事件到主应用(优化版本,快速响应
    */
   private async sendWidgetControlEvent(command: string, params: Record<string, Object>): Promise<void> {
     try {
@@ -552,49 +587,55 @@ export default class EntryAbility extends UIAbility {
         hilog.error(0x0000, 'Heanup2', '❌ UnifiedPlayerService 未初始化');
         return;
       }
-      // 直接执行命令 - 移除复杂的状态检查
+      
+      // 优化:立即执行命令,减少状态检查开销
+      let commandPromise: Promise<void>;
+      
       switch (command) {
         case 'PLAY_PAUSE':
-          // 简化版本:基于服务状态直接切换
-          const currentState = this.unifiedService.getCurrentState();
-          if (currentState.isPlaying) {
-            await this.unifiedService.pause();
+          // 优化:直接基于参数判断,避免重复状态查询
+          const widgetIsPlaying = params['widgetIsPlaying'] as boolean;
+          if (widgetIsPlaying !== undefined) {
+            commandPromise = widgetIsPlaying ? this.unifiedService.pause() : this.unifiedService.startPlayOrResumePlay();
           } else {
-            await this.unifiedService.startPlayOrResumePlay();
+            // 回退到状态检查
+            const currentState = this.unifiedService.getCurrentState();
+            commandPromise = currentState.isPlaying ? this.unifiedService.pause() : this.unifiedService.startPlayOrResumePlay();
           }
           break;
 
         case 'NEXT_SONG':
-          await this.unifiedService.playNext();
+          commandPromise = this.unifiedService.playNext();
           break;
 
         case 'PREV_SONG':
-          await this.unifiedService.playPrevious();
+          commandPromise = this.unifiedService.playPrevious();
           break;
 
         case 'OPEN_APP':
-          // 打开应用由UI处理
-          break;
+          // 打开应用由UI处理,直接返回
+          return;
 
         case "toggleFavorite":
-          await this.unifiedService.toggleFavorite();
+          commandPromise = this.unifiedService.toggleFavorite();
           break;
 
         default:
           hilog.warn(0x0000, 'Heanup2', `❓ 未知widget命令: ${command}`);
-          break;
+          return;
       }
 
-      // 立即广播状态更新
-      try {
-        setTimeout( ()=>{
-          this.unifiedService.broadcastCurrentState();
-        },0)
-        // this.unifiedService.broadcastCurrentState();
-        hilog.info(0x0000, 'Heanup2', `✅ Widget命令执行完成: ${command}`);
-      } catch (error) {
-        hilog.error(0x0000, 'Heanup2', `❌ 状态广播失败: ${error}`);
-      }
+      // 执行命令
+      await commandPromise;
+      
+      // 优化:异步广播状态更新,不阻塞命令执行
+      setTimeout(() => {
+        this.unifiedService.broadcastCurrentState().catch((error: Error) => {
+          hilog.error(0x0000, 'Heanup2', `❌ 状态广播失败: ${error.message}`);
+        });
+      }, 0);
+      
+      hilog.info(0x0000, 'Heanup2', `✅ Widget命令执行完成: ${command}`);
 
     } catch (error) {
       hilog.error(0x0000, 'Heanup2', `❌ Failed to process widget control command: ${error}`);