首选项

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

什么是首选项

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

首选项的特点如下:

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

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

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

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

首选项常用接口

// PreferencesUtil.ets
import dataPreferences from '@ohos.data.preferences';
   ...
  const PREFERENCES_NAME = 'myPreferences'; // 首选项名字
  const KEY_APP_FONT_SIZE = 'appFontSize';  // 首选项Key字段
// 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;
    });
  }

保存数据

在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);
    });
  }

获取数据

在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;
  }

是否包含指定的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);
    });
  }

数据持久化

通过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);
    });
  }

删除数据

删除首选项数据需要获取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);
    });
  }

后台通知管理

通知的作用

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

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

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

创建通知

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

import notification from '@ohos.notificationManager';

基础类型通知

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

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%')
  }
}

发布图片类型通知

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%')
  }
}

进度类型通知

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

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

    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}]`);
    });
    
  • 构造进度条模板,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}`);
    })
    

更新通知

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

移除通知

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

通知通道

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

后台代理提醒

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

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

  • 倒计时
  • 日历
  • 闹钟

添加使用权限

"module": {
  ...
  "requestPermissions": [
    {
      "name": "ohos.permission.PUBLISH_AGENT_REMINDER"
    }
  ]
}

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

导入 reminderAgent 模块

import reminderAgent from '@ohos.reminderAgentManager';

通过 @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);
      }
    });
  }
}

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

删除提醒

export class ReminderService {
  public deleteReminder(reminderId: number) {
    reminderAgent.cancelReminder(reminderId);
  }
}

使用 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;
    // 处理新提醒的逻辑
  })
}

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

11-11 06:55