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

news2024/11/5 6:01:52

场景介绍

激励广告是一种全屏幕的视频广告,用户可以选择点击观看,以换取相应奖励。

接口说明

接口名

描述

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

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

showAd(ad: Advertisement, options: AdDisplayOptions, context?: common.UIAbilityContext): void

展示广告,通过AdDisplayOptions进行广告展示参数设置。

开发步骤

  1. 获取OAID。

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

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

    说明

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

  2. 请求单广告位广告。

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

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

    请求广告参数名

    类型

    必填

    说明

    adType

    number

    请求广告类型,激励广告类型为7。

    adId

    string

    广告位ID。

    • 如果仅调测广告,可使用测试广告位ID:testx9dtjwj8hp。
    • 如果要接入正式广告,则需要申请正式的广告位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. @Entry
    6. @Component
    7. struct Index {
    8. private ads: Array<advertising.Advertisement> = [];
    9. private context = getContext(this) as common.UIAbilityContext;
    10. private oaid: string = '';
    11. aboutToAppear() {
    12. try {
    13. // 使用Promise回调方式获取OAID
    14. identifier.getOAID().then((data: string) => {
    15. this.oaid = data;
    16. hilog.info(0x0000, 'testTag', '%{public}s', 'Succeeded in getting adsIdentifierInfo by promise');
    17. }).catch((error: BusinessError) => {
    18. hilog.error(0x0000, 'testTag', '%{public}s', `Failed to get adsIdentifierInfo, message: ${error.message}`);
    19. })
    20. } catch (error) {
    21. hilog.error(0x0000, 'testTag', '%{public}s', `Catch err, code: ${error.code}, message: ${error.message}`);
    22. }
    23. }
    24. build() {
    25. Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
    26. Row() {
    27. Button('requestAd').onClick(() => {
    28. let load: advertising.AdLoader = new advertising.AdLoader(this.context);
    29. this.requestAd(load);
    30. }).width('45%')
    31. }
    32. }
    33. }
    34. private requestAd(adLoader: advertising.AdLoader): void {
    35. const adRequestParam: advertising.AdRequestParams = {
    36. // 广告类型:激励广告
    37. adType: 7,
    38. // 'testx9dtjwj8hp'为测试专用的广告位ID,App正式发布时需要改为正式的广告位ID
    39. adId: 'testx9dtjwj8hp',
    40. // 开放匿名设备标识符
    41. oaid: this.oaid
    42. };
    43. const adOption: advertising.AdOptions = {
    44. // 设置是否请求非个性化广告
    45. nonPersonalizedAd: 0,
    46. // 是否允许流量下载0:不允许,1:允许,不设置以广告主设置为准
    47. allowMobileTraffic: 0,
    48. // 是否希望根据 COPPA 的规定将您的内容视为面向儿童的内容: -1默认值,不确定 0不希望 1希望
    49. tagForChildProtection: -1,
    50. // 是否希望按适合未达到法定承诺年龄的欧洲经济区 (EEA) 用户的方式处理该广告请求: -1默认值,不确定 0不希望 1希望
    51. tagForUnderAgeOfPromise: -1,
    52. // 设置广告内容分级上限: W: 3+,所有受众 PI: 7+,家长指导 J:12+,青少年 A: 16+/18+,成人受众
    53. adContentClassification: 'A'
    54. };
    55. const adLoaderListener: advertising.AdLoadListener = {
    56. onAdLoadFailure: (errorCode: number, errorMsg: string) => {
    57. hilog.error(0x0000, 'testTag', '%{public}s',
    58. `Failed to request ad, message: ${errorMsg}, error code: ${errorCode}`);
    59. },
    60. onAdLoadSuccess: (ads: Array<advertising.Advertisement>) => {
    61. hilog.info(0x0000, 'testTag', '%{public}s', `Succeeded in requesting ad`);
    62. this.ads.push(...ads);
    63. },
    64. };
    65. adLoader.loadAd(adRequestParam, adOption, adLoaderListener);
    66. }
    67. }

  3. 事件订阅。

    开发者需要在App中订阅com.huawei.hms.pps.action.PPS_REWARD_STATUS_CHANGED事件来监听激励广告页面变化并接收奖励信息。示例代码中的订阅方法registerPPSReceiver()需要在每次展示广告前调用 。

    在订阅到公共事件后,可以从CommonEventData的parameters参数中使用"reward_ad_status"作为key值获取激励广告页面变化状态,使用"reward_ad_data"作为key值获取奖励信息,属性rewardType用来获取奖励物品的名称,rewardAmount用来获取奖励物品的数量。

    示例代码如下所示:

     
      
    1. import { commonEventManager, BusinessError } from '@kit.BasicServicesKit';
    2. import { hilog } from '@kit.PerformanceAnalysisKit';
    3. const KEY_REWARD_DATA = "reward_ad_data";
    4. const KEY_REWARD_STATUS = "reward_ad_status";
    5. export class RewardAdStatusHandler {
    6. // 用于保存创建成功的订阅者对象,后续使用其完成订阅及退订的动作
    7. private subscriber: commonEventManager.CommonEventSubscriber | null = null;
    8. // 订阅方法,需要在每次展示广告前调用
    9. public registerPPSReceiver(): void {
    10. if (this.subscriber) {
    11. this.unRegisterPPSReceiver();
    12. }
    13. // 订阅者信息
    14. const subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
    15. events: ['com.huawei.hms.pps.action.PPS_REWARD_STATUS_CHANGED'],
    16. // publisherBundleName被设置为"com.huawei.hms.adsservice",这意味着只有来自该包名的事件才会被订阅者接受和处理。
    17. // 如果没有明确声明publisherBundleName,那么订阅者可能会收到来自其它包名的伪造事件,从而导致安全性问题或误导。
    18. publisherBundleName: 'com.huawei.hms.adsservice'
    19. };
    20. // 创建订阅者回调
    21. commonEventManager.createSubscriber(subscribeInfo, (err: BusinessError, commonEventSubscriber:
    22. commonEventManager.CommonEventSubscriber) => {
    23. if (err) {
    24. hilog.error(0x0000, 'testTag', '%{public}s',
    25. `createSubscriber error, code: ${err.code}, message: ${err.message}`);
    26. return;
    27. }
    28. hilog.info(0x0000, 'testTag', '%{public}s', 'Succeeded in creating subscriber');
    29. this.subscriber = commonEventSubscriber;
    30. // 订阅公共事件回调
    31. if (!this.subscriber) {
    32. hilog.warn(0x0000, 'testTag', '%{public}s', 'Need create subscriber');
    33. return;
    34. }
    35. commonEventManager.subscribe(this.subscriber, (err: BusinessError, commonEventSubscriber:
    36. commonEventManager.CommonEventData) => {
    37. if (err) {
    38. hilog.error(0x0000, 'testTag', '%{public}s', `Subscribe error, code: ${err.code}, message: ${err.message}`);
    39. } else {
    40. hilog.info(0x0000, 'testTag', '%{public}s', 'Subscribe data');
    41. const status: string = commonEventSubscriber?.parameters?.[KEY_REWARD_STATUS];
    42. switch (status) {
    43. case AdStatus.AD_OPEN:
    44. hilog.info(0x0000, 'testTag', '%{public}s', 'onAdOpen');
    45. break;
    46. case AdStatus.AD_CLICKED:
    47. hilog.info(0x0000, 'testTag', '%{public}s', 'onAdClick');
    48. break;
    49. case AdStatus.AD_CLOSED:
    50. hilog.info(0x0000, 'testTag', '%{public}s', 'onAdClose');
    51. this.unRegisterPPSReceiver();
    52. break;
    53. case AdStatus.AD_REWARDED:
    54. const rewardData: Record<string, string | number> = commonEventSubscriber?.parameters?.[KEY_REWARD_DATA];
    55. const rewardType: string = rewardData?.rewardType as string;
    56. const rewardAmount: number = rewardData?.rewardAmount as number;
    57. hilog.info(0x0000, 'testTag', '%{public}s',
    58. `onAdReward, rewardType: ${rewardType}, rewardAmount: ${rewardAmount}`);
    59. break;
    60. case AdStatus.AD_VIDEO_START:
    61. hilog.info(0x0000, 'testTag', '%{public}s', 'onAdVideoStart');
    62. break;
    63. case AdStatus.AD_COMPLETED:
    64. hilog.info(0x0000, 'testTag', '%{public}s', 'onAdCompleted');
    65. break;
    66. default:
    67. break;
    68. }
    69. }
    70. });
    71. });
    72. }
    73. // 取消订阅
    74. public unRegisterPPSReceiver(): void {
    75. commonEventManager.unsubscribe(this.subscriber, (err: BusinessError) => {
    76. if (err) {
    77. hilog.error(0x0000, 'testTag', '%{public}s', `Unsubscribe error, code: ${err.code}, message: ${err.message}`);
    78. } else {
    79. hilog.info(0x0000, 'testTag', '%{public}s', 'Succeeded in unsubscribing');
    80. this.subscriber = null;
    81. }
    82. });
    83. }
    84. }
    85. enum AdStatus {
    86. AD_OPEN = 'onAdOpen',
    87. AD_CLICKED = 'onAdClick',
    88. AD_CLOSED = 'onAdClose',
    89. AD_REWARDED = 'onAdReward',
    90. AD_VIDEO_START = 'onVideoPlayBegin',
    91. AD_COMPLETED = 'onVideoPlayEnd'
    92. }

  4. 展示广告。

    ads为步骤2请求到的广告信息,调用showAd方法来展示广告。示例代码如下所示:
     
      
    1. import { advertising } from '@kit.AdsKit';
    2. import { common } from '@kit.AbilityKit';
    3. @Entry
    4. @Component
    5. struct Index {
    6. private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    7. // 步骤2中请求到的广告内容
    8. private ads: Array<advertising.Advertisement> = [];
    9. private displayOptions: advertising.AdDisplayOptions = {
    10. // 激励广告视频播放是否静音
    11. mute: true
    12. };
    13. build() {
    14. Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
    15. Row() {
    16. Button('showAd').onClick(() => {
    17. this.showAd();
    18. }).width('45%')
    19. }
    20. }
    21. }
    22. private showAd() {
    23. let load: advertising.AdLoader = new advertising.AdLoader(this.context);
    24. // 此处ads[0]表示请求到的第一个广告,用户根据实际情况选择
    25. advertising.showAd(this.ads[0], this.displayOptions, this.context);
    26. }
    27. }

