HarmonyOS鸿蒙Next中引导页使用this.pathStack.replacePathByName()跳转出现闪退问题

HarmonyOS鸿蒙Next中引导页使用this.pathStack.replacePathByName()跳转出现闪退问题

@Entry
@Component
struct Index {
  // 控制跳转的对象
  pathStack: NavPathStack = new NavPathStack()
  // 倒计时变量 - 使用@State装饰器
  @State countdown: number = 10
  // 计时器ID
  timer: number | null = null

  // 生命周期
  aboutToAppear(): void {
    // 启动倒计时
    this.startCountdown()
  }

  // 启动倒计时方法
  startCountdown(): void {
    this.timer = setInterval(() => {
      if (this.countdown > 0) {
        // 更新倒计时值,触发UI更新
        this.countdown--
      } else {
        // 倒计时结束,执行跳转
        this.pathStack.replacePathByName('PageTabs',null,false)
        // 清除计时器
        if (this.timer !== null) {
          clearInterval(this.timer)
          this.timer = null
        }
      }
    }, 1000)
  }

  // 组件销毁时清理计时器
  aboutToDisappear(): void {
    if (this.timer !== null) {
      clearInterval(this.timer)
      this.timer = null
    }
  }

  build() {
    // 子页内容存放区域
    NavDestination(){
      Stack({alignContent:Alignment.TopEnd}){
        Image($r('app.media.pic_nonevip_preview'))
          .width('100%')
          .height('100%')
          .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
        Button(`跳过 | 倒计时${this.countdown}`)
          .backgroundColor(Color.Grey)
          .margin(20)
          .onClick(()=>{
            // 停止倒计时
            if (this.timer !== null) {
              clearInterval(this.timer)
              this.timer = null
            }
            // 执行跳转
            this.pathStack.replacePathByName('PageTabs',null,false)
          })
      }
    }
    .onReady((context: NavDestinationContext) => {
      this.pathStack = context.pathStack;
    });
  }
}

更多关于HarmonyOS鸿蒙Next中引导页使用this.pathStack.replacePathByName()跳转出现闪退问题的实战教程也可以访问 https://www.itying.com/category-93-b0.html

8 回复

【背景知识】 Navigation组件导航:用于实现Navigation页面(NavDestination)间的跳转,支持在不同Navigation页面间传递参数,提供灵活的跳转栈操作,从而更便捷地实现对不同页面的访问和复用。 Navigation:Navigation组件是路由导航的根视图容器,一般作为Page页面的根容器使用,其内部默认包含了标题栏、内容区和工具栏,其中内容区默认首页显示导航内容(Navigation的子组件)或非首页显示(NavDestination的子组件),首页和非首页通过路由进行切换。

【解决方案】 Navigation页面为根页面,根页面无法被this.pageInfos.replacePathByName替换,调用该方法之后,页面依旧会保留在栈顶。

通过replacePathByName实现替换NavDestination可以参考下以下示例:

Index页面:

@Entry
@Component
struct Index {
  @Provide('pageInfos') pageInfos: NavPathStack = new NavPathStack();

  build() {
    Stack() {
      Navigation(this.pageInfos) {
        Column() {
          Button('To PageA', { stateEffect: true, type: ButtonType.Capsule })
            .width('80%')
            .height(40)
            .margin(20)
            .onClick(() => {
              this.pageInfos.pushPath({ name: 'pageA' });
            });
        };
      }.title('NavIndex');
    };
  }
}

PageA页面:

@Builder
export function PageABuilder() {
  PageA();
}

@Component
export struct PageA {
  @Consume('pageInfos') pageInfo: NavPathStack;
  // 倒计时变量 - 使用@State装饰器
  @State countdown: number = 10
  // 计时器ID
  timer: number | null = null

  // 生命周期
  aboutToAppear(): void {
    // 启动倒计时
    this.startCountdown()
  }

  // 组件销毁时清理计时器
  aboutToDisappear(): void {
    if (this.timer !== null) {
      clearInterval(this.timer)
      this.timer = null
    }
  }

