HarmonyOS如何监听窗口生命周期变化
=====================

#### 问题现象

HarmonyOS如何监听不同情况窗口生命周期变化，比如窗口失焦/获焦、窗口前后台切换、窗口进入多任务等。

#### 背景知识

*   开启WindowStage生命周期变化的监听：[windowStage.on('windowStageEvent')](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-window-windowstage#onwindowstageevent9)。
*   [WindowStageEventType](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-window-e#windowstageeventtype9)：WindowStage生命周期状态枚举。
*   [on('windowEvent')](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-window-window#onwindowevent10)：可通过on('windowEvent')监听窗口的生命周期变化。
*   [WindowEventType](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-window-e#windoweventtype10)：窗口生命周期。
*   UIAbility里提供UI界面的应用组件生命周期回调：[UIAbility生命周期](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-app-ability-uiability)。

#### 解决方案

*   监听窗口失焦与获焦的变化：
    *   可通过on('windowEvent')监听窗口的生命周期变化，其返回值WindowEventType.WINDOW\_ACTIVE为获焦状态，值为WindowEventType.WINDOW\_INACTIVE时为失焦状态。详情参考[PC或平板自由多窗模式下，如何做到主窗口获焦，子窗口隐藏](https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-arkui-980)。
    *   可通过on('windowStageEvent')开启windowStage生命周期变化，其返回值WindowStageEventType.ACTIVE为获焦状态，WindowStageEventType.INACTIVE为失焦状态。
*   监听窗口前后台切换的变化：
    *   可通过on('windowEvent')监听窗口的生命周期变化，其返回值WindowEventType.WINDOW\_SHOWN为前台状态，值为WindowEventType.WINDOW\_HIDDEN时为后台状态。
    *   可通过on('windowStageEvent')开启windowStage生命周期变化，其返回值WindowStageEventType.SHOWN为前台状态，WindowStageEventType.HIDDEN为后台状态。
*   监听窗口是否进入多任务：
    *   可通过on('windowStageEvent')开启windowStage生命周期变化，当上划应用时，会触发WindowStageEventType.PAUSED，松开时会触发WindowStageEventType.RESUMED，详情参考[如何监听应用进入多任务](https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-arkui-969)。

监听不同情况窗口生命周期变化示例代码参考如下：

onWindowStageCreate(windowStage: window.WindowStage): void {
 _ // Main window is created, set main page for this ability_
  hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate');


  windowStage.loadContent('pages/Index', (err) => {
    if (err.code) {
      hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
      return;
    }
    hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
  });


 _ // 监听windowStage生命周期变化_
  try {
    windowStage.on('windowStageEvent', (data) => {
      let stageEventType: window.WindowStageEventType = data;


      switch (stageEventType) {
        case window.WindowStageEventType.ACTIVE: _// 获焦状态_
          console.info(\`windowStage active.\`);
          break;
        case window.WindowStageEventType.INACTIVE: _// 失焦状态_
          console.info(\`windowStage inactive.\`);
          break;
        case window.WindowStageEventType.SHOWN: _// 切到前台_
          console.info(\`windowStage foreground.\`);
          break;
        case window.WindowStageEventType.HIDDEN: _// 切到后台_
          console.info(\`windowStage background.\`);
          break;
        default:
          break;
      }
    });
  } catch (exception) {
    console.info(\`Failed to enable the listener for window stage event changes. Cause: ${JSON.stringify(exception)}\`);
  }


  _// 监听窗口生命周期变化_
  window.getLastWindow(this.context, (\_, data) => {
    let windowClass = data;
    windowClass.on('windowEvent', (data) => {
      let winEventType: window.WindowEventType = data;


      switch (winEventType){
        case window.WindowEventType.WINDOW\_ACTIVE: _// 获焦状态_
          console.info(\`window active.\`);
          break;
        case window.WindowEventType.WINDOW\_INACTIVE: _// 失焦状态_
          console.info(\`window inactive.\`);
          break;
        case window.WindowEventType.WINDOW\_SHOWN: _// 切到前台_
          console.info(\`window foreground.\`);
          break;
        case window.WindowEventType.WINDOW\_HIDDEN: _// 切到后台_
          console.info(\`window background.\`);
          break;
        default:
          break;
      }
    });
  });
}

#### 常见FAQ

Q：使用[getLastWindow](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-window-f#windowgetlastwindow9)获取的window，当新建的窗口还未销毁此时获取window，就会报Window is nullptr和This window stage is abnormal，请问有什么好的解决办法？

A：建议应用在窗口销毁[onWindowStageDestroy](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-app-ability-abilitylifecyclecallback#onwindowstagedestroy)回调中处理业务。如果只是想获取业务的Component（Entry所属的那个window），可以通过getUIContext().[getWindowName](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-uicontext-uicontext#getwindowname12)获取。

Q：在[onBackground](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/uiability-lifecycle#onbackground)中使用[windowStage.getMainWindowSync](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-window-windowstage#getmainwindowsync9)获取主窗口偶现报错Cannot read property off of undefined如何解决？

A：在onBackground事件中，可能正在释放一部分资源，包括窗口资源，因此可能出现获取不到主窗。可以使用[ApplicationContext.on('abilityLifecycle')](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-inner-application-applicationcontext#applicationcontextonabilitylifecycle)监听[AbilityLifecycleCallback](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-app-ability-abilitylifecyclecallback#abilitylifecyclecallback)的[onAbilityWillBackground](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-app-ability-abilitylifecyclecallback#onabilitywillbackground12)事件或者[onWindowStageInactive](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-app-ability-abilitylifecyclecallback#onwindowstageinactive)事件，再操作主窗。

Q：折叠屏手机锁屏时会将[display.on('foldStatusChange')](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-display#displayonfoldstatuschange10)监听注销，如何在设备解锁时恢复监听？

A：当折叠屏设备锁屏时，系统会暂停后台应用的非必要监听以节省资源。display.on('foldStatusChange')作为屏幕状态监听器，属于系统级资源敏感操作，锁屏时会被自动注销。建议在[onForeground](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/uiability-lifecycle#onforeground)生命周期重新注册监听器，在onBackground主动注销避免资源泄漏。