校验激励广告服务端验证回调

服务端验证回调是指鲸鸿动能平台发送给媒体服务器的网址请求,其中带有特定的查询参数,用来通知媒体服务器某位用户因为与激励视频广告互动而应予以奖励,从而规避欺骗的行为。

奖励用户

  • 在给用户发奖励时,要把握好用户体验和奖励验证之间的平衡。由于服务器端回调会存在延迟的情况,因此我们建议客户端立即奖励用户,同时在收到服务器端回调时对所有奖励进行验证。这种做法可确保奖励符合发放条件,同时提供良好的用户体验。
  • 对于某些应用而言,奖励是否达到发放条件非常重要,用户可适当接受延迟。这时,推荐做法是等待服务器端回调完成验证,再向用户发放奖励。

校验服务端验证回调

说明

App上架至华为应用市场(AppGallery)时间超过12小时才可以收到回调。

  1. 设置激励广告的奖励配置。

    您在鲸鸿动能媒体服务平台上申请激励视频广告位时选择“媒体管理(点击媒体名)> 新增展示位 > 选择激励视频(点击下一步,进入编辑页面)”,设置奖励类型和奖励数量,并点击“高级设置”,设置服务器端验证的URL。如下图:

  2. (可选)设置自定义数据customData和userId。

    ads为步骤2请求到的广告信息,调用showAd方法来展示广告。

    您在App中展示激励广告之前设置自定义数据customData和userId。示例代码如下所示:

     
      
    1. import { advertising } from '@kit.AdsKit';
    2. import { common } from '@kit.AbilityKit';
    3. @Entry
    4. @Component
    5. struct Index {
    6. private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    7. // 步骤2中请求到的广告内容
    8. private ads: Array<advertising.Advertisement> = [];
    9. private displayOptions: advertising.AdDisplayOptions = {
    10. // 激励广告视频播放是否静音
    11. mute: true,
    12. // 设置自定义数据
    13. customData: 'CUSTOM_DATA',
    14. // 设置自定义数据
    15. userId: '1234567'
    16. };
    17. build() {
    18. Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
    19. Row() {
    20. Button('showAd').onClick(() => {
    21. this.showAd();
    22. }).width('45%')
    23. }
    24. }
    25. }
    26. private showAd() {
    27. let load: advertising.AdLoader = new advertising.AdLoader(this.context);
    28. // 此处ads[0]表示请求到的第一个广告,用户根据实际情况选择
    29. advertising.showAd(this.ads[0], this.displayOptions, this.context);
    30. }
    31. }

    说明

    如果没有设置customData和userId,不影响发放奖励事件上报但是服务端验证的参数中没有这两个字段。如果设置customData和userId,必须在展示广告之前设置并且URLEncode之后,长度不超过1024个字符,否则影响服务端验证。

  3. 获取要验证的内容。

    用户观看完激励广告时,鲸鸿动能平台服务端会把需要验证的参数以及keyId和sign传给媒体提供的URL:https://www.example.com/feedback(即步骤1中配置的验证URL)。请求体样例:

     
      
    1. {
    2. "adId" : "testx9dtjwj8hp",
    3. "data" : "CUSTOM_DATA",
    4. "keyId" : "12345678",
    5. "rewardAmount" : "10",
    6. "rewardName" : "金币",
    7. "sign" : "OA33u6mypnhE4hbmF32N/ibYi1uXt72nDDyYMwjDI6JXVVFKePZYo4F7Fuk2MaG......",
    8. "uniqueId" : "3361626337333932313435313430373438383561376265636130393939313166",
    9. "userId" : "1234567"
    10. }

    服务器端验证回调查询参数说明:

    参数名称

    类型

    是否必选

    描述

    adId

    String

    激励视频广告位ID

    data

    String

    自定义数据字符串

    keyId

    String

    验证回调的密钥

    rewardAmount

    String

    奖励数量

    rewardName

    String

    奖励奖品

    sign

    String

    回调的签名

    uniqueId

    String

    获奖事件生成的十六进制的标识符

    userId

    String

    用户ID

  4. 组装验证参数

    验证内容(除sign、keyId)格式顺序如下:

    adId={adId}&data={data}&rewardAmount={rewardAmount}&rewardName={rewardName}&uniqueId={uniqueId}&userId={userId}

    其中‘{}’里面表示参数的值,且参数顺序不能变。如果参数为null或者空字符串,则URL中不拼接该参数。然后用SHA256计算散列值,得到paramContentData。示例代码如下所示:

     
      
    1. String adId = request.getParameter("adId");
    2. String data = request.getParameter("data");
    3. ...
    4. String userId = request.getParameter("userId");
    5. String param = "adId=" + adId + "&data=" + data + "&rewardAmount=" + rewardAmount + "&rewardName=" + rewardName + "&uniqueId=" + uniqueId + "&userId=" + userId;
    6. String sha256Value = Sha256Util.digest(param);
    7. byte[] paramContentData = sha256Value.getBytes(Charset.forName("UTF-8"));
  5. 获取公钥列表。

    a. 在鲸鸿动能媒体服务平台上查看对应的帐户信息时选择“账户”。

    通过点击上图所示的“获取密钥”按钮弹出如下所示的弹框,获取“开发者ID”和“密钥”。

    b. 您根据应用分发区域不同,需要使用对应站点的接口URL去获取公钥列表,不同站点对应的接口URL如下所示:

    • 中国:https://ppscrowd-drcn.op.hicloud.com/action-lib-track/publickeys

    将body通过密钥进行HMAC-SHA256加密得到签名,替换到Authorization中,并设置“开发者ID”和Authorization到Header中。示例代码如下所示:

     
      
    1. String data = "";
    2. String url = "https://ppscrowd-dre.op.dbankcloud.com/action-lib-track/publickeys";
    3. String authorization = "Digest validTime=\"{0}\", response=\"{1}\"";
    4. // 开发者ID
    5. String userId = "YOUR_PUBLISHER_ID";
    6. // 密钥
    7. String key = "YOUR_KEY";
    8. HttpClient httpclient = HttpClients.createDefault();
    9. HttpGet request = new HttpGet();
    10. try {
    11. // 将body通过密钥进行HMAC-SHA256加密得到签名,替换到Authorization中
    12. String validTime = String.valueOf(System.currentTimeMillis());
    13. String body = validTime + ":/publickeys";
    14. byte[] keyBytes = Base64.decodeBase64(key);
    15. byte[] bodyBytes = body.getBytes(Charsets.UTF_8);
    16. Mac mac = Mac.getInstance("HmacSHA256");
    17. SecretKey secretKey = new SecretKeySpec(keyBytes, "HmacSHA256");
    18. mac.init(secretKey);
    19. byte[] signatureBytes = mac.doFinal(bodyBytes);
    20. String signature = (signatureBytes == null) ? null : Hex.encodeHexString(signatureBytes);
    21. authorization = MessageFormat.format(authorization, validTime, signature);
    22. // 设置开发者ID和Authorization到Header中
    23. request.setURI(new URI(url));
    24. request.setHeader("userId", userId);
    25. request.setHeader("Authorization", authorization);
    26. HttpResponse response = httpclient.execute(request);
    27. data = EntityUtils.toString(response.getEntity());
    28. } catch (Exception e) {
    29. System.out.println(e.getMessage());
    30. }

    返回data消息体(publicKey已匿名化):

     
      
    1. {
    2. "keys": [
    3. {
    4. "keyId":"12345678",
    5. "publicKey":"LS0tLS1*******************************************************"
    6. },
    7. {
    8. "keyId": "22345678",
    9. "publicKey":"LS0tLS1*******************************************************"
    10. }
    11. ]
    12. }

    返回消息结构体:

    参数名称

    类型

    是否必选

    描述

    keys

    List<key>

    返回公钥列表

    • key结构体:

    参数名称

    类型

    是否必选

    描述

    keyId

    String

    密钥ID

    publicKey

    String

    公钥

  6. 执行验证。

    a. 根据keyId从公钥列表中找到对应的base64编码后的publicKey。

    b. 将paramContentData、publicKey、sign和SHA256withRSA数字签名算法的入参,执行验证。

    示例代码如下所示:

     
      
    1. public static boolean verify(byte[] data, String publicKey, String sign, String signatureAlgorithm) {
    2. try {
    3. byte[] keyBytes = base64Decode(publicKey);
    4. X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
    5. KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    6. PublicKey publicK = keyFactory.generatePublic(keySpec);
    7. Signature signature = Signature.getInstance(signatureAlgorithm);
    8. signature.initVerify(publicK);
    9. signature.update(data);
    10. return signature.verify(base64Decode(sign));
    11. } catch (InvalidKeyException | SignatureException | UnsupportedEncodingException | InvalidKeySpecException | NoSuchAlgorithmException e) {
    12. return false;
    13. }
    14. }
    15. private static byte[] base64Decode(String encoded) throws UnsupportedEncodingException {
    16. return Base64.decodeBase64(encoded.getBytes("UTF-8"));
    17. }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2232376.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

