当前位置: 首页 > article >正文

华为HarmonyOS打造开放、合规的广告生态 - 贴片广告

场景介绍

贴片广告是一种在视频播放前、视频播放中或视频播放结束后插入的视频或图片广告。

接口说明

接口名

描述

loadAd(adParam: AdRequestParams, adOptions: AdOptions, listener: AdLoadListener): void

请求单广告位广告,通过AdRequestParams、AdOptions进行广告请求参数设置,通过AdLoadListener监听广告请求回调。

AdComponent(ads: advertising.Advertisement[], displayOptions: advertising.AdDisplayOptions, interactionListener: advertising.AdInteractionListener, @BuilderParam adRenderer?: () => void): void

展示广告,通过AdDisplayOptions进行广告展示参数设置,通过AdInteractionListener监听广告状态回调。

开发步骤

  1. 获取OAID。

    如果想要为用户更精准的推送广告,可以在请求参数AdRequestParams中添加oaid属性。

    如何获取OAID参见获取OAID信息。

    说明

    使用以下示例中提供的测试广告位必须先获取OAID信息。

  2. 请求单广告位广告。

    需要创建一个AdLoader对象,通过AdLoader的loadAd方法请求广告,最后通过AdLoadListener来监听广告的加载状态。

    在请求贴片广告时,需要在AdOptions中设置两个参数:totalDuration和placementAdCountDownDesc。

    请求广告关键参数如下所示:

    请求广告参数名

    类型

    必填

    说明

    adType

    number

    请求广告类型,贴片广告类型为60。

    adId

    string

    广告位ID。

    • 如果仅调测广告,可使用测试广告位ID:testy3cglm3pj0。
    • 如果要接入正式广告,则需要申请正式的广告位ID。可在应用发布前进入流量变现官网,点击“开始变现”,登录鲸鸿动能媒体服务平台进行申请,具体操作详情请参见展示位创建。

    oaid

    string

    开放匿名设备标识符,用于精准推送广告。不填无法获取到个性化广告。

    示例代码如下所示:
     
      
    1. import { advertising, identifier } from '@kit.AdsKit';
    2. import { common } from '@kit.AbilityKit';
    3. import { hilog } from '@kit.PerformanceAnalysisKit';
    4. import { BusinessError } from '@kit.BasicServicesKit';
    5. import { router } from '@kit.ArkUI';
    6. @Entry
    7. @Component
    8. struct Index {
    9. private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    10. // 获取到的OAID
    11. private oaid: string = '';
    12. aboutToAppear() {
    13. try {
    14. // 使用Promise回调方式获取OAID
    15. identifier.getOAID().then((data: string) => {
    16. this.oaid = data;
    17. hilog.info(0x0000, 'testTag', '%{public}s', 'Succeeded in getting adsIdentifierInfo by promise');
    18. }).catch((error: BusinessError) => {
    19. hilog.error(0x0000, 'testTag', '%{public}s',
    20. `Failed to get adsIdentifierInfo, code: ${error.code}, message: ${error.message}`);
    21. })
    22. } catch (error) {
    23. hilog.error(0x0000, 'testTag', '%{public}s', `Catch err, code: ${error.code}, message: ${error.message}`);
    24. }
    25. }
    26. build() {
    27. Row() {
    28. Button('加载广告', { type: ButtonType.Normal, stateEffect: true })
    29. .onClick(() => {
    30. // 调用加载广告方法
    31. requestAd(this.context, this.oaid);
    32. })
    33. .borderRadius(8)
    34. .backgroundColor(0x317aff)
    35. .width(90)
    36. .height(40)
    37. }
    38. .height('100%')
    39. }
    40. }
    41. /**
    42. * 加载广告
    43. *
    44. * @param context 上下文环境
    45. * @param oaid OAID信息
    46. */
    47. function requestAd(context: common.Context, oaid: string): void {
    48. const adRequestParam: advertising.AdRequestParams = {
    49. // 广告类型
    50. adType: 60,
    51. // 'testy3cglm3pj0'为测试专用的广告位ID,App正式发布时需要改为正式的广告位ID
    52. adId: 'testy3cglm3pj0',
    53. // 在AdRequestParams中添加oaid参数
    54. oaid: oaid,
    55. // 用于区分普通请求和预加载请求,默认值false代表普通请求,true代表预加载请求
    56. isPreload: false
    57. };
    58. const adOptions: advertising.AdOptions = {
    59. // 在AdOptions中添加totalDuration参数,用于设置贴片广告展示时长(贴片广告必填)
    60. totalDuration: 30,
    61. // 在AdOptions中添加placementAdCountDownDesc参数,设置贴片广告倒计时文案(可选,填写了则展示文案,不填写则只展示倒计时)
    62. placementAdCountDownDesc: encodeURI('VIP免广告'),
    63. // 是否允许流量下载 0不允许 1允许,不设置以广告主设置为准
    64. allowMobileTraffic: 0,
    65. // 是否希望根据 COPPA 的规定将您的内容视为面向儿童的内容: -1默认值,不确定 0不希望 1希望
    66. tagForChildProtection: -1,
    67. // 是否希望按适合未达到法定承诺年龄的欧洲经济区 (EEA) 用户的方式处理该广告请求: -1默认值,不确定 0不希望 1希望
    68. tagForUnderAgeOfPromise: -1,
    69. // 设置广告内容分级上限: W: 3+,所有受众 PI: 7+,家长指导 J:12+,青少年 A: 16+/18+,成人受众
    70. adContentClassification: 'A'
    71. };
    72. // 广告请求回调监听
    73. const adLoaderListener: advertising.AdLoadListener = {
    74. // 广告请求失败回调
    75. onAdLoadFailure: (errorCode: number, errorMsg: string) => {
    76. hilog.error(0x0000, 'testTag', '%{public}s',
    77. `Failed to request single ad, errorCode is: ${errorCode}, errorMsg is: ${errorMsg}`);
    78. },
    79. // 广告请求成功回调
    80. onAdLoadSuccess: (ads: Array<advertising.Advertisement>) => {
    81. hilog.info(0x0000, 'testTag', '%{public}s', 'Succeeded in requesting single ad!');
    82. // 保存请求到的广告内容用于展示
    83. const returnAds = ads;
    84. // 路由到广告展示页面
    85. routePage('pages/PlacementAdPage', returnAds);
    86. }
    87. };
    88. // 创建AdLoader广告对象
    89. const load: advertising.AdLoader = new advertising.AdLoader(context);
    90. // 调用广告请求接口
    91. hilog.info(0x0000, 'testTag', '%{public}s', 'Request single ad!');
    92. load.loadAd(adRequestParam, adOptions, adLoaderListener);
    93. }
    94. /**
    95. * 路由跳转
    96. *
    97. * @param pageUri 要路由到的页面
    98. */
    99. async function routePage(pageUri: string, ads: Array<advertising.Advertisement | null>) {
    100. let options: router.RouterOptions = {
    101. url: pageUri,
    102. params: {
    103. ads: ads
    104. }
    105. }
    106. try {
    107. hilog.info(0x0000, 'testTag', '%{public}s', `RoutePage: ${pageUri}`);
    108. router.pushUrl(options);
    109. } catch (error) {
    110. hilog.error(0x0000, 'testTag', '%{public}s',
    111. `Failed to routePage callback, code: ${error.code}, msg: ${error.message}`);
    112. }
    113. }

  3. 展示广告。

    在您的页面中使用AdComponent组件展示贴片广告,由媒体判断流量场景下,可以自动播放则展示广告,反之则不展示。以前贴广告为例,前贴广告播放完成后进入正片播放。您需要在entry/src/main/resources/base/profile/main_pages.json文件中添加页面,如下图所示。

    您需要在media和rawfile目录下分别指定正片未播放时的预览图video_preview.PNG和对应的正片文件videoTest.mp4,如下图所示。

    示例代码如下所示:
     
      
    1. import { router, window } from '@kit.ArkUI';
    2. import { BusinessError } from '@kit.BasicServicesKit';
    3. import { advertising, AdComponent } from '@kit.AdsKit';
    4. import { hilog } from '@kit.PerformanceAnalysisKit';
    5. @Entry
    6. @Component
    7. export struct PlacementAdPage {
    8. // 是否竖屏
    9. private portrait: boolean = true;
    10. // 请求到的广告内容
    11. private ads: Array<advertising.Advertisement> = [];
    12. // 广告展示参数
    13. private adDisplayOptions: advertising.AdDisplayOptions = {
    14. // 是否静音,默认不静音
    15. mute: false
    16. }
    17. // 广告参数
    18. private adOptions: advertising.AdOptions = {
    19. // 设置贴片广告展示时长(贴片广告必填)
    20. totalDuration: 30,
    21. // 设置贴片广告倒计时文案,文案需要使用encodeURI编码(可选,填写了则展示文案,不填写则只展示倒计时)
    22. placementAdCountDownDesc: encodeURI('VIP免广告'),
    23. // 是否希望根据 COPPA 的规定将您的内容视为面向儿童的内容: -1默认值,不确定 0不希望 1希望
    24. tagForChildProtection: -1,
    25. // 是否希望按适合未达到法定承诺年龄的欧洲经济区 (EEA) 用户的方式处理该广告请求: -1默认值,不确定 0不希望 1希望
    26. tagForUnderAgeOfPromise: -1,
    27. // 设置广告内容分级上限: W: 3+,所有受众 PI: 7+,家长指导 J:12+,青少年 A: 16+/18+,成人受众
    28. adContentClassification: 'A'
    29. }
    30. // 已经播放的贴片广告数量
    31. private playedAdSize: number = 0;
    32. // 是否播放正片
    33. @State isPlayVideo: boolean = false;
    34. // 视频播放控制器
    35. private controller: VideoController = new VideoController();
    36. // 指定视频未播放时的预览图片路径
    37. private previewUris: Resource = $r('app.media.video_preview');
    38. // 指定视频播放源的路径,这里取本地视频资源
    39. private innerResource: Resource = $rawfile('videoTest.mp4');
    40. // 用于渲染右上角倒计时
    41. private countDownTxtPlaceholder: string = '%d | %s';
    42. @State countDownTxt: string = '';
    43. aboutToAppear() {
    44. const params: Record<string, Object> = router.getParams() as Record<string, Object>;
    45. if (params && params.ads as Array<advertising.Advertisement>) {
    46. this.ads = params.ads as Array<advertising.Advertisement>;
    47. this.adOptions = params.adOptions as advertising.AdOptions;
    48. this.initData();
    49. }
    50. }
    51. build() {
    52. Stack({ alignContent: Alignment.TopEnd }) {
    53. // AdComponent组件用于展示非全屏广告
    54. AdComponent({
    55. ads: this.ads, displayOptions: this.adDisplayOptions,
    56. interactionListener: {
    57. // 广告状态变化回调
    58. onStatusChanged: (status: string, ad: advertising.Advertisement, data: string) => {
    59. switch (status) {
    60. case 'onPortrait':
    61. hilog.info(0x0000, 'testTag', '%{public}s', 'Status is onPortrait');
    62. // 设置屏幕方向为竖屏或返回上一页
    63. this.setWindowPortrait();
    64. break;
    65. case 'onLandscape':
    66. hilog.info(0x0000, 'testTag', '%{public}s', 'Status is onLandscape');
    67. // 设置屏幕方向为横屏
    68. this.setWindowLandscape();
    69. break;
    70. case 'onMediaProgress':
    71. hilog.info(0x0000, 'testTag', '%{public}s', 'Status is onMediaProgress');
    72. break;
    73. case 'onMediaStart':
    74. hilog.info(0x0000, 'testTag', '%{public}s', 'Status is onMediaStart');
    75. break;
    76. case 'onMediaPause':
    77. hilog.info(0x0000, 'testTag', '%{public}s', 'Status is onMediaPause');
    78. break;
    79. case 'onMediaStop':
    80. hilog.info(0x0000, 'testTag', '%{public}s', 'Status is onMediaStop');
    81. break;
    82. case 'onMediaComplete':
    83. hilog.info(0x0000, 'testTag', '%{public}s', 'Status is onMediaComplete');
    84. // 所有广告都播放完毕后,开始播放正片
    85. this.playedAdSize++;
    86. if (this.playedAdSize === this.ads.length) {
    87. this.isPlayVideo = true;
    88. }
    89. break;
    90. case 'onMediaError':
    91. hilog.error(0x0000, 'testTag', '%{public}s', 'Status is onMediaError');
    92. break;
    93. case 'onMediaCountdown':
    94. try {
    95. hilog.info(0x0000, 'testTag', '%{public}s', 'Status is onMediaCountdown');
    96. const parseData: Record<string, number> = JSON.parse(JSON.stringify(data));
    97. this.updateCountDownTxt(parseData.countdownTime);
    98. } catch (e) {
    99. hilog.error(0x0000, 'testTag', '%{public}s',
    100. `Failed to parse data, code: ${e.code}, msg: ${e.message}`);
    101. }
    102. break;
    103. }
    104. }
    105. }
    106. })
    107. .visibility(!this.isPlayVideo ? Visibility.Visible : Visibility.None)
    108. .width('100%')
    109. .height('100%')
    110. Row() {
    111. if (this.countDownTxt) {
    112. Text(this.countDownTxt.split('').join('\u200B'))
    113. .fontSize(12)
    114. .textAlign(TextAlign.Center)
    115. .maxLines(1)
    116. .fontColor(Color.White)
    117. .lineHeight(12)
    118. .textOverflow({ overflow: TextOverflow.Ellipsis })
    119. .maxLines(1)
    120. .backgroundColor('#66000000')
    121. .border({ radius: 25 })
    122. .padding({
    123. left: 8,
    124. right: 8,
    125. top: 6,
    126. bottom: 6
    127. })
    128. .margin({ right: 16, top: 16 })
    129. .height(24)
    130. .constraintSize({ minWidth: 60, maxWidth: 100 })
    131. .onClick((event: ClickEvent) => {
    132. hilog.info(0x0000, 'testTag', '%{public}s', 'OnVipClicked, do something...');
    133. })
    134. }
    135. }
    136. .alignItems(VerticalAlign.Top)
    137. .justifyContent(FlexAlign.End)
    138. Video({
    139. src: this.innerResource,
    140. previewUri: this.previewUris,
    141. controller: this.controller
    142. })
    143. .visibility(this.isPlayVideo ? Visibility.Visible : Visibility.None)
    144. .autoPlay(this.isPlayVideo ? true : false)
    145. .controls(false)
    146. .width('100%')
    147. .height('100%')
    148. }.width('100%').height('100%')
    149. }
    150. /**
    151. * 设置竖屏或返回上一页
    152. */
    153. private setWindowPortrait() {
    154. hilog.info(0x0000, 'testTag', '%{public}s', `Set WindowPortrait, portrait: ${this.portrait}`);
    155. if (!this.portrait) {
    156. window.getLastWindow(getContext(this), (err: BusinessError, win) => {
    157. win.setPreferredOrientation(window.Orientation.PORTRAIT)
    158. });
    159. this.portrait = true;
    160. } else {
    161. router.back();
    162. }
    163. }
    164. /**
    165. * 设置横屏(正向)
    166. */
    167. private setWindowLandscape() {
    168. hilog.info(0x0000, 'testTag', '%{public}s', `Set WindowLandscape, portrait: ${this.portrait}`);
    169. if (this.portrait) {
    170. window.getLastWindow(getContext(this), (err: BusinessError, win) => {
    171. win.setPreferredOrientation(window.Orientation.LANDSCAPE)
    172. });
    173. this.portrait = false;
    174. }
    175. }
    176. private initData() {
    177. this.initCountDownText();
    178. }
    179. private initCountDownText() {
    180. const decodeText = this.decodeString(this.adOptions?.placementAdCountDownDesc as string);
    181. if (!this.isBlank(decodeText)) {
    182. this.countDownTxtPlaceholder = this.countDownTxtPlaceholder.replace('%s', decodeText);
    183. } else {
    184. this.countDownTxtPlaceholder = '%d';
    185. }
    186. }
    187. private updateCountDownTxt(leftTime: number) {
    188. hilog.info(0x0000, 'testTag', '%{public}s', `Show LeftTime: ${leftTime}`);
    189. this.countDownTxt = this.countDownTxtPlaceholder.replace('%d', leftTime + '');
    190. }
    191. private decodeString(str: string): string {
    192. if (!str) {
    193. return str;
    194. }
    195. let decodeUrl = str;
    196. try {
    197. decodeUrl = decodeURIComponent(str.replace(/\+/g, '%20'));
    198. } catch (e) {
    199. hilog.error(0x0000, 'testTag', '%{public}s', `Failed to decodeURIComponent, code:${e.code}, msg: ${e.message}`);
    200. }
    201. return decodeUrl;
    202. }
    203. private isBlank(str: string): boolean {
    204. if (str === null || str === undefined) {
    205. return true;
    206. }
    207. if (typeof str === 'string') {
    208. return str.trim().length === 0;
    209. }
    210. return false;
    211. }
    212. }

