Browse Source

单卡片正常

chendeben 1 year ago
parent
commit
4acb91bc01

+ 40 - 12
entry/src/main/ets/common/widget/AvSessionWidgetListener.ets

@@ -8,14 +8,19 @@ const TAG = 'Heanup AvSessionWidgetListener';
  * AvSession卡片监听器
  * AvSession卡片监听器
  * 负责监听媒体会话状态变化并同步到卡片
  * 负责监听媒体会话状态变化并同步到卡片
  * 简化版本,避免API兼容性问题
  * 简化版本,避免API兼容性问题
+ * 支持跨进程数据同步
  */
  */
 export class AvSessionWidgetListener {
 export class AvSessionWidgetListener {
   private static instance: AvSessionWidgetListener | null = null;
   private static instance: AvSessionWidgetListener | null = null;
   private stateListeners: Array<(data: WidgetData) => void> = [];
   private stateListeners: Array<(data: WidgetData) => void> = [];
   private isInitialized: boolean = false;
   private isInitialized: boolean = false;
   private lastWidgetData: WidgetData | null = null;
   private lastWidgetData: WidgetData | null = null;
+  private processId: string = '';
 
 
   private constructor() {
   private constructor() {
+    // 生成进程唯一标识
+    this.processId = `process_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
+    hilog.info(0x0000, TAG, `AvSessionWidgetListener created for process: ${this.processId}`);
     this.initializeAvSessionManager();
     this.initializeAvSessionManager();
   }
   }
 
 
@@ -51,35 +56,58 @@ export class AvSessionWidgetListener {
       return;
       return;
     }
     }
     
     
-    // 如果有缓存的数据,立即返回
-    if (this.lastWidgetData) {
-      callback(this.lastWidgetData);
-    } else {
-      // 否则返回默认数据
-      const defaultData = this.getDefaultWidgetData();
-      callback(defaultData);
-    }
+    // 延迟回调,给主应用时间广播当前状态
+    setTimeout(() => {
+      // 如果有缓存的数据,立即返回
+      if (this.lastWidgetData) {
+        hilog.info(0x0000, TAG, `Sending cached data to new listener: isPlaying=${this.lastWidgetData.playState.isPlaying}, title=${this.lastWidgetData.currentSong.title}`);
+        callback(this.lastWidgetData);
+      } else {
+        // 否则返回默认数据
+        const defaultData = this.getDefaultWidgetData();
+        hilog.info(0x0000, TAG, `Sending default data to new listener: isPlaying=${defaultData.playState.isPlaying}, title=${defaultData.currentSong.title}`);
+        callback(defaultData);
+      }
+    }, 500); // 延迟500ms,给主应用时间广播状态
   }
   }
 
 
   /**
   /**
    * 更新卡片数据(由主应用调用)
    * 更新卡片数据(由主应用调用)
    */
    */
   public updateWidgetData(data: WidgetData): void {
   public updateWidgetData(data: WidgetData): void {
+    // 检查数据是否真的发生了变化,避免无意义的更新
+    if (this.lastWidgetData && this.isDataEqual(this.lastWidgetData, data)) {
+      hilog.info(0x0000, TAG, `[${this.processId}] Data unchanged, skipping update`);
+      return;
+    }
+    
     this.lastWidgetData = data;
     this.lastWidgetData = data;
     
     
-    hilog.info(0x0000, TAG, `Updating widget data: isPlaying=${data.playState.isPlaying}, title=${data.currentSong.title}, listeners=${this.stateListeners.length}`);
+    hilog.info(0x0000, TAG, `[${this.processId}] Updating widget data: isPlaying=${data.playState.isPlaying}, title=${data.currentSong.title}, listeners=${this.stateListeners.length}`);
     
     
     // 通知所有监听器
     // 通知所有监听器
     this.stateListeners.forEach((listener: (data: WidgetData) => void, index: number) => {
     this.stateListeners.forEach((listener: (data: WidgetData) => void, index: number) => {
       try {
       try {
-        hilog.info(0x0000, TAG, `Notifying listener ${index}`);
+        hilog.info(0x0000, TAG, `[${this.processId}] Notifying listener ${index}`);
         listener(data);
         listener(data);
       } catch (error) {
       } catch (error) {
-        hilog.error(0x0000, TAG, `Error in state listener ${index}: ${error}`);
+        hilog.error(0x0000, TAG, `[${this.processId}] Error in state listener ${index}: ${error}`);
       }
       }
     });
     });
     
     
-    hilog.info(0x0000, TAG, `Widget data updated and broadcasted to ${this.stateListeners.length} listeners`);
+    hilog.info(0x0000, TAG, `[${this.processId}] Widget data updated and broadcasted to ${this.stateListeners.length} listeners`);
+  }
+
+  /**
+   * 检查两个WidgetData是否相等(简化版本,只检查关键字段)
+   */
+  private isDataEqual(data1: WidgetData, data2: WidgetData): boolean {
+    return data1.playState.isPlaying === data2.playState.isPlaying &&
+           data1.currentSong.title === data2.currentSong.title &&
+           data1.currentSong.id === data2.currentSong.id &&
+           data1.playlist.hasNext === data2.playlist.hasNext &&
+           data1.playlist.hasPrevious === data2.playlist.hasPrevious &&
+           Math.abs(data1.progress.percentage - data2.progress.percentage) < 0.1; // 进度变化小于0.1%时忽略
   }
   }
 
 
   /**
   /**

+ 47 - 31
entry/src/main/ets/common/widget/PlayerControlService.ets

@@ -78,11 +78,40 @@ export class PlayerControlService {
 
 
       this.isListenerRegistered = true;
       this.isListenerRegistered = true;
       hilog.info(0x0000, TAG, 'Event listener initialized successfully');
       hilog.info(0x0000, TAG, 'Event listener initialized successfully');
+      
+      // 立即请求当前状态,确保新进程能获取到最新数据
+      setTimeout(() => {
+        this.requestCurrentState();
+      }, 1000);
     } catch (error) {
     } catch (error) {
       hilog.error(0x0000, TAG, `Failed to initialize event listener: ${error}`);
       hilog.error(0x0000, TAG, `Failed to initialize event listener: ${error}`);
     }
     }
   }
   }
 
 
+  /**
+   * 请求当前播放状态
+   */
+  private async requestCurrentState(): Promise<void> {
+    try {
+      const requestData: RequestData = { 
+        timestamp: Date.now(),
+        source: 'widget_form_process'
+      };
+      const requestInfo: commonEventManager.CommonEventPublishData = {
+        data: JSON.stringify(requestData)
+      };
+      await commonEventManager.publish(WIDGET_REQUEST_STATE_EVENT, requestInfo, (err) => {
+        if (err) {
+          hilog.error(0x0000, TAG, `Failed to request current state: ${err}`);
+        } else {
+          hilog.info(0x0000, TAG, 'Current state requested from main app');
+        }
+      });
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Failed to request current state: ${error}`);
+    }
+  }
+
   /**
   /**
    * 发送控制命令到主应用
    * 发送控制命令到主应用
    */
    */
@@ -123,7 +152,10 @@ export class PlayerControlService {
       const avSessionData = this.avSessionListener.getCurrentWidgetData();
       const avSessionData = this.avSessionListener.getCurrentWidgetData();
       
       
       // 同时请求CommonEvent状态作为备用
       // 同时请求CommonEvent状态作为备用
-      const requestData: RequestData = { timestamp: Date.now() };
+      const requestData: RequestData = {
+        timestamp: Date.now(),
+        source: 'widget_form_process'
+      };
       const requestInfo: commonEventManager.CommonEventPublishData = {
       const requestInfo: commonEventManager.CommonEventPublishData = {
         data: JSON.stringify(requestData)
         data: JSON.stringify(requestData)
       };
       };
@@ -145,19 +177,9 @@ export class PlayerControlService {
    */
    */
   private initializeAvSessionListener(): void {
   private initializeAvSessionListener(): void {
     try {
     try {
-      // 注册AvSession状态监听器
-      this.avSessionListener.addStateListener((data: WidgetData) => {
-        // 将AvSession的状态变化转发给所有监听器
-        this.stateListeners.forEach((listener: (data: WidgetData) => void) => {
-          try {
-            listener(data);
-          } catch (error) {
-            hilog.error(0x0000, TAG, `Error in AvSession state listener: ${error}`);
-          }
-        });
-      });
-      
-      hilog.info(0x0000, TAG, 'AvSession listener initialized successfully');
+      // 注册AvSession状态监听器,这个监听器主要用于EntryFormAbility的全局监听
+      // 不要在这里注册,让EntryFormAbility直接注册到AvSessionWidgetListener
+      hilog.info(0x0000, TAG, 'AvSession listener initialized successfully (no direct registration needed)');
     } catch (error) {
     } catch (error) {
       hilog.error(0x0000, TAG, `Failed to initialize AvSession listener: ${error}`);
       hilog.error(0x0000, TAG, `Failed to initialize AvSession listener: ${error}`);
     }
     }
@@ -167,7 +189,14 @@ export class PlayerControlService {
    * 注册状态变化监听器
    * 注册状态变化监听器
    */
    */
   registerStateListener(callback: (data: WidgetData) => void): void {
   registerStateListener(callback: (data: WidgetData) => void): void {
-    this.stateListeners.push(callback);
+    // 检查是否已经注册过相同的监听器,避免重复注册
+    if (this.stateListeners.indexOf(callback) === -1) {
+      this.stateListeners.push(callback);
+      hilog.info(0x0000, TAG, `State listener registered, total listeners: ${this.stateListeners.length}`);
+    } else {
+      hilog.warn(0x0000, TAG, 'State listener already registered, skipping');
+      return;
+    }
     
     
     // 延迟获取当前状态,给主应用时间来广播真实状态
     // 延迟获取当前状态,给主应用时间来广播真实状态
     setTimeout(() => {
     setTimeout(() => {
@@ -179,8 +208,6 @@ export class PlayerControlService {
         hilog.error(0x0000, TAG, `Error getting current AvSession data: ${error}`);
         hilog.error(0x0000, TAG, `Error getting current AvSession data: ${error}`);
       }
       }
     }, 1500); // 延迟1.5秒,确保主应用已经广播了状态
     }, 1500); // 延迟1.5秒,确保主应用已经广播了状态
-    
-    hilog.info(0x0000, TAG, 'State listener registered');
   }
   }
 
 
   /**
   /**
@@ -265,22 +292,11 @@ export class PlayerControlService {
         widgetData = this.convertToWidgetData(data);
         widgetData = this.convertToWidgetData(data);
       }
       }
       
       
-      hilog.info(0x0000, TAG, `Notifying ${this.stateListeners.length} CommonEvent listeners`);
-      
-      // 通知所有监听器
-      this.stateListeners.forEach((listener: (data: WidgetData) => void, index: number) => {
-        try {
-          hilog.info(0x0000, TAG, `Calling CommonEvent listener ${index}`);
-          listener(widgetData);
-        } catch (error) {
-          hilog.error(0x0000, TAG, `Error in CommonEvent state listener ${index}: ${error}`);
-        }
-      });
-      
-      // 同时更新AvSession监听器的数据,确保数据同步
+      // 只更新AvSession监听器的数据,避免重复通知
+      // AvSession监听器会自动通知所有注册的监听器
       this.avSessionListener.updateWidgetData(widgetData);
       this.avSessionListener.updateWidgetData(widgetData);
       
       
-      hilog.info(0x0000, TAG, `Player ${eventData.event} handled, all listeners notified`);
+      hilog.info(0x0000, TAG, `Player ${eventData.event} handled, data updated in AvSession`);
     } catch (error) {
     } catch (error) {
       hilog.error(0x0000, TAG, `Failed to handle player state change: ${error}`);
       hilog.error(0x0000, TAG, `Failed to handle player state change: ${error}`);
     }
     }

+ 1 - 0
entry/src/main/ets/common/widget/WidgetTypes.ets

@@ -157,6 +157,7 @@ export interface EventData {
  */
  */
 export interface RequestData {
 export interface RequestData {
   timestamp: number;
   timestamp: number;
+  source: String;
 }
 }
 
 
 /**
 /**

+ 105 - 34
entry/src/main/ets/entryformability/EntryFormAbility.ets

@@ -71,27 +71,31 @@ implements SizeChangeListener, ThemeChangeListener, PreferencesChangeListener {
   
   
   // 使用实例变量而不是静态变量,确保每个进程都能正确设置监听器
   // 使用实例变量而不是静态变量,确保每个进程都能正确设置监听器
   private globalListenerSetup: boolean = false;
   private globalListenerSetup: boolean = false;
+  
+  // 防抖机制:避免短时间内重复更新
+  private lastUpdateTime: number = 0;
+  private updateDebounceDelay: number = 100; // 100ms防抖延迟
 
 
   /**
   /**
    * 初始化服务
    * 初始化服务
    */
    */
   private initializeServices(): void {
   private initializeServices(): void {
-    hilog.info(0x0000, TAG, 'Heanup widget initializeServices called');
+    hilog.info(0x0000, TAG, 'Heanup EntryFormAbility initializeServices called');
     
     
     if (!this.widgetDataManager) {
     if (!this.widgetDataManager) {
       this.widgetDataManager = new WidgetDataManager();
       this.widgetDataManager = new WidgetDataManager();
-      hilog.info(0x0000, TAG, 'WidgetDataManager initialized');
+      hilog.info(0x0000, TAG, 'Heanup EntryFormAbility WidgetDataManager initialized');
     }
     }
     
     
     // 设置全局状态监听器(每个进程实例设置一次)
     // 设置全局状态监听器(每个进程实例设置一次)
-    hilog.info(0x0000, TAG, `Heanup widget checking global listener setup: ${this.globalListenerSetup}`);
+    hilog.info(0x0000, TAG, `Heanup EntryFormAbility checking global listener setup: ${this.globalListenerSetup}`);
     if (!this.globalListenerSetup) {
     if (!this.globalListenerSetup) {
-      hilog.info(0x0000, TAG, 'Heanup widget setting up global listener...');
+      hilog.info(0x0000, TAG, 'Heanup EntryFormAbility setting up global listener...');
       this.setupGlobalStateListener();
       this.setupGlobalStateListener();
       this.globalListenerSetup = true;
       this.globalListenerSetup = true;
-      hilog.info(0x0000, TAG, 'Heanup widget global listener setup flag set to true');
+      hilog.info(0x0000, TAG, 'Heanup EntryFormAbility global listener setup flag set to true');
     } else {
     } else {
-      hilog.info(0x0000, TAG, 'Heanup widget global listener already setup, skipping');
+      hilog.info(0x0000, TAG, 'Heanup EntryFormAbility global listener already setup, skipping');
     }
     }
   }
   }
 
 
@@ -100,17 +104,19 @@ implements SizeChangeListener, ThemeChangeListener, PreferencesChangeListener {
    */
    */
   private setupGlobalStateListener(): void {
   private setupGlobalStateListener(): void {
     try {
     try {
+      hilog.info(0x0000, TAG, 'Heanup EntryFormAbility getting AvSessionWidgetListener instance...');
       const avSessionListener = AvSessionWidgetListener.getInstance();
       const avSessionListener = AvSessionWidgetListener.getInstance();
+      hilog.info(0x0000, TAG, 'Heanup EntryFormAbility adding state listener...');
       avSessionListener.addStateListener((data: WidgetData) => {
       avSessionListener.addStateListener((data: WidgetData) => {
-        hilog.info(0x0000, TAG, `Heanup widget global listener received state update: isPlaying=${data.playState.isPlaying}, title=${data.currentSong.title}`);
+        hilog.info(0x0000, TAG, `Heanup EntryFormAbility global listener received state update: isPlaying=${data.playState.isPlaying}, title=${data.currentSong.title}`);
         
         
         // 总是尝试更新,让updateAllWidgetsWithData自己检查
         // 总是尝试更新,让updateAllWidgetsWithData自己检查
         this.updateAllWidgetsWithData(data);
         this.updateAllWidgetsWithData(data);
       });
       });
       
       
-      hilog.info(0x0000, TAG, 'Heanup widget global state listener setup successfully');
+      hilog.info(0x0000, TAG, 'Heanup EntryFormAbility global state listener setup successfully');
     } catch (error) {
     } catch (error) {
-      hilog.error(0x0000, TAG, `Heanup widget failed to setup global listener: ${error}`);
+      hilog.error(0x0000, TAG, `Heanup EntryFormAbility failed to setup global listener: ${error}`);
     }
     }
   }
   }
 
 
@@ -118,48 +124,88 @@ implements SizeChangeListener, ThemeChangeListener, PreferencesChangeListener {
    * 使用指定数据更新所有卡片
    * 使用指定数据更新所有卡片
    */
    */
   private updateAllWidgetsWithData(data: WidgetData): void {
   private updateAllWidgetsWithData(data: WidgetData): void {
+    hilog.info(0x0000, TAG, `Heanup EntryFormAbility updateAllWidgetsWithData called with isPlaying=${data.playState.isPlaying}, title=${data.currentSong.title}`);
+    
+    // 防抖机制:避免短时间内重复更新
+    const now = Date.now();
+    if (now - this.lastUpdateTime < this.updateDebounceDelay) {
+      hilog.info(0x0000, TAG, `Heanup EntryFormAbility update debounced, skipping (${now - this.lastUpdateTime}ms since last update)`);
+      return;
+    }
+    this.lastUpdateTime = now;
+    
     const activeWidgets = this.globalWidgetManager.getActiveWidgets();
     const activeWidgets = this.globalWidgetManager.getActiveWidgets();
-    hilog.info(0x0000, TAG, `Heanup widget updateAllWidgetsWithData called, active widgets: ${activeWidgets.size}`);
+    hilog.info(0x0000, TAG, `Heanup EntryFormAbility active widgets count: ${activeWidgets.size}`);
+    
+    // 打印所有活跃的widget ID
+    if (activeWidgets.size > 0) {
+      activeWidgets.forEach((size: WidgetSize, formId: string) => {
+        hilog.info(0x0000, TAG, `Heanup EntryFormAbility found active widget: ${formId}, size: ${size}`);
+      });
+    }
     
     
     if (activeWidgets.size === 0) {
     if (activeWidgets.size === 0) {
-      hilog.warn(0x0000, TAG, 'Heanup widget no widgets to update in this process, skipping');
+      hilog.warn(0x0000, TAG, 'Heanup EntryFormAbility no widgets to update in this process, skipping');
       return;
       return;
     }
     }
     
     
     activeWidgets.forEach((size: WidgetSize, formId: string) => {
     activeWidgets.forEach((size: WidgetSize, formId: string) => {
-      try {
-        // 根据卡片尺寸适配数据
-        const adaptedData = this.layoutManager.adaptDataForSize(data, size);
-        
-        hilog.info(0x0000, TAG, `Heanup widget ${formId} calling formProvider.updateForm with isPlaying=${adaptedData.isPlaying}`);
-        
-        // 直接使用系统API更新卡片
-        const formData = formBindingData.createFormBindingData(adaptedData);
-        formProvider.updateForm(formId, formData).then(() => {
-          hilog.info(0x0000, TAG, `Heanup widget ${formId} updated successfully: isPlaying=${adaptedData.isPlaying}, title=${adaptedData.songTitle}`);
-        }).catch(() => {//不能传error参数
-          hilog.error(0x0000, TAG, `Heanup widget ${formId} update failed: `);
-        });
-      } catch (error) {
-        hilog.error(0x0000, TAG, `Heanup widget ${formId} process error: ${error}`);
-      }
+      hilog.info(0x0000, TAG, `Heanup EntryFormAbility updating widget: ${formId}`);
+      this.updateSingleWidget(formId, size, data);
     });
     });
     
     
-    hilog.info(0x0000, TAG, `Heanup widget batch update completed for ${activeWidgets.size} widgets`);
+    hilog.info(0x0000, TAG, `Heanup EntryFormAbility batch update completed for ${activeWidgets.size} widgets`);
+  }
+
+  /**
+   * 更新单个卡片,带重试机制
+   */
+  private updateSingleWidget(formId: string, size: WidgetSize, data: WidgetData, retryCount: number = 0): void {
+    try {
+      // 根据卡片尺寸适配数据
+      const adaptedData = this.layoutManager.adaptDataForSize(data, size);
+      
+      hilog.info(0x0000, TAG, `Heanup widget ${formId} calling formProvider.updateForm with isPlaying=${adaptedData.isPlaying}, hasNext=${adaptedData.hasNext}, hasPrevious=${adaptedData.hasPrevious}`);
+      
+      // 直接使用系统API更新卡片
+      const formData = formBindingData.createFormBindingData(adaptedData);
+      formProvider.updateForm(formId, formData).then(() => {
+        hilog.info(0x0000, TAG, `Heanup widget ${formId} updated successfully: isPlaying=${adaptedData.isPlaying}, title=${adaptedData.songTitle}, hasNext=${adaptedData.hasNext}, hasPrevious=${adaptedData.hasPrevious}`);
+      }).catch(() => {
+        hilog.error(0x0000, TAG, `Heanup widget ${formId} update failed, retry count: ${retryCount}`);
+        
+        // 重试机制:最多重试2次
+        if (retryCount < 2) {
+          setTimeout(() => {
+            this.updateSingleWidget(formId, size, data, retryCount + 1);
+          }, 1000 * (retryCount + 1)); // 递增延迟
+        }
+      });
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Heanup widget ${formId} process error: ${error}`);
+      
+      // 如果是处理错误,也尝试重试
+      if (retryCount < 2) {
+        setTimeout(() => {
+          this.updateSingleWidget(formId, size, data, retryCount + 1);
+        }, 1000 * (retryCount + 1));
+      }
+    }
   }
   }
 
 
   /**
   /**
    * 卡片创建时调用
    * 卡片创建时调用
    */
    */
   onAddForm(want: Want): formBindingData.FormBindingData {
   onAddForm(want: Want): formBindingData.FormBindingData {
-    hilog.info(0x0000, TAG, 'onAddForm called');
+    hilog.info(0x0000, TAG, 'Heanup EntryFormAbility onAddForm called');
     
     
     // 初始化服务
     // 初始化服务
     try {
     try {
+      hilog.info(0x0000, TAG, 'Heanup EntryFormAbility initializing services...');
       this.initializeServices();
       this.initializeServices();
-      hilog.info(0x0000, TAG, 'Services initialized successfully');
+      hilog.info(0x0000, TAG, 'Heanup EntryFormAbility services initialized successfully');
     } catch (error) {
     } catch (error) {
-      hilog.error(0x0000, TAG, `Failed to initialize services: ${error}`);
+      hilog.error(0x0000, TAG, `Heanup EntryFormAbility failed to initialize services: ${error}`);
     }
     }
 
 
     const formId = want.parameters?.['ohos.extra.param.key.form_identity'] as string;
     const formId = want.parameters?.['ohos.extra.param.key.form_identity'] as string;
@@ -252,7 +298,19 @@ implements SizeChangeListener, ThemeChangeListener, PreferencesChangeListener {
    * 卡片更新时调用
    * 卡片更新时调用
    */
    */
   onUpdateForm(formId: string): void {
   onUpdateForm(formId: string): void {
-    hilog.info(0x0000, TAG, `onUpdateForm called: ${formId}`);
+    hilog.info(0x0000, TAG, `Heanup EntryFormAbility onUpdateForm called: ${formId}`);
+
+    // 确保服务已初始化
+    if (!this.globalListenerSetup) {
+      hilog.info(0x0000, TAG, 'Heanup EntryFormAbility services not initialized, initializing now...');
+      this.initializeServices();
+    }
+
+    // 确保widget已注册到GlobalWidgetManager
+    if (!this.globalWidgetManager.hasWidget(formId)) {
+      hilog.info(0x0000, TAG, `Heanup EntryFormAbility widget ${formId} not registered in onUpdateForm, registering as LARGE size`);
+      this.globalWidgetManager.registerWidget(formId, 'large' as WidgetSize);
+    }
 
 
     // 只更新指定的卡片,避免重复更新
     // 只更新指定的卡片,避免重复更新
     this.updateWidgetData(formId);
     this.updateWidgetData(formId);
@@ -300,13 +358,26 @@ implements SizeChangeListener, ThemeChangeListener, PreferencesChangeListener {
    * 处理卡片事件(用户交互)
    * 处理卡片事件(用户交互)
    */
    */
   onFormEvent(formId: string, message: string): void {
   onFormEvent(formId: string, message: string): void {
-    hilog.info(0x0000, TAG, `onFormEvent called: ${formId}, message: ${message}`);
+    hilog.info(0x0000, TAG, `Heanup EntryFormAbility onFormEvent called: ${formId}, message: ${message}`);
+
+    // 确保服务已初始化
+    if (!this.globalListenerSetup) {
+      hilog.info(0x0000, TAG, 'Heanup EntryFormAbility services not initialized in onFormEvent, initializing now...');
+      this.initializeServices();
+    }
+
+    // 确保widget已注册到GlobalWidgetManager
+    if (!this.globalWidgetManager.hasWidget(formId)) {
+      hilog.info(0x0000, TAG, `Heanup EntryFormAbility widget ${formId} not registered, registering as LARGE size`);
+      // 默认注册为LARGE尺寸,实际尺寸可以后续检测
+      this.globalWidgetManager.registerWidget(formId, 'large' as WidgetSize);
+    }
 
 
     try {
     try {
       const eventData = JSON.parse(message) as Object;
       const eventData = JSON.parse(message) as Object;
       this.handleWidgetEvent(formId, eventData);
       this.handleWidgetEvent(formId, eventData);
     } catch (error) {
     } catch (error) {
-      hilog.error(0x0000, TAG, `Failed to parse form event: ${error}`);
+      hilog.error(0x0000, TAG, `Heanup EntryFormAbility failed to parse form event: ${error}`);
     }
     }
   }
   }
 
 

+ 113 - 0
test_widget_fix.md

@@ -0,0 +1,113 @@
+# 卡片更新机制修复测试
+
+## 修复内容
+
+### 问题分析
+1. **重复监听器问题**:每个卡片都注册了监听器,导致同一个状态更新被多次处理
+2. **部分卡片更新失败**:某些卡片ID总是更新失败
+3. **监听器重复触发**:同一个状态变化触发了多次监听器调用
+
+### 解决方案
+1. ✅ **单一全局监听器**:只在 `initializeServices` 时设置一次监听器
+2. ✅ **统一更新机制**:使用 `updateAllWidgetsWithData` 统一处理所有卡片
+3. ✅ **避免重复注册**:移除了每个卡片的单独监听器注册
+4. ✅ **简化初始化**:卡片初始化只负责初始数据加载
+
+## 代码修改
+
+### 1. 添加全局监听器标志
+```typescript
+private globalListenerSetup: boolean = false;
+```
+
+### 2. 修改initializeServices方法
+```typescript
+private initializeServices(): void {
+  if (!this.widgetDataManager) {
+    this.widgetDataManager = new WidgetDataManager();
+    hilog.info(0x0000, TAG, 'WidgetDataManager initialized');
+  }
+  
+  // 设置全局状态监听器(只设置一次)
+  if (!this.globalListenerSetup) {
+    this.setupGlobalStateListener();
+    this.globalListenerSetup = true;
+  }
+}
+```
+
+### 3. 新增全局监听器设置方法
+```typescript
+private setupGlobalStateListener(): void {
+  try {
+    const avSessionListener = AvSessionWidgetListener.getInstance();
+    avSessionListener.addStateListener((data: WidgetData) => {
+      hilog.info(0x0000, TAG, `Heanup widget global listener received state update: isPlaying=${data.playState.isPlaying}, title=${data.currentSong.title}`);
+      
+      // 更新所有活跃的卡片
+      this.updateAllWidgetsWithData(data);
+    });
+    
+    hilog.info(0x0000, TAG, 'Heanup widget global state listener setup successfully');
+  } catch (error) {
+    hilog.error(0x0000, TAG, `Heanup widget failed to setup global listener: ${error}`);
+  }
+}
+```
+
+### 4. 新增统一更新方法
+```typescript
+private updateAllWidgetsWithData(data: WidgetData): void {
+  this.formSizeMap.forEach((size: WidgetSize, formId: string) => {
+    try {
+      // 根据卡片尺寸适配数据
+      const adaptedData = this.layoutManager.adaptDataForSize(data, size);
+      
+      hilog.info(0x0000, TAG, `Heanup widget ${formId} calling formProvider.updateForm with isPlaying=${adaptedData.isPlaying}`);
+      
+      // 直接使用系统API更新卡片
+      const formData = formBindingData.createFormBindingData(adaptedData);
+      formProvider.updateForm(formId, formData).then(() => {
+        hilog.info(0x0000, TAG, `Heanup widget ${formId} updated successfully: isPlaying=${adaptedData.isPlaying}, title=${adaptedData.songTitle}`);
+      }).catch((error) => {
+        hilog.error(0x0000, TAG, `Heanup widget ${formId} update failed: ${error}`);
+      });
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Heanup widget ${formId} process error: ${error}`);
+    }
+  });
+}
+```
+
+### 5. 简化initializeWidget方法
+- 移除了重复的监听器注册
+- 移除了定期更新机制
+- 只保留初始化数据加载
+
+## 预期效果
+
+### 日志输出应该显示:
+1. ✅ 只有一个 "global state listener setup successfully" 消息
+2. ✅ 状态更新时只有一个 "global listener received state update" 消息
+3. ✅ 所有活跃卡片都会被统一更新
+4. ✅ 减少更新失败的情况
+
+### 不应该再看到:
+- ❌ 重复的监听器注册消息
+- ❌ 同一个状态更新被多次处理
+- ❌ 大量的更新失败消息
+
+## 测试步骤
+
+1. 启动应用并添加桌面卡片
+2. 播放音乐,观察日志输出
+3. 暂停/播放切换,检查所有卡片是否同步更新
+4. 添加多个卡片,验证统一更新机制
+5. 检查日志中是否还有重复监听器问题
+
+## 关键改进点
+
+1. **架构优化**:从"每卡片一个监听器"改为"全局单一监听器"
+2. **性能提升**:减少了重复的监听器调用和状态处理
+3. **稳定性增强**:统一的更新机制减少了更新失败的可能性
+4. **代码简化**:移除了复杂的重复逻辑,提高了可维护性

+ 127 - 0
widget_fix_summary.md

@@ -0,0 +1,127 @@
+# 桌面卡片更新机制修复总结
+
+## 修复完成 ✅
+
+### 主要问题
+1. **重复监听器问题**:每个卡片都注册了监听器,导致同一个状态更新被多次处理
+2. **部分卡片更新失败**:`487570509` 和 `219980721` 总是更新失败,只有 `1255272278` 成功
+3. **监听器重复触发**:同一个状态变化触发了多次监听器调用
+4. **批量更新不完整**:批量更新只处理了部分卡片
+
+### 解决方案实施
+
+#### 1. 架构重构 ✅
+- **从**:每个卡片单独注册监听器
+- **到**:单一全局监听器统一管理
+
+#### 2. 代码修改详情
+
+##### A. 添加全局监听器标志
+```typescript
+private globalListenerSetup: boolean = false;
+```
+
+##### B. 修改服务初始化
+```typescript
+private initializeServices(): void {
+  if (!this.widgetDataManager) {
+    this.widgetDataManager = new WidgetDataManager();
+    hilog.info(0x0000, TAG, 'WidgetDataManager initialized');
+  }
+  
+  // 设置全局状态监听器(只设置一次)
+  if (!this.globalListenerSetup) {
+    this.setupGlobalStateListener();
+    this.globalListenerSetup = true;
+  }
+}
+```
+
+##### C. 新增全局监听器方法
+```typescript
+private setupGlobalStateListener(): void {
+  try {
+    const avSessionListener = AvSessionWidgetListener.getInstance();
+    avSessionListener.addStateListener((data: WidgetData) => {
+      hilog.info(0x0000, TAG, `Heanup widget global listener received state update: isPlaying=${data.playState.isPlaying}, title=${data.currentSong.title}`);
+      
+      // 更新所有活跃的卡片
+      this.updateAllWidgetsWithData(data);
+    });
+    
+    hilog.info(0x0000, TAG, 'Heanup widget global state listener setup successfully');
+  } catch (error) {
+    hilog.error(0x0000, TAG, `Heanup widget failed to setup global listener: ${error}`);
+  }
+}
+```
+
+##### D. 统一更新机制
+```typescript
+private updateAllWidgetsWithData(data: WidgetData): void {
+  this.formSizeMap.forEach((size: WidgetSize, formId: string) => {
+    try {
+      // 根据卡片尺寸适配数据
+      const adaptedData = this.layoutManager.adaptDataForSize(data, size);
+      
+      hilog.info(0x0000, TAG, `Heanup widget ${formId} calling formProvider.updateForm with isPlaying=${adaptedData.isPlaying}`);
+      
+      // 直接使用系统API更新卡片
+      const formData = formBindingData.createFormBindingData(adaptedData);
+      formProvider.updateForm(formId, formData).then(() => {
+        hilog.info(0x0000, TAG, `Heanup widget ${formId} updated successfully: isPlaying=${adaptedData.isPlaying}, title=${adaptedData.songTitle}`);
+      }).catch((error) => {
+        hilog.error(0x0000, TAG, `Heanup widget ${formId} update failed: ${error}`);
+      });
+    } catch (error) {
+      hilog.error(0x0000, TAG, `Heanup widget ${formId} process error: ${error}`);
+    }
+  });
+}
+```
+
+##### E. 简化卡片初始化
+- 移除了每个卡片的单独监听器注册
+- 移除了定期更新机制(setInterval)
+- 只保留初始数据加载
+
+##### F. 统一方法调用
+- 将所有 `updateAllWidgetsData()` 调用替换为 `updateAllWidgetsWithData(currentState)`
+- 删除了重复的 `updateAllWidgetsData` 方法
+
+## 预期效果
+
+### 应该看到的改进:
+1. ✅ **单一监听器**:只有一个 "global state listener setup successfully" 消息
+2. ✅ **统一更新**:状态更新时只有一个 "global listener received state update" 消息
+3. ✅ **所有卡片同步**:所有活跃卡片都会被统一更新
+4. ✅ **减少失败**:更新失败的情况应该大幅减少
+
+### 不应该再看到的问题:
+- ❌ 重复的监听器注册消息
+- ❌ 同一个状态更新被多次处理
+- ❌ 大量的更新失败消息
+- ❌ 卡片ID `487570509` 和 `219980721` 持续失败
+
+## 测试建议
+
+1. **启动测试**:启动应用,观察是否只有一个全局监听器设置消息
+2. **卡片添加**:添加多个桌面卡片,验证都能正常初始化
+3. **播放控制**:播放/暂停音乐,检查所有卡片是否同步更新
+4. **日志检查**:确认没有重复的监听器调用和更新失败
+5. **长期稳定性**:运行一段时间,确保没有内存泄漏或性能问题
+
+## 关键改进点
+
+1. **性能优化**:从 O(n) 个监听器减少到 O(1) 个监听器
+2. **稳定性提升**:统一的更新机制减少了竞态条件
+3. **代码简化**:移除了复杂的重复逻辑
+4. **维护性增强**:单一职责原则,更容易调试和维护
+
+## 风险评估
+
+- **低风险**:主要是架构重构,没有改变核心业务逻辑
+- **向后兼容**:保持了所有现有的API接口
+- **测试覆盖**:建议进行充分的回归测试
+
+修复已完成,可以进行测试验证。

+ 133 - 0
widget_fix_v2_summary.md

@@ -0,0 +1,133 @@
+# 桌面卡片更新机制修复 V2
+
+## 问题分析
+
+从日志分析发现的关键问题:
+
+1. **"batch update completed for 0 widgets"** - 批量更新时没有找到任何卡片
+2. **状态反复切换** - isPlaying在true/false之间快速切换
+3. **缺少全局监听器日志** - 没有看到全局监听器的设置消息
+4. **多实例问题** - 可能有多个FormExtensionAbility实例,每个都有自己的formSizeMap
+
+## 根本原因
+
+1. **多实例冲突**:多个FormExtensionAbility实例共享同一个全局监听器,但各自维护独立的formSizeMap
+2. **状态管理混乱**:卡片删除后,某些实例的formSizeMap变空,但全局监听器仍在运行
+3. **重复更新**:onUpdateForm中同时调用单个更新和批量更新
+
+## 解决方案 V2
+
+### 1. 创建全局卡片管理器
+
+创建了 `GlobalWidgetManager` 来统一管理所有活跃卡片:
+
+```typescript
+export class GlobalWidgetManager {
+  private static instance: GlobalWidgetManager | null = null;
+  private static activeWidgets: Map<string, WidgetSize> = new Map();
+
+  // 注册卡片
+  public registerWidget(formId: string, size: WidgetSize): void
+  
+  // 注销卡片
+  public unregisterWidget(formId: string): void
+  
+  // 获取所有活跃卡片
+  public getActiveWidgets(): Map<string, WidgetSize>
+  
+  // 获取活跃卡片数量
+  public getActiveWidgetCount(): number
+}
+```
+
+### 2. 修改EntryFormAbility
+
+- **移除本地formSizeMap**:使用全局管理器替代
+- **统一卡片注册**:在onAddForm中注册到全局管理器
+- **统一卡片注销**:在onRemoveForm中从全局管理器注销
+- **全局状态检查**:更新前检查全局活跃卡片数量
+
+### 3. 关键修改点
+
+#### A. 卡片注册
+```typescript
+// 旧代码
+this.formSizeMap.set(formId, widgetSize);
+
+// 新代码
+this.globalWidgetManager.registerWidget(formId, widgetSize);
+```
+
+#### B. 卡片注销
+```typescript
+// 旧代码
+this.formSizeMap.delete(formId);
+
+// 新代码
+this.globalWidgetManager.unregisterWidget(formId);
+```
+
+#### C. 全局监听器
+```typescript
+private setupGlobalStateListener(): void {
+  const avSessionListener = AvSessionWidgetListener.getInstance();
+  avSessionListener.addStateListener((data: WidgetData) => {
+    const activeWidgetCount = GlobalWidgetManager.getInstance().getActiveWidgetCount();
+    hilog.info(0x0000, TAG, `Global listener received update: widgets count: ${activeWidgetCount}`);
+    
+    // 只有当有活跃卡片时才更新
+    if (activeWidgetCount > 0) {
+      this.updateAllWidgetsWithData(data);
+    } else {
+      hilog.info(0x0000, TAG, 'No active widgets, skipping update');
+    }
+  });
+}
+```
+
+#### D. 批量更新
+```typescript
+private updateAllWidgetsWithData(data: WidgetData): void {
+  const activeWidgets = this.globalWidgetManager.getActiveWidgets();
+  hilog.info(0x0000, TAG, `Batch update called, active widgets: ${activeWidgets.size}`);
+  
+  if (activeWidgets.size === 0) {
+    hilog.warn(0x0000, TAG, 'No widgets to update');
+    return;
+  }
+  
+  // 更新所有活跃卡片...
+}
+```
+
+## 预期效果
+
+### 应该看到的改进:
+
+1. ✅ **正确的卡片计数**:批量更新显示正确的卡片数量,不再是0
+2. ✅ **全局监听器日志**:看到 "global state listener setup successfully" 消息
+3. ✅ **状态同步**:所有卡片状态保持一致,不再反复切换
+4. ✅ **清晰的日志**:每次更新都显示正确的活跃卡片数量
+
+### 不应该再看到的问题:
+
+- ❌ "batch update completed for 0 widgets"
+- ❌ 状态在true/false之间快速无序切换
+- ❌ 卡片删除后仍然尝试更新的错误
+
+## 测试要点
+
+1. **启动测试**:启动应用,确认看到全局监听器设置消息
+2. **卡片管理**:添加/删除卡片,观察全局管理器的注册/注销日志
+3. **状态同步**:播放/暂停音乐,确认所有卡片同步更新
+4. **批量更新**:确认批量更新显示正确的卡片数量
+5. **多卡片测试**:添加多个卡片,确认都能正常工作
+
+## 架构改进
+
+1. **单一数据源**:所有卡片信息统一由GlobalWidgetManager管理
+2. **实例隔离**:多个FormExtensionAbility实例不再相互干扰
+3. **状态一致性**:全局状态确保所有卡片保持同步
+4. **资源管理**:更好的卡片生命周期管理,避免内存泄漏
+
+这个修复应该彻底解决卡片更新的问题,提供更稳定和一致的用户体验。

+ 131 - 0
widget_fix_v3_summary.md

@@ -0,0 +1,131 @@
+# 桌面卡片状态相反问题修复 V3
+
+## 🔍 问题根本原因分析
+
+从详细的日志分析中发现了真正的问题:
+
+### 1. 全局监听器没有被设置
+- 日志中没有看到 "global state listener setup successfully" 消息
+- 说明 `initializeServices` 或 `setupGlobalStateListener` 没有被正确调用
+
+### 2. 卡片没有注册到全局管理器
+- 日志显示 "active widgets: 0",但卡片实际在运行
+- 说明 `GlobalWidgetManager.registerWidget` 没有成功
+
+### 3. 状态完全相反的根本原因
+**关键发现**:卡片点击时使用的是**缓存的旧状态**,而不是实时状态!
+
+从日志序列可以看出:
+```
+1. 用户点击播放按钮
+2. 卡片调用 getCurrentPlayState() → 返回缓存的旧状态 (isPlaying=false)
+3. 卡片立即更新显示 isPlaying=false
+4. 主应用异步处理命令,真实状态变为 isPlaying=true
+5. CommonEvent 广播真实状态 isPlaying=true
+6. 但卡片已经显示了相反的状态
+```
+
+## 🔧 修复方案 V3
+
+### 1. 增强调试信息
+```typescript
+private initializeServices(): void {
+  hilog.info(0x0000, TAG, 'Heanup widget initializeServices called');
+  
+  if (!this.widgetDataManager) {
+    this.widgetDataManager = new WidgetDataManager();
+    hilog.info(0x0000, TAG, 'WidgetDataManager initialized');
+  }
+  
+  hilog.info(0x0000, TAG, `Heanup widget checking global listener setup: ${EntryFormAbility.globalListenerSetup}`);
+  if (!EntryFormAbility.globalListenerSetup) {
+    hilog.info(0x0000, TAG, 'Heanup widget setting up global listener...');
+    this.setupGlobalStateListener();
+    EntryFormAbility.globalListenerSetup = true;
+    hilog.info(0x0000, TAG, 'Heanup widget global listener setup flag set to true');
+  } else {
+    hilog.info(0x0000, TAG, 'Heanup widget global listener already setup, skipping');
+  }
+}
+```
+
+### 2. 增强错误处理
+```typescript
+onAddForm(want: Want): formBindingData.FormBindingData {
+  hilog.info(0x0000, TAG, 'onAddForm called');
+  
+  // 初始化服务
+  try {
+    this.initializeServices();
+    hilog.info(0x0000, TAG, 'Services initialized successfully');
+  } catch (error) {
+    hilog.error(0x0000, TAG, `Failed to initialize services: ${error}`);
+  }
+  
+  // 卡片注册
+  try {
+    this.globalWidgetManager.registerWidget(formId, widgetSize);
+    hilog.info(0x0000, TAG, `Widget registered successfully: ${formId}, size: ${widgetSize}`);
+  } catch (error) {
+    hilog.error(0x0000, TAG, `Failed to register widget: ${error}`);
+  }
+}
+```
+
+### 3. 修复状态更新逻辑
+**关键修复**:移除立即状态更新,等待真实状态通过CommonEvent到达
+
+```typescript
+// 旧逻辑(错误)
+// 处理完命令后立即更新当前卡片状态
+await this.updateWidgetData(formId);  // ❌ 使用缓存的旧状态
+
+// 新逻辑(正确)
+// 不要立即更新卡片,等待真实状态通过CommonEvent到达
+// 真实状态会通过全局监听器自动更新所有卡片
+hilog.info(0x0000, TAG, `Widget event processed, waiting for real state update via CommonEvent`);
+```
+
+## 🎯 预期修复效果
+
+### 应该看到的日志:
+1. ✅ `"Heanup widget initializeServices called"`
+2. ✅ `"WidgetDataManager initialized"`
+3. ✅ `"Heanup widget setting up global listener..."`
+4. ✅ `"Heanup widget global state listener setup successfully"`
+5. ✅ `"Widget registered successfully: [formId], size: [size]"`
+6. ✅ `"Heanup widget global listener received state update: isPlaying=[correct_state]"`
+7. ✅ `"active widgets: [correct_count]"` (不再是0)
+
+### 状态同步应该正确:
+- 点击播放 → 卡片显示播放状态 ✅
+- 点击暂停 → 卡片显示暂停状态 ✅
+- 不再有状态相反的问题 ✅
+
+## 🔄 工作流程
+
+### 正确的状态更新流程:
+```
+1. 用户点击卡片按钮
+2. 发送控制命令到主应用
+3. 主应用处理命令,更新真实播放状态
+4. 主应用通过CommonEvent广播新状态
+5. 全局监听器接收到新状态
+6. 全局监听器更新所有活跃卡片
+7. 卡片显示正确的新状态
+```
+
+### 关键改进:
+- **移除立即更新**:不再使用缓存的旧状态立即更新卡片
+- **依赖事件驱动**:完全依赖CommonEvent的真实状态更新
+- **全局统一管理**:所有卡片通过全局监听器统一更新
+
+## 🧪 测试要点
+
+1. **启动测试**:确认看到所有初始化日志
+2. **注册测试**:确认卡片成功注册到全局管理器
+3. **监听器测试**:确认全局监听器成功设置
+4. **状态测试**:点击播放/暂停,确认状态正确同步
+5. **多卡片测试**:多个卡片都能正确同步状态
+
+这个修复应该彻底解决状态相反的问题,让卡片显示与实际播放状态完全一致。

+ 94 - 0
widget_fix_v4_summary.md

@@ -0,0 +1,94 @@
+# 桌面卡片无更新问题修复 V4
+
+## 🔍 问题根本原因
+
+从最新日志分析发现了真正的问题:
+
+### 多进程问题
+日志显示有多个不同的进程:
+- `9103-9103` - 卡片进程1
+- `8980-8980` - 主应用进程  
+- `9738-9738` - 卡片进程2
+
+**关键发现**:每个进程都有自己的 `GlobalWidgetManager` 实例!
+
+### 问题表现
+```
+08-09 10:20:20.972   I   Heanup widget global listener received state update: isPlaying=true, widgets count: 
+```
+
+注意 `widgets count:` 后面是空的,说明 `GlobalWidgetManager.getInstance().getActiveWidgetCount()` 在某些进程中返回了 `undefined`。
+
+### 状态不同步
+- 进程A:注册了3个卡片,`activeWidgets: 3`
+- 进程B:接收到状态更新,但 `activeWidgets: 0`(因为是不同的GlobalWidgetManager实例)
+- 结果:全局监听器收到更新,但因为认为没有活跃卡片而跳过更新
+
+## 🔧 修复方案 V4
+
+### 1. 移除跨进程依赖
+不再依赖 `GlobalWidgetManager` 的跨进程状态同步,每个进程只管理自己的卡片。
+
+```typescript
+// 旧代码(有问题)
+const activeWidgetCount = GlobalWidgetManager.getInstance().getActiveWidgetCount();
+if (activeWidgetCount > 0) {
+  this.updateAllWidgetsWithData(data);
+}
+
+// 新代码(修复)
+hilog.info(0x0000, TAG, `Heanup widget global listener received state update: isPlaying=${data.playState.isPlaying}, process: ${process.pid}`);
+// 总是尝试更新,让updateAllWidgetsWithData自己检查
+this.updateAllWidgetsWithData(data);
+```
+
+### 2. 增强进程识别
+添加进程ID到日志中,便于调试多进程问题:
+
+```typescript
+hilog.info(0x0000, TAG, `Heanup widget updateAllWidgetsWithData called, active widgets: ${activeWidgets.size}, process: ${process.pid}`);
+```
+
+### 3. 简化逻辑
+- 移除全局状态检查的前置条件
+- 让每个进程的 `updateAllWidgetsWithData` 自己决定是否有卡片需要更新
+- 如果没有卡片,方法内部会直接返回,不会造成问题
+
+## 🎯 预期效果
+
+### 应该看到的改进:
+1. ✅ **每个进程都会尝试更新**:不再因为跨进程状态不同步而跳过
+2. ✅ **正确的进程日志**:可以看到哪个进程在处理更新
+3. ✅ **卡片正常更新**:每个进程管理自己的卡片,不依赖其他进程
+
+### 日志应该显示:
+```
+I   Heanup widget global listener received state update: isPlaying=true, process: 9738
+I   Heanup widget updateAllWidgetsWithData called, active widgets: 1, process: 9738
+I   Heanup widget 1952361985 calling formProvider.updateForm with isPlaying=true
+I   Heanup widget 1952361985 updated successfully: isPlaying=true
+```
+
+## 🔄 架构改进
+
+### 从全局单例到进程本地管理
+- **旧架构**:试图在多进程间共享GlobalWidgetManager状态(不可行)
+- **新架构**:每个进程管理自己的卡片,通过CommonEvent同步状态
+
+### 工作流程
+```
+1. 主应用状态变化
+2. 通过CommonEvent广播到所有进程
+3. 每个卡片进程的全局监听器接收更新
+4. 每个进程更新自己管理的卡片
+5. 所有卡片都得到正确更新
+```
+
+## 🧪 测试要点
+
+1. **多进程测试**:确认每个进程都能正确处理状态更新
+2. **卡片更新**:确认所有卡片都能正确显示状态变化
+3. **日志完整性**:确认看到完整的更新流程日志
+4. **性能测试**:确认没有重复更新或性能问题
+
+这个修复解决了多进程环境下的状态同步问题,让每个进程独立管理自己的卡片,避免了跨进程状态依赖的复杂性。