easyui +vue v-slot 注意事项

https://www.jeasyui.com/demo-vue/main/index.php?pluginDataGrid&themematerial-teal&dirltr&pitemCheckBox%20Selection&sortasc 接口说明 <template><div><h2>Checkbox Selection</h2><DataGrid :data"data" style&…

unity搭建场景学习

unity搭建场景学习 创建场景创建gameobject创建材质&#xff0c;用于给gameobject上色拖拽材质球上色上色原理设置多个材质方式设置贴图的方式 效果设置光滑度一些预览设置菜单渲染模型与碰撞模型网格渲染参数1. materials(材质)2. lighting(光照)3. reflection probes(反射探针…

软件加密与授权管理:构建安全高效的软件使用体系

“软件加密与授权管理&#xff1a;构建安全高效的软件使用体系”是一个全面且深入的议题&#xff0c;以下是对该议题的详细探讨&#xff1a; 一、软件加密的概念与重要性 软件加密是指为软件添加保护措施&#xff0c;以防止其被盗版或非法复制。这一技术站在软件开发者的角度&a…

【VScode】中文版ChatGPT编程工具-CodeMoss!教程+示例+快捷键

文章目录 1. 多模型选择2. 编辑快捷键3. 历史记录收藏 CodeMoss使用教程1. 安装CodeMoss插件2. 配置AI模型3. 使用快捷键4. 进行代码优化与解释5. 收藏历史记录 总结与展望 在当今快速发展的编程世界中&#xff0c;开发者们面临着越来越多的挑战。如何提高编程效率&#xff0c;…

