• HarmonyOS应用开发-首选项与后台通知管理


    首选项

    在移动互联网蓬勃发展的今天,移动应用给我们生活带来了极大的便利,这些便利的本质在于数据的互联互通。因此在应用的开发中数据存储占据了非常重要的位置,HarmonyOS应用开发也不例外。本章以HarmonyOS的首选项为例,介绍了HarmonyOS的数据管理能力。

    什么是首选项

    首选项为应用提供Key-Value键值型的数据存储能力,支持应用持久化轻量级数据,并对其进行增删除改查等。该存储对象中的数据会被缓存在内存中,因此它可以获得更快的存取速度。

    首选项的特点如下:

    1. Key-Value形式存储数据: 数据以键值对的形式存储,其中Key是唯一的关键字,而对应的Value是实际的数据值。

    2. 非关系型数据库: 与关系型数据库不同,首选项不遵循ACID特性(Atomicity, Consistency, Isolation, Durability),且数据之间没有关系。

    3. 进程中的唯一实例: 在一个进程中,每个文件只存在一个首选项实例。应用程序获取到该实例后,可以从中读取数据或将数据存入其中。通过调用flush方法,可以将实例中的数据回写到文件中。

    4. 与关系数据库的区别:

      特点/属性关系数据库首选项
      数据存储形式表格(关系)Key-Value(非关系型)
      ACID特性遵循ACID不遵循ACID
      数据关系数据表之间存在关联关系无数据关系,独立的Key-Value对
      存储引擎使用数据库引擎,如SQLite等存储在文件中
      使用场景复杂场景下的本地数据库管理对Key-Value结构的数据进行简单操作
      数据处理复杂查询、事务处理等简单的存取和持久化操作
      约束和限制连接池大小、同时写操作数等数据条目数量建议、Key类型限制

    首选项常用接口

    接口功能
    put将数据存储到首选项中
    get通过指定的Key获取首选项中的数据值
    has检查首选项中是否包含给定的Key
    delete从首选项中删除指定Key的数据
    flush将首选项中的数据写回到文件中,实现数据持久化

    在使用前需要导入@ohos.data.preferences模块,实例名字命名为dataPreferences,同时定义两个常量PREFERENCES_NAME和KEY_APP_FONT_SIZE。

    // PreferencesUtil.ets
    import dataPreferences from '@ohos.data.preferences';
       ...
      const PREFERENCES_NAME = 'myPreferences'; // 首选项名字
      const KEY_APP_FONT_SIZE = 'appFontSize';  // 首选项Key字段
    
    • 1
    • 2
    • 3
    • 4
    • 5

    需要在entryAbility的onCreate方法获取首选项实例,以便后续能进行保存、读取、删除等操作,获取实例需要上下文context和文件名字PREFERENCES_NAME

    // entryAbility.ets  
      onCreate(want, launchParam) {
        Logger.info(TAG, 'onCreate');
        globalThis.abilityWant = want;
        // 创建首选项
        PreferencesUtil.createFontPreferences(this.context);
        ...
      }
    
    // PreferencesUtil.ets  
      createFontPreferences(context) {
        globalThis.getFontPreferences = (() => {
          // 获取首选项实例
          let preferences: Promise<dataPreferences.Preferences> = dataPreferences.getPreferences(context, PREFERENCES_NAME);
          return preferences;
        });
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    保存数据

    在entryAbility的onCreate方法,调用PreferencesUtil.saveDefaultFontSize保存默认数据,先用has方法判断当前key是否有存在,如果没有就通过put方法把用户数据保存起来,该方法通过key-value键值对方式保存,常量KEY_APP_FONT_SIZE作为key,用户数据fontSize作为value,再通过flush方法把数据保存到文件。

    // entryAbility.ets  
      onCreate(want, launchParam) {
        Logger.info(TAG, 'onCreate');
        globalThis.abilityWant = want;
        ...
        // 设置字体默认大小
        PreferencesUtil.saveDefaultFontSize(Constants.SET_SIZE_STANDARD);
      }
    // PreferencesUtil.ets    
      saveDefaultFontSize(fontSize: number) {
        globalThis.getFontPreferences().then((preferences) => {
          // 判断保存的key是否存在
          preferences.has(KEY_APP_FONT_SIZE).then(async (isExist) => {
            Logger.info(TAG, 'preferences has changeFontSize is ' + isExist);
            if (!isExist) {
              // 保存数据
              await preferences.put(KEY_APP_FONT_SIZE, fontSize);
              preferences.flush();
            }
          }).catch((err) => {
            Logger.error(TAG, 'Has the value failed with err: ' + err);
          });
        }).catch((err) => {
          Logger.error(TAG, 'Get the preferences failed, err: ' + err);
        });
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    获取数据

    在HomePage的onPageShow方法,调用PreferencesUtil.getChangeFontSize方法获取用户数据,调用get方法获取,该方法通过key-value键值对方式读取,常量KEY_APP_FONT_SIZE作为key,默认数据fontSize作为value,把的到的结果赋值给变量fontSize,通过return方式把值返回去。

    // HomePage.ets
      onPageShow() {
        PreferencesUtil.getChangeFontSize().then((value) => {
          this.changeFontSize = value;
          Logger.info(TAG, 'Get the value of changeFontSize: ' + this.changeFontSize);
        });
      }
    // PreferencesUtil.ets 
      async getChangeFontSize() {
        let fontSize: number = 0;
        const preferences = await globalThis.getFontPreferences();
        fontSize = await preferences.get(KEY_APP_FONT_SIZE, fontSize);
        return fontSize;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    是否包含指定的key

    通过has方法判断首选项中是否包含指定的key,保证指定的key不会被重复保存。

    // PreferencesUtil.ets    
      saveDefaultFontSize(fontSize: number) {
        globalThis.getFontPreferences().then((preferences) => {
          // 判断保存的key是否存在
          preferences.has(KEY_APP_FONT_SIZE).then(async (isExist) => {
            Logger.info(TAG, 'preferences has changeFontSize is ' + isExist);
          }).catch((err) => {
            Logger.error(TAG, 'Has the value failed with err: ' + err);
          });
        }).catch((err) => {
          Logger.error(TAG, 'Get the preferences failed, err: ' + err);
        });
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    数据持久化

    通过flush方法把应用数据保存到文件中,使得应用数据保存期限变长

    // PreferencesUtil.ets 
      saveChangeFontSize(fontSize: number) {
        globalThis.getFontPreferences().then(async (preferences) => {
          // 保存数据
          await preferences.put(KEY_APP_FONT_SIZE, fontSize);
         // 数据持久化
          preferences.flush();
        }).catch((err) => {
          Logger.error(TAG, 'put the preferences failed, err: ' + err);
        });
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    删除数据

    删除首选项数据需要获取preferences实例,用delete方法删除指定的key所对应的值,常量KEY_APP_FONT_SIZE作为key,通过Promise异步回调是否删除成功。

    // PreferencesUtil.ets 
      async deleteChangeFontSize() {
        const preferences: dataPreferences.Preferences = await globalThis.getFontPreferences();
        // 删除数据
        let deleteValue = preferences.delete(KEY_APP_FONT_SIZE);
        deleteValue.then(() => {
          Logger.info(TAG, 'Succeeded in deleting the key appFontSize.');
        }).catch((err) => {
          Logger.error(TAG, 'Failed to delete the key appFontSize. Cause: ' + err);
        });
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    后台通知管理

    通知的作用

    通知旨在让用户以合适的方式及时获得有用的新消息,帮助用户高效地处理任务。应用可以通过通知接口发送通知消息,用户可以通过通知栏查看通知内容,也可以点击通知来打开应用,通知主要有以下使用场景:

    • 显示接收到的短消息、即时消息等。
    • 显示应用的推送消息,如广告、版本更新等。
    • 显示当前正在进行的事件,如下载等。

    通知会在不同场景以不同形式提示用户,例如通知在状态栏上显示为图标、在通知栏上会显示通知详细信息。重要的信息还可以使用横幅通知,浮动在界面顶部显示。

    创建通知

    本节将介绍几种常见类型通知的创建,在创建通知前需要先导入notificationManager模块,该模块提供通知管理的能力,包括发布、取消发布通知,创建、获取、移除通知通道等能力。

    import notification from '@ohos.notificationManager';
    
    • 1

    基础类型通知

    基础类型通知主要应用于发送短信息提示信息广告推送等,支持普通文本类型、长文本类型、多行文本类型和图片类型,可以通过contentType指定通知的内容类型。

    发布普通文本类型通知,需要设置contentType类型为ContentType.NOTIFICATION_CONTENT_BASIC_TEXT

    import notification from '@ohos.notificationManager';
    
    @Entry
    @Component
    struct NotificationDemo {
      publishNotification() {
        let notificationRequest: notification.NotificationRequest = { // 描述通知的请求
          id: 1, // 通知ID
          slotType: notification.SlotType.SERVICE_INFORMATION,
          content: { // 通知内容
            contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT, // 普通文本类型通知
            normal: { // 基本类型通知内容
              title: '通知内容标题',
              text: '通知内容详情',
              additionalText: '通知附加内容', // 通知附加内容,是对通知内容的补充。
            }
          }
        }
        notification.publish(notificationRequest).then(() => { // 发布通知
          console.info('publish success');
        }).catch((err) => {
          console.error(`publish failed, dcode:${err.code}, message:${err.message}`);
        });
      }
    
      build() {
        Column() {
          Button('发送通知')
            .onClick(() => {
              this.publishNotification()
            })
        }
        .width('100%')
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    参数名作用
    notificationRequest描述通知的请求对象,包含通知的各种信息。
    id通知的唯一标识符,用于区分不同的通知。
    slotType通知的槽类型,指定通知在通知栏的位置。
    content包含通知内容的对象,指定通知的类型和具体内容。
    contentType指定通知的内容类型,例如普通文本、长文本、图片等。
    normal包含普通文本类型通知的具体内容信息。
    title普通文本类型通知的标题。
    text普通文本类型通知的详细内容。
    additionalText普通文本类型通知的附加内容,对通知内容的补充说明。
    publishnotification.publish方法,用于发布通知。
    thenPromise的成功回调,用于在通知发布成功后执行的操作。
    catchPromise的失败回调,用于在通知发布失败时处理错误情况。
    onClick按钮的点击事件处理函数,用于在按钮点击时触发通知发布操作。

    发布图片类型通知

    发布普通文本类型通知,需要设置contentType类型为ContentType.NOTIFICATION_CONTENT_PICTURE

    import notification from '@ohos.notificationManager';
    import image from '@ohos.multimedia.image';
    
    @Entry
    @Component
    struct NotificationTest1 {
      async publishPictureNotification() {
        // 将资源图片转化为PixelMap对象
        let resourceManager = getContext(this).resourceManager;
        let imageArray = await resourceManager.getMediaContent($r('app.media.bigPicture').id);
        let imageResource = image.createImageSource(imageArray.buffer);
        let pixelMap = await imageResource.createPixelMap();
    
        let notificationRequest: notification.NotificationRequest = { // 描述通知的请求
          id: 1,
          content: {
            contentType: notification.ContentType.NOTIFICATION_CONTENT_PICTURE,
            picture: {
              title: '好物热销中', // 通知内容标题
              text: '展开查看详情', // 通知内容
              expandedTitle: '今日热门推荐', // 通知展开时的内容标题
              briefText: '这里一定有您喜欢的', // 通知概要内容,是对通知内容的总结
              picture: pixelMap // 通知的图片内容
            }
          }
        }
    
        notification.publish(notificationRequest).then(() => { // 发布通知
          console.info('publish success');
        }).catch((err) => {
          console.error(`publish failed, dcode:${err.code}, message:${err.message}`);
        });
      }
    
      build() {
        Column() {
          Button('发送大图通知')
            .onClick(() => {
              this.publishPictureNotification()
            })
        }
        .width('100%')
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    参数名作用
    notificationRequest描述通知的请求对象,包含通知的各种信息。
    id通知的唯一标识符,用于区分不同的通知。
    content包含通知内容的对象,指定通知的类型和具体内容。
    contentType指定通知的内容类型,例如图片、普通文本、长文本等。
    picture包含图片类型通知的具体内容信息。
    title图片类型通知的标题。
    text图片类型通知的详细内容。
    expandedTitle图片类型通知展开时的内容标题。
    briefText图片类型通知的概要内容,对通知内容的总结。
    picture图片类型通知的图片内容。
    pixelMap通知的图片内容,通过像素图 (pixelMap) 表示。
    publishnotification.publish 方法,用于发布通知。
    thenPromise 的成功回调,用于在通知发布成功后执行的操作。
    catchPromise 的失败回调,用于在通知发布失败时处理错误情况。
    onClick按钮的点击事件处理函数,用于在按钮点击时触发通知发布操作。

    进度类型通知

    进度条通知也是常见的通知类型,主要应用于文件下载事务处理进度显示。目前系统模板仅支持进度条模板。

    • 在发布进度类型通知前需要查询系统是否支持进度条模板

      notification.isSupportTemplate('downloadTemplate').then((data) => {
        console.info(`[ANS] isSupportTemplate success`);
        let isSupportTpl: boolean = data; // isSupportTpl的值为true表示支持支持downloadTemplate模板类通知,false表示不支持
        // ...
      }).catch((err) => {
        console.error(`[ANS] isSupportTemplate failed, error[${err}]`);
      });
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    • 构造进度条模板,name字段当前需要固定配置为downloadTemplate。

      let template = {
       name: 'downloadTemplate',
       data: {
         progressValue: 60, // 当前进度值
         progressMaxValue: 100 // 最大进度值
        }
      }
      
      let notificationRequest = {
       id: 1,
       content: {
         contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
         normal: {
           title: '文件下载:music.mp4',
           text: 'senTemplate',
           additionalText: '60%'
         }
       },
        template: template  
      }
      // 发布通知
      notification.publish(notificationRequest).then(() => {
       console.info(`publish success`);
      }).catch(error => {
       console.error(`[ANS] publish failed, code is ${error.code}, message is ${error.message}`);
      })
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26

    更新通知

    在发出通知后,使用您之前使用的相同通知ID,再次调用notification.publish来实现通知的更新。如果之前的通知是关闭的,将会创建新通知。

    移除通知

    // 通过通知ID取消已发布的通知
    notification.cancel(notificationId)
    // 取消所有已发布的通知
    notification.cancelAll()
    
    • 1
    • 2
    • 3
    • 4

    通知通道

    通过通知通道,您可让通知有不同的表现形式,比如社交类型的通知是横幅显示的,并且有提示音,而一般的通知则不会横幅显示,您可以使用slotType来实现。

    通知通道类型显示通知图标显示横幅提示音适用场景
    SlotType.SOCIAL_COMMUNICATION社交应用,如消息、社交媒体等,需要用户及时关注和响应的场景。
    SlotType.SERVICE_INFORMATION服务性应用,如后台服务状态、系统信息等,不需要过于显眼的提示。
    SlotType.CONTENT_INFORMATION内容相关的应用,如新闻、文章更新等,用户更关心通知的内容而非及时响应。
    SlotType.OTHER_TYPES不需要在状态栏中显示图标,通知内容不需要引起用户关注的场景。

    后台代理提醒

    本节讲述了在HarmonyOS应用中使用后台代理提醒(reminderAgentManager)来添加、发布、删除和修改提醒的基本操作。

    后台代理提醒主要有以下几种类型:

    • 倒计时
    • 日历
    • 闹钟

    添加使用权限

    "module": {
      ...
      "requestPermissions": [
        {
          "name": "ohos.permission.PUBLISH_AGENT_REMINDER"
        }
      ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在应用的 module 配置中添加了后台代理提醒的使用权限。

    导入 reminderAgent 模块

    import reminderAgent from '@ohos.reminderAgentManager';
    
    • 1

    通过 @ohos.reminderAgentManager 模块导入后台代理提醒模块,并将其命名为 reminderAgent

    新增提醒

    export class ReminderService {
      public addReminder(alarmItem: ReminderItem, callback?: (reminderId: number) => void) {
        let reminder = this.initReminder(alarmItem);
        reminderAgent.publishReminder(reminder, (err, reminderId) => {
          if (callback != null) {
            callback(reminderId);
          }
        });
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    使用 reminderAgent.publishReminder 方法发布新的提醒。提醒的具体信息由 ReminderRequestAlarm 类型定义。

    删除提醒

    export class ReminderService {
      public deleteReminder(reminderId: number) {
        reminderAgent.cancelReminder(reminderId);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用 reminderAgent.cancelReminder 方法删除指定 reminderId 的提醒。

    修改提醒

    public async setAlarmRemind(alarmItem: AlarmItem) {
      let index = await this.findAlarmWithId(alarmItem.id);
      if (index !== Constants.DEFAULT_NUMBER_NEGATIVE) {
        this.reminderService.deleteReminder(alarmItem.id);
      } else {
        // 处理新增提醒的逻辑
      }
    
      this.reminderService.addReminder(alarmItem, (newId) => {
        alarmItem.id = newId;
        // 处理新提醒的逻辑
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在修改提醒时,首先调用 deleteReminder 方法删除旧提醒,然后调用 addReminder 方法添加新提醒。

  • 相关阅读:
    C++继承
    k8s 部署RocketMQ主从
    HTTP和HTTPS详解
    【JavaScript】三大元素系列讲解
    ButterKnife依赖注入框架源码解析
    【Vue+Element-UI】实现登陆注册界面及axios之get、post请求登录功能实现、跨域问题的解决
    章文嵩:开源为我打开一扇窗
    枚举和注解05:注解
    【ISO14229_UDS刷写】-1-$34诊断服务RequestDownload理论部分
    【HJ53 杨辉三角的变形】
  • 原文地址:https://blog.csdn.net/weixin_41908433/article/details/134340770