http://www.kler.cn/a/379756.html

相关文章:

  • 【自动化利器】12个评估大语言模型(LLM)质量的自动化框架
  • BEV数据集标注成本高?BEVPose:减少对标注数据依赖!
  • linux shell脚本学习(1):shell脚本基本概念与操作
  • Qt项目实战:红绿灯小程序
  • Redis 初学者指南
  • CloudCompare——基于连通性的点云分类【2024最新版】
  • 关于wordpress instagram feed 插件 (现更名为Smash Balloon Social Photo Feed)
  • 使用OCC,输入一个三维的曲面body,和一个平面plane,如果平面plane与body有相交,输出相交结果左右两部分
  • 多线程和线程同步基础篇学习笔记(Linux)
  • 数据结构---链表实现双端队列
  • 小程序跳转另一个小程序
  • WiFi一直获取不到IP地址是怎么回事?
  • Spark中的宽窄依赖
  • 【6G 需求与定义】ITU(国际电联)对全球6G标准的愿景
  • 【工具变量】大数据管理机构改革DID(2007-2023年)
  • el-table 滚动条重置 手动控制滚动条
  • 鸿蒙OS带来前端的机遇:ArkTS与Typescript+ArkUI与SwiftUI的简单对比你就知道了
  • 【编程语言】Kotlin快速入门 - 泛型
  • 深入解密 K 均值聚类:从理论基础到 Python 实践
  • 72页PPT高效协同:SOP运营变革规划核心框架
  • VMware虚拟机Debian扩展磁盘
  • IO 多路复用技术:原理、类型及 Go 实现
  • 助力风力发电风机设备智能化巡检,基于YOLOv8全系列【n/s/m/l/x】参数模型开发构建无人机巡检场景下风机叶片缺陷问题智能化检测预警模型
  • 初级社会工作者试题
  • 代码随想录第十七天
  • [双指针] 盛最多水的容器, 有效三角形的个数, 和为s的两个数