宝塔Linux面板安装PHP扩展失败报wget: unable to resolve host address ‘download.bt.cn’

一、问题&#xff1a; 当使用宝塔面板安装PHP扩展失败出现如下错误时 Resolving download.bt.cn(download.bt.cn)...failed: Connection timed out. wget: unable toresolve host address download.bt.cn’ 二、解决&#xff1a; 第一步&#xff1a;如下命令执行拿到返回的I…

Scrapy源码解析:DownloadHandlers设计与解析

1、源码解析 代码路径&#xff1a;scrapy/core/downloader/__init__.py 详细代码解析&#xff0c;请看代码注释 """Download handlers for different schemes"""import logging from typing import TYPE_CHECKING, Any, Callable, Dict, Gener…

【C++】对左值引用右值引用的深入理解(右值引用与移动语义)

&#x1f308; 个人主页&#xff1a;谁在夜里看海. &#x1f525; 个人专栏&#xff1a;《C系列》《Linux系列》 ⛰️ 天高地阔&#xff0c;欲往观之。 ​ 目录 前言&#xff1a;对引用的底层理解 一、左值与右值 提问&#xff1a;左值在左&#xff0c;右值在右&#xff1f;…

docker下迁移elasticsearch的问题与解决方案

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 &#x1f38f;&#xff1a;你只管努力&#xff0c;剩下的交给时间 &#x1f3e0; &#xff1a;小破站 docker下迁移elasticsearch的问题与解决方案 数据挂载报错解决权限问题节点故障 直接上图&#x…