  // 启动倒计时方法
  startCountdown(): void {
    this.timer = setInterval(() => {
      if (this.countdown > 0) {
        // 更新倒计时值,触发UI更新
        this.countdown--
      } else {
        // 倒计时结束,执行跳转
        this.pageInfo.replacePathByName('PageB', null, false)
        // 清除计时器
        if (this.timer !== null) {
          clearInterval(this.timer)
          this.timer = null
        }
      }
    }, 1000)
  }

  build() {
    NavDestination() {
      Stack({ alignContent: Alignment.TopEnd }) {
        Image($r('app.media.pic_nonevip_preview'))
          .width('100%')
          .height('100%')
          .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
        Button(`跳过 | 倒计时${this.countdown}`)
          .backgroundColor(Color.Grey)
          .margin(20)
          .onClick(() => {
            // 停止倒计时
            if (this.timer !== null) {
              clearInterval(this.timer)
              this.timer = null
            }
            // 执行跳转
            this.pageInfo.replacePathByName('PageB', null, false)
          })
      }
    }
    .width('100%')
    .title('pageA')
    .onBackPressed(() => {
      this.pageInfo.pop();
      return true;
    })
    .onReady((context: NavDestinationContext) => {
      this.pageInfo = context.pathStack;
    });
  }
}

PageB页面:

@Builder
export function PageBBuilder() {
  PageB();
}

@Component
export struct PageB {
  @Consume('pageInfos') pageInfo: NavPathStack;

  build() {
    NavDestination() {
      Column() {
        Text('PageB')
          .width('80%')
          .height(40)
          .margin(20)
      }.width('100%').height(300);
    }
    .width('100%')
    .title('pageB')
    .onBackPressed(() => {
      this.pageInfo.pop();
      return true;
    }).onReady((context: NavDestinationContext) => {
      this.pageInfo = context.pathStack;
    });
  }
}

router_map.json配置:

{
  "routerMap": [
    {
      "name": "pageA",
      "pageSourceFile": "src/main/ets/view/PageA.ets",
      "buildFunction": "PageABuilder",
      "data": {
        "description": "this is pageA"
      }
    },
    {
      "name": "PageB",
      "pageSourceFile": "src/main/ets/view/PageB.ets",
      "buildFunction": "PageBBuilder",
      "data": {
        "description": "this is pageB"
      }
    }
  ]
}

需要实现页面替换根页面的效果可以在根页面当中定义一个状态变量,通过该状态变量控制Navigation根页面显示内容,示例如下:

@Entry
@Component
struct Index {
  pathStack: NavPathStack = new NavPathStack();
  @State isShowIndex: boolean = false;

  build() {
    Navigation(this.pathStack) {
      if (this.isShowIndex) {
        Column() {
          Text('首页面')
            .fontSize(30)
            .fontWeight(FontWeight.Medium)
        }
        .width('100%')
        .height('100%')
      } else {
        SplashPage({
          onChangePage: () => {
            this.isShowIndex = true;
          }
        })
      }
    }
  }
}

@Component
export struct SplashPage {
  onChangePage: VoidCallback = () => undefined;
  // 倒计时变量 - 使用@State装饰器
  @State countdown: number = 10;
  // 计时器ID
  timer: number | null = null;

  aboutToAppear(): void {
    this.startCountdown();
  }

  // 组件销毁时清理计时器
  aboutToDisappear(): void {
    if (this.timer !== null) {
      clearInterval(this.timer);
      this.timer = null;
    }
  }

  // 启动倒计时方法
  startCountdown(): void {
    this.timer = setInterval(() => {
      if (this.countdown > 0) {
        // 更新倒计时值,触发UI更新
        this.countdown--;
      } else {
        // 倒计时结束,执行跳转
        this.onChangePage();
        // 清除计时器
        if (this.timer !== null) {
          clearInterval(this.timer);
          this.timer = null;
        }
      }
    }, 1000)
  }

