本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

一. 概述

UIAbility 是 HarmonyOS 系统调度的最小单元,负责应用的功能模块。在应用内部,经常需要从一个 UIAbility 跳转到另一个 UIAbility。

二. 基本启动方式

2.1 简单启动 UIAbility

场景:从当前 UIAbility 启动另一个 UIAbility,不需要返回结果。

核心方法startAbility()

示例

import { common, Want } from '@kit.AbilityKit';
import { logger } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';

const LOG_TAG: string = '[MainPage]';
const DOMAIN_ID: number = 0xFF00;

@Entry
@Component
struct MainApplicationPage {
  // 获取 UIAbility 上下文
  private abilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;

  build() {
    Column() {
      List({ initialIndex: 0 }) {
        ListItem() {
          Row() {
            Text('启动功能页面')
              .fontSize(18)
          }
          .padding(12)
        }
        .onClick(() => {
          // 构造启动参数
          let targetWant: Want = {
            deviceId: '', // 空字符串表示本设备
            bundleName: 'com.example.myapplication',
            moduleName: 'feature', // 可选参数,不同模块时需要
            abilityName: 'FeatureAbility',
            parameters: {
              // 自定义参数
              source: '来自主页面',
              timestamp: new Date().getTime()
            }
          };

          // 启动目标 UIAbility
          this.abilityContext.startAbility(targetWant).then(() => {
            logger.info(DOMAIN_ID, LOG_TAG, '功能页面启动成功');
          }).catch((error: BusinessError) => {
            logger.error(DOMAIN_ID, LOG_TAG, 
              `启动失败: 错误码 ${error.code}, 错误信息 ${error.message}`);
          });
        })
      }
    }
  }
}

2.2 目标 UIAbility 接收参数

在被启动的 UIAbility 中接收传递过来的参数:

import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';

export default class FeatureAbility extends UIAbility {
  private receivedSource: string = '';
  private receivedTimestamp: number = 0;

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // 接收调用方传递的参数
    this.receivedSource = want?.parameters?.source as string || '未知来源';
    this.receivedTimestamp = want?.parameters?.timestamp as number || 0;
    
    console.info(`来自: ${this.receivedSource}, 时间: ${this.receivedTimestamp}`);
  }
}

三. 启动并获取返回结果

3.1 带结果启动

场景:启动另一个 UIAbility 并期望在目标 UIAbility 完成操作后返回结果。

核心方法startAbilityForResult()

示例

import { common, Want } from '@kit.AbilityKit';
import { logger } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';

const LOG_TAG: string = '[MainPage]';
const DOMAIN_ID: number = 0xFF00;
const LOGIN_RESULT_CODE: number = 1001;

@Entry
@Component
struct LoginEntryPage {
  private abilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;

  build() {
    Column() {
      Button('用户登录')
        .onClick(() => {
          let loginWant: Want = {
            deviceId: '',
            bundleName: 'com.example.myapplication',
            abilityName: 'UserLoginAbility',
            parameters: {
              loginType: 'password',
              rememberMe: true
            }
          };

          // 启动登录页面并等待结果
          this.abilityContext.startAbilityForResult(loginWant).then((resultData) => {
            if (resultData?.resultCode === LOGIN_RESULT_CODE) {
              // 解析返回的登录结果
              const userInfo = resultData.want?.parameters?.userData;
              const loginSuccess = resultData.want?.parameters?.success;
              
              if (loginSuccess) {
                logger.info(DOMAIN_ID, LOG_TAG, `用户登录成功: ${JSON.stringify(userInfo)}`);
                this.getUIContext().getPromptAction().showToast({
                  message: '登录成功'
                });
              }
            }
          }).catch((error: BusinessError) => {
            logger.error(DOMAIN_ID, LOG_TAG, 
              `登录流程失败: 错误码 ${error.code}, 错误信息 ${error.message}`);
          });
        })
    }
  }
}

3.2 返回结果给调用方

在被启动的 UIAbility 中返回结果:

import { common } from '@kit.AbilityKit';
import { logger } from '@kit.PerformanceAnalysisKit';

const LOG_TAG: string = '[LoginPage]';
const DOMAIN_ID: number = 0xFF00;
const LOGIN_RESULT_CODE: number = 1001;

@Entry
@Component
struct UserLoginPage {
  private abilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;

  build() {
    Column() {
      Button('确认登录')
        .onClick(() => {
          // 构造返回结果
          let loginResult: common.AbilityResult = {
            resultCode: LOGIN_RESULT_CODE,
            want: {
              bundleName: 'com.example.myapplication',
              abilityName: 'MainAbility',
              parameters: {
                success: true,
                userData: {
                  userId: '12345',
                  userName: '张三',
                  token: 'abc123xyz'
                }
              }
            }
          };

          // 返回结果并结束当前 UIAbility
          this.abilityContext.terminateSelfWithResult(loginResult, (error) => {
            if (error?.code) {
              logger.error(DOMAIN_ID, LOG_TAG, 
                `返回结果失败: 错误码 ${error.code}, 错误信息 ${error.message}`);
              return;
            }
            logger.info(DOMAIN_ID, LOG_TAG, '登录结果返回成功');
          });
        })
    }
  }
}

4. 启动指定页面

4.1 调用方指定目标页面

场景:启动 UIAbility 时直接跳转到指定的页面。

import { common, Want } from '@kit.AbilityKit';
import { logger } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';

const LOG_TAG: string = '[MainPage]';
const DOMAIN_ID: number = 0xFF00;

@Entry
@Component
struct ApplicationMainPage {
  private abilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;

  build() {
    Column() {
      // 跳转到设置页面
      Button('系统设置')
        .onClick(() => {
          this.navigateToSpecificPage('settings');
        })
        
      // 跳转到个人资料页面  
      Button('个人资料')
        .onClick(() => {
          this.navigateToSpecificPage('profile');
        })
    }
  }

  private navigateToSpecificPage(pageRoute: string): void {
    let navigationWant: Want = {
      deviceId: '',
      bundleName: 'com.example.myapplication',
      abilityName: 'FeatureAbility',
      parameters: {
        targetPage: pageRoute,  // 指定目标页面
        navigationSource: '主页面'
      }
    };

    this.abilityContext.startAbility(navigationWant).then(() => {
      logger.info(DOMAIN_ID, LOG_TAG, `跳转到 ${pageRoute} 页面成功`);
    }).catch((error: BusinessError) => {
      logger.error(DOMAIN_ID, LOG_TAG, 
        `页面跳转失败: 错误码 ${error.code}, 错误信息 ${error.message}`);
    });
  }
}

4.2 目标 UIAbility 冷启动处理

冷启动:UIAbility 实例完全关闭状态下被启动。

import { AbilityConstant, Want, UIAbility } from '@kit.AbilityKit';
import { logger } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';

const DOMAIN_ID: number = 0xFF00;
const LOG_TAG: string = '[FeatureAbility]';

export default class FeatureAbility extends UIAbility {
  private incomingWant: Want | undefined = undefined;

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    // 保存启动参数
    this.incomingWant = want;
    logger.info(DOMAIN_ID, LOG_TAG, 'FeatureAbility 创建');
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    logger.info(DOMAIN_ID, LOG_TAG, '窗口阶段创建');
    
    // 根据参数决定加载哪个页面
    let targetPageUrl = 'pages/Index'; // 默认页面
    
    const requestedPage = this.incomingWant?.parameters?.targetPage;
    if (requestedPage === 'settings') {
      targetPageUrl = 'pages/SettingsPage';
    } else if (requestedPage === 'profile') {
      targetPageUrl = 'pages/UserProfilePage';
    }

    // 加载指定页面
    windowStage.loadContent(targetPageUrl, (error, data) => {
      if (error?.code) {
        logger.error(DOMAIN_ID, LOG_TAG, 
          `页面加载失败: 错误码 ${error.code}`);
        return;
      }
      logger.info(DOMAIN_ID, LOG_TAG, `页面加载成功: ${targetPageUrl}`);
    });
  }
}