1.3 初探OpenCV贡献库

OpenCV贡献库&#xff08;opencv_contrib&#xff09;是OpenCV的一个扩展库&#xff0c;由社区开发&#xff0c;包含更多视觉应用和受专利保护的算法。它提供最新研究算法、扩展功能和社区支持。可以通过pip安装或手动编译。

太空旅游:科技能否让星辰大海变为现实?

内容概要 在这个快速变化的时代&#xff0c;太空旅游成为了一个让人热血沸腾的话题。想象一下&#xff0c;坐在一颗漂浮的太空舱里&#xff0c;手中端着饮料&#xff0c;眺望着无尽的星辰大海&#xff0c;简直就像科幻电影中的情节一样。不过&#xff0c;这不仅仅是一个空洞的…

智能提醒助理系列-jdk8升级到21,springboot2.3升级到3.3【性能篇】

本系列文章记录“智能提醒助理”产品建设历程&#xff0c;记录实践经验、巩固知识点、锻炼总结能力。 本篇介绍技术栈升级后的切换方案以及性能提升。 一、需求出发点 智能提醒小程序 当前使用的是jdk8&#xff0c;springboot2.3,升级到jdk21和springboot3.3 学习新知识的同时…

ROS2入门学习——ROS在机器人中的运行

一、入门级基础平台TurtleBot TurtleBot 是 ROS 中重要且资源丰富的机器人之一&#xff0c;特别适合入门级机器人爱好者提供基础平台。用户可以直接利用其自带的软硬件&#xff0c;专注于应用程序的开发。TurtleBot 随着 ROS 的发展&#xff0c;一直处于开发前沿。 TurtleBot…