  build() {
    Column({ space: 15 }) {
      Stack({ alignContent: Alignment.TopEnd }) {
        Image($r('app.media.pic_nonevip_preview'))
          .width('100%')
          .height('100%')
          .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
        Button(`跳过 | 倒计时${this.countdown}`)
          .backgroundColor(Color.Grey)
          .margin(20)
          .onClick(() => {
            // 停止倒计时
            if (this.timer !== null) {
              clearInterval(this.timer);
              this.timer = null;
            }
            // 执行跳转
            this.onChangePage();
          })
      }
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}

更多关于HarmonyOS鸿蒙Next中引导页使用this.pathStack.replacePathByName()跳转出现闪退问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


根本原因是主页需要使用 Navigation 用作根容器,由它承载 NavPathStack 页面栈对象,然后你写一个子页面,子页面使用 NavDestination 作为根容器。这样才能调用路由栈 NavPathStack  对象的方法进行页面路由。

**官网api原文:**NavDestination组件必须配合Navigation使用,作为Navigation目的页面的根节点,单独使用只能作为普通容器组件,不具备路由相关属性能力。

链接:Navigation-导航与切换

真机跑了一下,问题找到了:

问题根本原因: EntryAbility使用传统的 windowStage.loadContent() 方式加载页面,但Index.ets中使用了 NavDestination 组件和 NavPathStack ,这两种方式不能混用。 NavDestination 需要配合 Navigation 组件使用,而EntryAbility使用的是传统的路由方式。

修复方案: 将Index.ets改为使用传统的 router API进行页面跳转,移除了 NavDestination 组件和 NavPathStack 相关代码,改用 router.pushUrl() 方法。

修改内容:

  1. 添加router导入 : Index.ets:1 - 添加了 import { router } from ‘@kit.ArkUI’
  2. 移除NavPathStack :删除了 pathStack: NavPathStack = new NavPathStack() 变量
  3. 修改跳转方法 :将 this.pathStack.pushPathByName(‘PageTabs’,null,false) 改为 router.pushUrl({ url: ‘pages/PageTabs’ })
  4. 移除NavDestination组件 :将 NavDestination() 包装改为直接使用 Stack() 组件
  5. 恢复倒计时启动 :将倒计时启动恢复到 aboutToAppear() 生命周期方法中

修改的文件:

  • Index.ets

现在应用可以正常运行,倒计时结束或点击"跳过"按钮时会正确跳转到PageTabs页面,不会再出现闪退问题。

import { router } from '[@kit](/user/kit).ArkUI';

@Entry
@Component
struct Index {
  // 倒计时变量 - 使用@State装饰器
  @State countdown: number = 10
  // 计时器ID
  timer: number | null = null

  // 生命周期
  aboutToAppear(): void {
    // 启动倒计时
    this.startCountdown()
  }

  // 启动倒计时方法
  startCountdown(): void {
    this.timer = setInterval(() => {
      if (this.countdown > 0) {
        // 更新倒计时值,触发UI更新
        this.countdown--
      } else {
        // 倒计时结束,执行跳转
        router.pushUrl({
          url: 'pages/PageTabs'
        })
        // 清除计时器
        if (this.timer !== null) {
          clearInterval(this.timer)
          this.timer = null
        }
      }
    }, 1000)
  }

  // 组件销毁时清理计时器
  aboutToDisappear(): void {
    if (this.timer !== null) {
      clearInterval(this.timer)
      this.timer = null
    }
  }

  build() {
    Stack({alignContent:Alignment.TopEnd}){
      Image($r('app.media.pic_nonevip_preview'))
        .width('100%')
        .height('100%')
        .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
      Button(`跳过 | 倒计时${this.countdown}`)
        .backgroundColor(Color.Grey)
        .margin(20)
        .onClick(()=>{
          // 停止倒计时
          if (this.timer !== null) {
            clearInterval(this.timer)
            this.timer = null
          }
          // 执行跳转
          router.pushUrl({
            url: 'pages/PageTabs'
          })
        })
    }
  }
}

cke_125.png

你的报错信息是不是某某对象为空,比如:this.pathStack是空的,或者this.pathStack没有replacePathByName的方法。

正常来说,在”子页内容存放区域“跳转页面,是要用父的”Navigation(this.pathStack)“的”this.pathStack“才行。

可以尝试用以下代码段的方法试试

首页

@Entry
@Component
struct Index {
  // 从主导航栈中传递给子页面使用
  @Provide("pathStack") pathStack: NavPathStack = new NavPathStack()

  build() {
    Navigation(this.pathStack) {
      // 主导航栈内容
    }
  }
}

子页面

@Component
struct Child {
  // 控制跳转的对象
  @Consume("pathStack") pathStack: NavPathStack = new NavPathStack()
  // 倒计时变量 - 使用@State装饰器
  @State countdown: number = 10
  // 计时器ID
  timer: number | null = null

  // 生命周期
  aboutToAppear(): void {
    // 启动倒计时
    this.startCountdown()
  }

  // 启动倒计时方法
  startCountdown(): void {
    this.timer = setInterval(() => {
      if (this.countdown > 0) {
        // 更新倒计时值,触发UI更新
        this.countdown--
      } else {
        // 倒计时结束,执行跳转
        this.pathStack.replacePathByName('PageTabs', null, false)
        // 清除计时器
        if (this.timer !== null) {
          clearInterval(this.timer)
          this.timer = null
        }
      }
    }, 1000)
  }

  // 组件销毁时清理计时器
  aboutToDisappear(): void {
    if (this.timer !== null) {
      clearInterval(this.timer)
      this.timer = null
    }
  }

  build() {
    // 子页内容存放区域
    NavDestination() {
      Stack({ alignContent: Alignment.TopEnd }) {
        Image($r('app.media.pic_nonevip_preview'))
          .width('100%')
          .height('100%')
          .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM])
        Button(`跳过 | 倒计时${this.countdown}`)
          .backgroundColor(Color.Grey)
          .margin(20)
          .onClick(() => {
            // 停止倒计时
            if (this.timer !== null) {
              clearInterval(this.timer)
              this.timer = null
            }
            // 执行跳转
            this.pathStack.replacePathByName('PageTabs', null, false)
          })
      }
    }

    // .onReady((context: NavDestinationContext) => {
    //   this.pathStack = context.pathStack;
    // });
  }
}

