HarmonyNote.TOP鸿蒙开发笔记

ArkUI中回调函数this指向导致应用闪崩解决

起因是我正在开发的应用有一个页面需要检测手机屏幕是横屏还是竖屏,当横竖屏切换时还需要作监听并在回调函数里做一些逻辑处理。页面检测横屏竖屏的代码示例如下:

import { mediaquery } from '@kit.ArkUI';
import { SinglePageTemplate } from '../page_templates/SinglePageTemplate';

@Entry
@Component
struct ScreenDirectionPage3 {
  @State message: string = '';
  listener: mediaquery.MediaQueryListener | null = null;

  aboutToAppear(): void {
    // 监听横屏事件
    this.listener = mediaquery.matchMediaSync('(orientation: landscape)');
    this.directionListener()
  }

  /*
   * module.json5->abilities->orientation: $string:ScreenDirection
   * orientation可以设置的值包括:
        auto_rotation:让屏幕自动旋转。
        unspecified:由系统决定,默认值。
        landscape:固定为横屏。
        portrait:固定为竖屏。
        landscape_inverted:固定为反向横屏(旋转180度)。
        portrait_inverted:固定为反向竖屏(旋转180度)。
        sensor_landscape:根据设备传感器确定方向,可以是横屏或反向横屏。
        sensor_portrait:根据设备传感器确定方向,可以是竖屏或反向竖屏。
        sensor:根据设备传感器确定方向,可以是任意四种方向。
        full_sensor:与sensor类似,但允许在任何四个方向旋转(包括竖屏、反向竖屏、横屏、反向横屏)。
        nosensor:忽略物理传感器,完全由用户控制。
   * */
  directionListener(){
    this.listener?.on('change', this.onPortrait)
  }
  onPortrait(mediaQueryResult: mediaquery.MediaQueryResult) {
    const msg = `_${mediaQueryResult?.matches}_${mediaQueryResult?.media}`
    if (mediaQueryResult.matches) {
      this.message = '横屏' + msg
    } else {
      this.message = '竖屏' + msg
    }
  }

  aboutToDisappear(): void {
    this.listener?.off('change', this.onPortrait)
  }
  build() {
    Column(){
      SinglePageTemplate({
        title: 'MediaQuery横竖屏监听'
      }) {
        Text(this.message)
      }
    }.width('100%').height('100%')
  }
}

部署应用到NOVA 14真机上运行,然而每当跳转到上述页面时,应用立马闪崩重启,同时DevEco Studio提示“jscrash happened in nova 14[UYG67TYFYDG7V]”,进入Log面板,查看FaultLog,得到如下错误信息:

Device info:nova 14
Build info:TLR-AL00 5.0.1.125(SP5C00E125R3P5)
Fingerprint:7ade702d383a5516289583ba20ac0fe5fa76c06fd1143637363b45c9f0bb9218
Module name:com.xxx.XXOOProject
Version:1.0.0
VersionCode:1000000
PreInstalled:No
Foreground:Yes
Pid:55216
Uid:20020195
Reason:TypeError
Error name:TypeError
Error message:Obj is not a Valid object
Stacktrace:
    at onPortrait (entry/src/main/ets/pages/screen_direction/ScreenDirectionPage3.ets:39:7)

看最后一行错误日志,错误代码是在第39行,所在函数名为onPortrait,第39行代码如下:

this.message = '竖屏' + msg

也就是逻辑判断当前为竖屏,再看最后一行错误日志的具体定位,第39行第7个字符也就是this,错误消息是Error message:Obj is not a Valid object,也就是说this不是一个有效的object,通过打断点调试得知this=undefined。于是想到使用bind方法来绑定当前页面的this,改造代码如下:

directionListener(){
  // 使用bind绑定当前应用实例
  this.listener?.on('change', this.onPortrait.bind(this))
}

也可以改成如下形式:

directionListener(){
  const thisWrapper: Callback<mediaquery.MediaQueryResult> = this.onPortrait.bind(this)
  this.listener?.on('change', thisWrapper)
}

还可以改成如下形式:

directionListener(){
  const thisWrapper: (result: mediaquery.MediaQueryResult)=>void = this.onPortrait.bind(this)
  this.listener?.on('change', thisWrapper)
}

再运行就没问题了,但是编辑器提示不支持Function.bind这种用法,具体提示信息如下:

"Function.bind" is not supported (arkts-no-func-bind) <ArkTSCheck>
Function.bind(this: Function, thisArg: any, ...argArray: any[]): any
For a given function, creates a bound function that has the same body as the original function. 
The this object of the bound function is associated with the specified object, and has the specified initial parameters.
params:
thisArg – An object to which the this keyword can refer inside the new function.

argArray – A list of arguments to be passed to the new function.

虽然加了bind绑定页面可以正常运行了,但是编辑器老是提示不支持Function.bind用法也挺烦的,于是想到可以用箭头函数来代替bind解决this指向问题,继续改造代码如下:

directionListener(){
  // 使用箭头函数使this始终指向当前应用实例
  this.listener?.on('change', (mediaQueryResult: mediaquery.MediaQueryResult)=>{
    this.onPortrait(mediaQueryResult)
  })
}

这样就完美解决了this指向问题了,虽然代码变多了点。