cuda、pytorch-gpu安装踩坑!!!

前提&#xff1a;已经安装了acanoda cuda11.6下载 直接搜索cuda11.6 acanoda操作 python版本3.9 conda create -n pytorch python3.9conda activate pytorch安装Pytorch-gpu版本等包 要使用pip安装&#xff0c;cu116cuda11.6版本 pip install torch1.13.1cu116 torchvi…

二分法查找(c基础)

二分法查找一个有序数组中是否有某个数 大家看了可以自己写一下 &#xff08; 要用知识点 数组 while循环 scanf 函数 printf函数 &#xff09; //用二分法查找 #include<stdio.h> int main() {char arr[] { 1,2,3,4,5,6,7,8,9,10 };int sz sizeof(arr) / size…

实现图书管理系统

1. 图书管理系统菜单 如上图给用户选项 1. 管理员 2. 普通用户 2. 实现基本框架 右键点src&#xff0c;选择new&#xff0c;选择Package命名三个包 book operation user 1.先选择book包&#xff0c;new两个类 book bookList 在book类中定义书的基本属性&#xff0c;并重写…

Efficient Cascaded Multiscale Adaptive Network for Image Restoration 论文阅读笔记

Efficient Cascaded Multiscale Adaptive Network for Image Restoration 论文阅读笔记 这是新国立和新加坡管理大学发表在ECCV2024上的一篇image restoration的文章&#xff0c;提出了一个新的网络结构ECMA&#xff0c;从实验结果上看在超分&#xff0c;去噪&#xff0c;去模糊…