4.3 目标 UIAbility 热启动处理

热启动:UIAbility 实例已经启动过并切换到后台,再次启动时触发。

import { AbilityConstant, Want, UIAbility } from '@kit.AbilityKit';
import { logger } from '@kit.PerformanceAnalysisKit';
import { window, UIContext } from '@kit.ArkUI';
import { AppStorage } from '@kit.ArkUI';

const DOMAIN_ID: number = 0xFF00;
const LOG_TAG: string = '[MessageAbility]';

export default class MessageAbility extends UIAbility {
  private uiContext: UIContext | undefined = undefined;

  onWindowStageCreate(windowStage: window.WindowStage): void {
    // 获取 UIContext 用于后续热启动时页面跳转
    windowStage.getMainWindow((error, windowData) => {
      if (error?.code) {
        logger.error(DOMAIN_ID, LOG_TAG, 
          `获取主窗口失败: 错误码 ${error.code}`);
        return;
      }
      this.uiContext = windowData.getUIContext();
    });

    windowStage.loadContent('pages/MessageIndex', (error, data) => {
      // 正常加载首页
    });
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    logger.info(DOMAIN_ID, LOG_TAG, '热启动 - onNewWant');
    
    // 处理新的启动意图,设置全局变量用于页面跳转
    const contactName = want?.parameters?.contactName;
    if (contactName) {
      AppStorage.setOrCreate<string>('selectedContact', contactName);
      AppStorage.setOrCreate<boolean>('navigateToChat', true);
    }
  }
}

4.4 热启动页面跳转处理

在首页面中监听全局变量变化并执行跳转:

// MessageIndex.ets
@Entry
@Component
struct MessageIndexPage {
  @State currentMessage: string = '消息首页';
  messageStack: NavPathStack = new NavPathStack();

  onPageShow(): void {
    // 检查是否有需要跳转的页面
    const shouldNavigate = AppStorage.get<boolean>('navigateToChat');
    const targetContact = AppStorage.get<string>('selectedContact');
    
    if (shouldNavigate && targetContact) {
      // 跳转到聊天页面
      this.messageStack.pushPath({ 
        name: 'chatPage',
        param: { contact: targetContact }
      }, false);
      
      // 清理全局变量
      AppStorage.delete('navigateToChat');
      AppStorage.delete('selectedContact');
    }
  }

  build() {
    Navigation(this.messageStack) {
      Column() {
        Text(this.currentMessage)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
      }
    }
    .mode(NavigationMode.Stack)
    .height('100%')
    .width('100%')
  }
}

5. UIAbility 的停止和清理

5.1 停止当前 UIAbility

import { common } from '@kit.AbilityKit';
import { logger } from '@kit.PerformanceAnalysisKit';

const LOG_TAG: string = '[CurrentPage]';
const DOMAIN_ID: number = 0xFF00;

@Entry
@Component
struct CurrentFeaturePage {
  build() {
    Column() {
      Button('完成并返回')
        .onClick(() => {
          this.finishCurrentAbility();
        })
        
      Button('取消操作')
        .onClick(() => {
          this.cancelAndExit();
        })
    }
  }

  private finishCurrentAbility(): void {
    const context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    context.terminateSelf((error) => {
      if (error?.code) {
        logger.error(DOMAIN_ID, LOG_TAG, 
          `停止失败: 错误码 ${error.code}, 错误信息 ${error.message}`);
        return;
      }
      logger.info(DOMAIN_ID, LOG_TAG, '页面正常退出');
    });
  }

  private cancelAndExit(): void {
    const context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    context.terminateSelf();
  }
}

5.2 配置不保留快照

在 module.json5 中配置 UIAbility 停止后不保留快照:

{
  "module": {
    "abilities": [
      {
        "name": "FeatureAbility",
        "removeMissionAfterTerminate": true
      }
    ]
  }
}

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