这样写直接进入时,页面确实白屏,

在HarmonyOS Next中,使用this.pathStack.replacePathByName()导致闪退,通常是由于目标页面路径未在router中正确注册或路径名拼写错误。请检查router模块中是否已通过router.addRoute()注册该页面,并确保replacePathByName()传入的路径名与注册时完全一致。此外,确保页面组件已正确定义且在跳转前已加载完成。

在HarmonyOS Next中,this.pathStack.replacePathByName() 在引导页跳转时出现闪退,通常是由于导航栈未正确初始化生命周期时序问题导致的。从你的代码看,问题可能出现在以下两点:

  1. pathStack 初始化时机不当
    你在 build()NavDestination.onReady() 回调中才将 context.pathStack 赋值给 this.pathStack,但 aboutToAppear() 中启动的倒计时可能先于 onReady() 执行。当倒计时结束时,this.pathStack 可能还是初始化的空对象,调用 replacePathByName() 会导致异常。

  2. replacePathByName 参数问题
    replacePathByName(name: string, param?: Object, isStackClear?: boolean) 的第三个参数 isStackClear 设为 false 时,若当前导航栈为空(未初始化),可能引发栈操作错误。

建议修改如下:

// 1. 移除 pathStack 的初始赋值,改为在 onReady 后使用
private pathStack?: NavPathStack;

// 2. 在 onReady 回调中赋值后执行跳转逻辑
.onReady((context: NavDestinationContext) => {
  this.pathStack = context.pathStack;
  this.startCountdown(); // 将倒计时启动移到 onReady 之后
})

// 3. 跳转前检查 pathStack 是否已初始化
if (this.pathStack) {
  this.pathStack.replacePathByName('PageTabs', null, false);
} else {
  // 可添加日志或延迟重试
  console.error('NavPathStack not initialized');
}

其他注意事项:

  • 确保 PageTabs 页面已在 router 中正确配置。
  • 若仍闪退,检查 DevEco Studio 的日志输出,查看具体异常堆栈。
  • 考虑使用 router.replaceUrl() 进行页面跳转,有时比直接操作 NavPathStack 更稳定。

修改后应能解决因导航栈未就绪导致的闪退问题。

回到顶部