不需要复制粘贴,重复内容如何使用Mac快速完成输入

在Mac的日常使用中&#xff0c;必然有着重复内容需要重复输入的需求&#xff0c;但是Mac的剪切板又不具备历史记录的功能&#xff0c;所以只能一次次的复制粘贴&#xff0c;费时费力&#xff0c;那么该如何才能不这么麻烦 快捷短语就是为了解决这一问题而存在的 提前在设置好…

ubuntu20.04 加固方案-设置限制su命令用户组

一、编辑/etc/pam.d/su配置文件 打开终端。 使用文本编辑器&#xff08;如vim&#xff09;编辑/etc/pam.d/su文件。 vim /etc/pam.d/su 二、添加配置参数 在打开的配置文件的中&#xff0c;添加以下参数&#xff1a; auth required pam_wheel.so 创建 wheel 组 并添加用户 …

002 配置YUM国内镜像源

打开XShell 工具&#xff0c;连接Linux 选择上次的连接&#xff0c;直接双击。 具体连接步骤&#xff0c;参考前面的内容001 编辑YUM默认配置文件 /etc/yum.repos.d/CentOS-Base.repo 是YUM的默认配置文件。 修改这个文件&#xff0c;将其中的内容替换成国内的镜像源 输入下…

【工具变量】大数据管理机构改革DID(2007-2023年)

数据简介&#xff1a;数字ZF是指以新一代信息技术为支撑&#xff0c;重塑政务信息化管理架构、业务架构、技术架构的现代化治理模式。随着数字政府的建设&#xff0c;特别是借助大数据等新一代数字技术&#xff0c;极大地提升了政府的治理能力&#xff0c;从而起到辅助监管机构…