HarmonyOS 广告服务 ArkTS 实现指南:从激励广告到多形式适配
在移动应用商业化进程中,广告服务是重要的变现途径,但不当的广告集成往往会破坏用户体验,甚至违反平台合规要求。HarmonyOS 针对广告服务提供了系统化的解决方案,支持 6 类主流广告形式,并明确了合规与体验平衡的实践标准。这篇文章我将基于华为开发者学院相关课程内容,从广告形式场景化选型、开发流程、合规风险规避三个维度,为大家总结广告服务集成方案。
文章目录
- 一、广告形式场景化选型:匹配 App 功能与用户习惯
- 1. Banner 广告:工具类 App 的 “轻量之选”
- 2. 原生广告:内容类 App 的 “融合之选”
- 3. 激励广告:游戏 / 工具类 App 的 “共赢之选”
- 4. 插屏广告:场景切换时的 “高效之选”
- 5. 开屏广告:流量入口的 “曝光之选”
- 6. 贴片广告:视频类 App 的 “内容绑定之选”
- 二、开发前置准备:权限与 SDK 配置
- 1. 权限声明与动态申请
- (1)配置module.json5权限
- (2)动态申请用户授权权限
- 2. 导入广告 SDK
- 三、激励广告完整流程
- 1. 页面结构定义(UI 部分)
- 2. 广告初始化与加载(逻辑部分)
- 3. 广告展示与奖励发放
- 四、ArkTS 多广告形式扩展:Banner 与原生广告
- 1. Banner 广告
- 五、 广告开发注意事项
- 上下文(Context)正确性:
- 状态管理与内存泄漏:
- 测试广告与正式广告区分:
- 六、 总结
一、广告形式场景化选型:匹配 App 功能与用户习惯
HarmonyOS 广告服务的 6 类广告形式各有特性,开发者需结合 App 的业务场景、用户交互节奏及核心功能,选择最适配的广告类型,避免 “为加广告而加广告” 的误区。以下为不同广告形式的场景化选型指南:
1. Banner 广告:工具类 App 的 “轻量之选”
Banner 广告以固定在页面顶部或底部的横幅形态存在,尺寸小巧(典型 320×50px),持续展示但不打断用户操作,适合核心功能为 “工具使用” 的 App。例如:
天气 App 的首页底部:用户查看天气时,Banner 广告位于视觉边缘,不影响温度、预警信息的核心阅读;计算器 App 的结果展示区下方:用户输入计算指令时,广告不会遮挡数字键盘或计算结果;待办清单 App 的任务列表顶部:滚动查看任务时,Banner 广告位置固定,避免随内容滚动频繁晃动。
选型关键:需确保 Banner 广告与页面背景色有轻微区分(如浅灰色背景),同时避免在同一页面叠加多个 Banner—— 例如部分工具 App 为追求收益,在顶部和底部同时放置 Banner,导致中间核心功能区被挤压,用户体验大幅下降。
2. 原生广告:内容类 App 的 “融合之选”
原生广告的核心优势是 “形态自定义”,可根据 App 界面风格调整字体、配色、布局,让广告内容与原生内容无缝融合,适合以 “信息流” 为核心的内容类 App。典型应用场景包括:
社交 App 的动态列表:将原生广告设计为 “好友动态卡片” 样式,仅在右上角标注 “广告” 标识,用户滑动浏览时无明显割裂感;电商 App 的商品推荐页:原生广告以 “商品卡片” 形态插入推荐列表,图片尺寸、价格字体与普通商品一致,仅通过 “ Sponsored” 标签区分;资讯 App 的文章列表:原生广告标题字体、摘要长度与普通文章统一,点击后跳转至广告落地页,避免用户因 “广告突兀” 产生抵触。
选型关键:自定义程度越高,越需注意合规边界 —— 根据 HarmonyOS 广告规范,原生广告必须在明显位置标注 “广告” 或 “ Sponsored” 标识,且标识颜色需与正文区分(如红色 “广告” 标签),禁止通过 “隐藏标识” 误导用户点击。
3. 激励广告:游戏 / 工具类 App 的 “共赢之选”
激励广告以 “用户完成指定行为获取奖励” 为核心逻辑,用户主动参与度高,适合需要 “用户付费解锁功能” 或 “虚拟道具变现” 的场景:
游戏 App:用户观看 30 秒视频广告后,获取游戏币、复活机会或解锁新关卡,相比 “强制付费” 更易被用户接受;图片处理 App:用户点击广告后,解锁 “去除水印”“高清导出” 等高级功能,替代传统 “付费会员” 模式;小说阅读 App:用户观看广告后,获取 “免费阅读 1 章” 的权限,平衡免费用户体验与商业化需求。
选型关键:奖励的 “即时性” 是用户接受度的核心 —— 例如游戏 App 需在广告播放完成后,立即弹出 “奖励已到账” 提示,并同步更新游戏内道具数量;若出现 “广告完成但奖励未到账” 的情况,会严重损害用户信任,甚至导致 App 被投诉。
4. 插屏广告:场景切换时的 “高效之选”
插屏广告在应用场景切换时(如页面跳转、功能完成)弹出,全屏或半屏展示,单次曝光强度高,适合 “用户操作间隙” 的场景:
游戏 App:关卡结束结算时弹出插屏广告,此时用户处于 “等待结果” 状态,广告不会打断游戏操作;阅读 App:章节切换加载时弹出广告,利用 “内容加载时间” 展示广告,减少对阅读节奏的影响;文件管理 App:文件导出成功后弹出广告,用户完成核心操作(导出文件)后,对广告的容忍度更高。
选型关键:控制弹出频率是避免用户反感的核心 —— 建议同一用户 1 小时内插屏广告弹出不超过 2 次,且必须提供明显的 “关闭” 按钮(如右上角红色圆形关闭图标,尺寸不小于 48×48px),禁止 “强制观看满时长才能关闭” 的设计。
5. 开屏广告:流量入口的 “曝光之选”
开屏广告在 App 启动时展示,持续 3-5 秒,是曝光量最大的广告形式,适合各类 App 的 “启动流量变现”:
社交 App:启动时展示 3 秒动态开屏广告,3 秒后出现 “跳过” 按钮,用户可选择 “观看获取奖励” 或 “直接跳过”;电商 App:大促期间(如 618、双 11),开屏广告展示促销活动信息,点击后直接跳转至活动主会场;工具 App:启动时展示静态开屏广告,背景图与 App 品牌色调一致,避免因 “风格冲突” 影响品牌认知。
选型关键:启动速度优先于广告展示 —— 需提前预加载开屏广告资源(如在 App 前一次退出时缓存广告图片 / 视频),避免因 “广告加载缓慢” 延长 App 启动时间;根据 HarmonyOS 性能规范,App 冷启动时间需控制在 3 秒内,开屏广告加载不可导致启动时间超过 5 秒。
6. 贴片广告:视频类 App 的 “内容绑定之选”
贴片广告嵌入在视频内容中,分为前贴片(视频播放前)、中贴片(播放中)、后贴片(播放后),适合以 “视频内容” 为核心的 App:
短视频 App:15 秒短视频播放前,展示 5 秒前贴片广告,点击 “跳过” 按钮可直接观看视频;教育 App:课程视频播放中,每 20 分钟插入 1 个 15 秒中贴片广告,利用 “学习间隙” 减少干扰;影视 App:电影播放结束后,展示后贴片广告,推荐 “同类影片” 或 “影视周边”,用户接受度较高。
选型关键:时长与插入时机需匹配视频长度 —— 前贴片建议≤5 秒(短视频)或≤15 秒(长视频),中贴片需避免在 “剧情高潮” 或 “知识点关键处” 插入,例如教育 App 可在 “章节总结” 后插入中贴片广告,不影响核心知识点学习。
二、开发前置准备:权限与 SDK 配置
1. 权限声明与动态申请
(1)配置module.json5权限
ArkTS 项目需在src/main/module.json5中声明广告服务所需权限,相比 Java 项目的config.json,module.json5支持更清晰的权限分类:
{"module": {"reqPermissions": [{"name": "ohos.permission.INTERNET", // 必选:加载广告资源"reason": "用于获取广告素材和数据","usedScene": { "ability": ["MainAbility"], "when": "always" }},{"name": "ohos.permission.WRITE_USER_STORAGE", // 可选:缓存广告素材"reason": "用于缓存广告图片和视频,提升加载速度","usedScene": { "ability": ["MainAbility"], "when": "always" },"grantMode": "user_grant" // 需用户授权的权限},{"name": "ohos.permission.READ_USER_STORAGE", // 可选:读取缓存"reason": "读取缓存的广告素材","usedScene": { "ability": ["MainAbility"], "when": "always" },"grantMode": "user_grant"}]}
}
(2)动态申请用户授权权限
存储权限属于 “用户授权权限”,需在 ArkTS 页面中通过requestPermissionsFromUser方法动态申请,避免直接使用导致权限报错:
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import bundle from '@ohos.bundle';// 动态申请存储权限
async function requestStoragePermission() {const permissions = ['ohos.permission.WRITE_USER_STORAGE', 'ohos.permission.READ_USER_STORAGE'];const atManager = abilityAccessCtrl.createAtManager();const bundleInfo = await bundle.getBundleInfoForSelf(bundle.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);const appInfo = bundleInfo.appInfo;// 检查权限是否已授权for (const permission of permissions) {const status = await atManager.checkAccessToken(appInfo.accessTokenId, permission);if (status !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {// 未授权则发起申请const result = await atManager.requestPermissionsFromUser(getContext(), permissions);if (result.authResults[0] !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {// 用户拒绝授权:提示“无法缓存广告,可能影响加载速度”promptAction.showToast({ message: '请授予存储权限以优化广告体验' });return false;}}}return true;
}
2. 导入广告 SDK
从华为开发者联盟官网下载 HarmonyOS 广告服务 ArkTS SDK(通常为ad-sdk-arkts.har),将其复制到项目src/main/libs目录下;
在src/main/module.json5中添加 SDK 依赖,确保编译器能识别 SDK 中的 ArkTS 类:
{"module": {"dependencies": [{"name": "ad-sdk-arkts","type": "har","source": "local","version": "1.0.0" // 与SDK实际版本一致}]}
}
同步项目(点击 IDE 中的 “Sync Now”),确保无 “模块未找到” 的编译错误。
三、激励广告完整流程
这里用激励广告举例,它是 ArkTS 项目中最常用的广告形式之一,以下从 “初始化→加载→展示→奖励发放” 全流程,提供可直接复用的代码:
1. 页面结构定义(UI 部分)
首先在 ArkTS 页面中定义 “观看广告获取奖励” 按钮,以及奖励展示区域:
import promptAction from '@ohos.promptAction';
import { IncentiveAd, AdParam, AdDisplayListener, IncentiveAdListener, Reward } from '@huawei/hms.ads';@Entry
@Component
struct IncentiveAdPage {// 状态变量:广告是否加载完成、当前游戏币数量@State isAdLoaded: boolean = false;@State coinCount: number = 0;// 激励广告实例private incentiveAd: IncentiveAd | null = null;// 测试广告单元ID(上线前替换为正式ID)private readonly AD_UNIT_ID: string = 'testx9dtjwj8hp';build() {Column({ space: 20 }) {// 奖励展示区域Text(`当前游戏币:${this.coinCount}`).fontSize(20).fontWeight(FontWeight.Bold);// 广告展示按钮(加载完成前禁用)Button('观看广告获取10游戏币').width(280).height(50).fontSize(16).enabled(this.isAdLoaded).onClick(() => this.showIncentiveAd()).backgroundColor(this.isAdLoaded ? '#007DFF' : '#CCCCCC');}.width('100%').height('100%').justifyContent(FlexAlign.Center).onPageShow(() => {// 页面显示时初始化广告this.initIncentiveAd();}).onPageHide(() => {// 页面隐藏时销毁广告,避免内存泄漏this.destroyIncentiveAd();});}
}
2. 广告初始化与加载(逻辑部分)
在页面类中补充广告初始化、加载的核心方法,通过监听onAdLoaded和onAdFailed事件处理加载状态:
// 初始化激励广告
private async initIncentiveAd() {// 先申请存储权限(可选,但推荐)const hasPermission = await requestStoragePermission();if (!hasPermission) return;// 创建激励广告实例this.incentiveAd = IncentiveAd.createIncentiveAd(this.AD_UNIT_ID);if (!this.incentiveAd) {promptAction.showToast({ message: '广告初始化失败' });return;}// 设置广告加载监听器this.incentiveAd.setAdLoadListener({onAdLoaded: () => {// 广告加载成功:更新按钮状态this.isAdLoaded = true;promptAction.showToast({ message: '广告已就绪,点击按钮观看' });},onAdFailed: (errorCode: number) => {// 广告加载失败:提示错误信息(错误码可参考华为广告文档)this.isAdLoaded = false;let errorMsg = '广告加载失败';switch (errorCode) {case 1001: errorMsg = '网络异常,请检查网络'; break;case 2001: errorMsg = '广告单元ID无效'; break;case 3001: errorMsg = '广告资源暂时不足'; break;}promptAction.showToast({ message: errorMsg });// 10秒后重试加载(避免频繁请求)setTimeout(() => this.loadIncentiveAd(), 10000);}});// 发起广告加载请求this.loadIncentiveAd();
}// 加载广告(单独封装,便于重试)
private loadIncentiveAd() {if (!this.incentiveAd) return;// 构建广告请求参数(可设置用户标签用于精准投放)const adParam = new AdParam.Builder().setGender(AdParam.GENDER_MALE) // 示例:设置用户性别(可选).setAge(25) // 示例:设置用户年龄(可选).build();this.incentiveAd.loadAd(adParam);
}// 销毁广告实例
private destroyIncentiveAd() {if (this.incentiveAd) {this.incentiveAd.destroy();this.incentiveAd = null;this.isAdLoaded = false;}
}
3. 广告展示与奖励发放
补充showIncentiveAd方法,实现广告展示、关闭及奖励发放逻辑,重点处理 “用户完成广告观看” 的事件:
// 展示激励广告
private showIncentiveAd() {if (!this.incentiveAd || !this.isAdLoaded) return;// 设置广告展示监听器(监听广告显示/关闭)this.incentiveAd.setAdDisplayListener({onAdDisplayed: () => {// 广告开始展示:禁用按钮,防止重复点击this.isAdLoaded = false;},onAdClosed: () => {// 广告关闭:重新加载广告,为下一次展示做准备this.loadIncentiveAd();},onAdClicked: () => {// 广告被点击:可记录点击数据(可选)console.log('激励广告被点击');}});// 设置奖励监听器(监听用户是否完成广告任务)this.incentiveAd.setIncentiveAdListener({onUserEarnedReward: (reward: Reward) => {// 用户完成广告观看:发放奖励(reward.amount为奖励数量,此处固定为10)const rewardAmount = reward.amount || 10;this.coinCount += rewardAmount;// 提示奖励到账promptAction.showToast({message: `奖励到账!获得${rewardAmount}游戏币`,duration: 2000});},onRewardVerify: (isVerify: boolean) => {// 奖励验证(部分场景下广告平台会验证奖励有效性)if (!isVerify) {promptAction.showToast({ message: '奖励验证失败,请稍后重试' });}}});// 展示广告(需传入当前页面的Context)this.incentiveAd.show(getContext() as common.UIAbilityContext);
}
四、ArkTS 多广告形式扩展:Banner 与原生广告
除激励广告外,Banner 广告和原生广告也是 ArkTS 项目中的常用形式,以下补充核心实现代码:
1. Banner 广告
Banner 广告通过BannerAd组件直接嵌入页面,适合工具类 App 的顶部 / 底部展示:
import { BannerAd, AdParam } from '@huawei/hms.ads';@Component
struct BannerAdComponent {// Banner广告单元ID(需与激励广告ID区分)private readonly AD_UNIT_ID: string = 'testb4znbuh3n2';build() {// Banner广告组件(宽高比建议16:9,避免拉伸)BannerAd({adUnitId: this.AD_UNIT_ID,adParam: new AdParam.Builder().build(),// 广告加载状态回调onAdLoaded: () => {console.log('Banner广告加载成功');},onAdFailed: (errorCode: number) => {console.error(`Banner广告加载失败:${errorCode}`);},onAdClicked: () => {console.log('Banner广告被点击');}}).width('100%').height(150) // 典型Banner尺寸:宽度满屏,高度150px.margin({ top: 10 });}
}// 使用方式:在页面的Column中直接引入
// Column() {
// BannerAdComponent()
// // 其他页面内容
// }
- 原生广告
原生广告需自定义 UI 样式,与页面内容融合,适合信息流场景:
import { NativeAd, NativeAdLoadListener, NativeAdData } from '@huawei/hms.ads';@Component
struct NativeAdComponent {@State nativeAdData: NativeAdData | null = null;private nativeAd: NativeAd | null = null;private readonly AD_UNIT_ID: string = 'testn44hll3dty';build() {if (!this.nativeAdData) return;// 自定义原生广告UI(模拟信息流卡片样式)Column({ space: 8 }) {// 广告标识(必加,合规要求)Text('广告').fontSize(12).backgroundColor('#FF4444').color('white').padding(2).alignSelf(ItemAlign.Start);// 广告标题Text(this.nativeAdData.getTitle() || '未知标题').fontSize(16).fontWeight(FontWeight.Bold);// 广告描述Text(this.nativeAdData.getDescription() || '未知描述').fontSize(14).color('#666666').maxLines(2).textOverflow({ overflow: TextOverflow.Ellipsis });// 广告图片(若有)if (this.nativeAdData.getImages()?.length) {Image(this.nativeAdData.getImages()![0].getUrl()).width('100%').height(180).objectFit(ImageFit.Cover);}// 广告按钮Button(this.nativeAdData.getCallToAction() || '立即查看').width('100%').height(40).fontSize(14).backgroundColor('#007DFF').onClick(() => {// 触发广告点击(必须调用,否则无法统计点击数据)this.nativeAd?.onAdClicked(this.nativeAdData);});}.width('100%').padding(12).backgroundColor('white').borderRadius(8).margin(10).shadow({ radius: 4, color: '#EEEEEE' });}aboutToAppear() {// 页面渲染前初始化原生广告this.initNativeAd();}aboutToDisappear() {// 页面销毁前销毁广告if (this.nativeAd) {this.nativeAd.destroy();this.nativeAd = null;}}private initNativeAd() {this.nativeAd = NativeAd.createNativeAd(this.AD_UNIT_ID);this.nativeAd?.setAdLoadListener({onAdLoaded: (ads: NativeAdData[]) => {// 加载到广告数据,取第一条展示if (ads.length > 0) {this.nativeAdData = ads[0];// 触发广告曝光(必须调用,否则无法统计曝光数据)this.nativeAd?.onAdImpression(this.nativeAdData);}},onAdFailed: (errorCode: number) => {console.error(`原生广告加载失败:${errorCode}`);}});// 加载原生广告(请求1条数据)this.nativeAd?.loadAd(1);}
}
五、 广告开发注意事项
上下文(Context)正确性:
广告展示(如incentiveAd.show())需传入UIAbilityContext,避免使用ApplicationContext导致崩溃,可通过getContext() as common.UIAbilityContext获取当前页面上下文。
状态管理与内存泄漏:
使用@State装饰器管理广告加载状态,在onPageHide或aboutToDisappear中销毁广告实例,避免页面销毁后广告仍占用资源。
测试广告与正式广告区分:
集成初期使用华为提供的测试广告单元 ID(如激励广告testx9dtjwj8hp),上线前需在华为广告平台申请正式 ID,并替换代码中的测试 ID,否则无法产生真实收益。
六、 总结
总结一下,关于HarmonyOS 广告服务的集成,核心是在 “商业化变现” 与 “用户体验” 之间找到平衡。我们作为开发者需遵循以下原则:
场景优先:根据 App 的核心功能(如工具、内容、游戏)选择适配的广告形式,避免 “一刀切” 的广告策略;合规底线:严格遵守隐私保护、内容审核、标识标注的合规要求,避免因违规导致 App 下架;体验优化:通过预加载、频率控制、交互适配等手段,减少广告对用户的干扰,提升用户接受度。
好了,本篇文章就讲到这里,谢谢大家阅读观看。