HarmonyOS 网络请求以及数据持久化

网络管理开发

应用的开发离不开网络请求,因为我们不可能把所有数据都存储在本地,因此应用需要跟数据进行远程交互。而HarmonyOS也为开发者提供了网络管理模块,分别提供了以下功能:

  • HTTP数据请求:通过HTTP发起一个数据请求
  • WebSocket连接:使用WebSocket建立服务器与客户端的双向连接
  • Socket:通过Socket进行数据传输

本文主要讲一下http请求的使用,其它具体请查阅官方文档

1 HTTP数据请求

1.1 HarmonyOS内置HTTP模块
1.1.1 使用示例
  • 导入http模块
import http from "@oos.net.http"
  • 使用http模块发送请求,处理响应
// 创建一个http的请求对象,不可复用
const httpRequest = http.createHttp()
// httpRequest.request(url, HttpRequestOptions)
// 发起网络请求
httpRequest.request(
    "http://localhost:8080/users", // 请求URL路径
    {
        method: http.RequestMethod.GET,
        extraData: {"param1": value1} // k1=v1&k2=v2
    }
)
// 处理响应结果
.then((resp: http.HttpResponse) => {
    if (resp.responseCode == 200) {
        // 请求成功
    }
})
.catch((err: Error) => {
    // 请求失败
})

参数HttpRequestOptions具体参数属性

名称类型描述
methodRequestMethod请求方式,GET、POST、PUT、DELETE等
extraDatastring|Object请求参数
headerObject请求头字段
connectTimeoutnumber连接超时时间,单位毫秒,默认是60000ms
readTimeoutnumber读取超时间,同上

请求返回结果HttpResponse具体属性值

名称类型描述
responseCodeResponseCode响应状态码
headerObject响应头
cookiesstring响应返回的cookies
resultstring|Object响应体,默认是JSON字符串
resultTypeHttpDataType返回值类型
1.2 第三方库:axios

因为axios属于第三方库,需要使用到HarmonyOS提供的第三方包管理工具:ohpm,具体查看官方文档

  • 步骤1:下载和安装ohpm

    • 下载ohpm工具包,下载链接
    • 解压工具包,执行初始化命令
    # windows 环境
    init.bat
    # Linux或Mac环境
    ./init.sh
    
    • 将ohpm配置到环境变量
    # windows 环境,直接在我的电脑配置即可
    直接在系统变量的Path下加入你的ohpm安装路径即可,
    如:D:\software\Huawei\command-line-tools\ohpm\bin
    # Linux或Mac环境,其中OHPM的路径请替换为ohpm的安装路径
    export OHPM_HOME=/XX/ohpm
    export PATH=${OHPM_HOME}/bin:${PATH}
    
  • 步骤2:下载和安装axios

    • 下载axios
    # 进入项目目录,然后使用命令行
    ohpm install @ohos/axios
    

    在这里插入图片描述

    • 开放网络权限(涉及网络请求的都需要开发该权限)
    {
        "module": {
            "requestPermission": [
                {
                    "name": "ohos.permission.INTERNET"
                }
            ]
        }
    }
    
  • 步骤3:使用axios (前端同学最熟悉了)

下面简单介绍一下axios的使用

导入axios

import axios from "@ohos/axios"

发送请求,并处理响应

axios.get(
  'url',
  {
    params: { param1: 'value1' },
    data: { param1: 'value1' }
  }
)
  .then(response => {
    if (response.status !== 200) {
      console.log('查询失败')
      return false
    }
    console.log('查询成功')
  })
  .catch(err => {
    console.log('查询失败', JSON.stringify(err))
  })

2 数据持久化

2.1 用户首选项
  • 导入首选项模块
import dataPreference from '@ohos.data.preferences'
  • 获取首选项实例,读取指定文件
dataPreference.getPreferences(this.context, 'MyAppPreferences')
  .then(peferences => {
    // 获取成功
  })
  .catch(reason => {
    // 获取失败
  })
  • 数据操作
// 写入数据,如果已经存在则会覆盖,可利用.has()判断是否存在
preferences.put('key', val)
  .then(() => preferences.flush()) // 刷到磁盘
  .catch(reason =>{}) // 处理异常

// 删除数据
preferences.delete('key')
  .then(() => {}).catch(reason => {})

// 查询数据
preferences.get('key', 'defaultValue')
  .then(value => console.log('查询成功'))
  .catch(reason => console.log('查询失败'))

说明:

  • Key为string类型,要求非空且长度不超过80字节
  • Value可以是string、number、boolean及以上类型数组,大小不超过8192字节
  • 数据量建议不超过一万条

使用示例:

  • 创建preferences工具类:PreferencesUtil.ts
import preferences from '@ohos.data.preferences';

class PreferencesUtil {
  // 存储preferences
  prefs: Map<string, preferences.Preferences> = new Map()
  // 初始化preferences
  async initPreferences(context, name: string) {
    try {
      const pref = await preferences.getPreferences(context, name)
      this.prefs.set(name, pref)
      console.log('testTag', `preferences-${name} 初始化成功`)
    } catch (e) {
      console.log('testTag', `preferences-${name} 初始化失败:`, JSON.stringify(e))
    }
  }
  // 写入数据
  async putPreferences(name: string, key: string, value: preferences.ValueType) {
    if (!this.prefs.has(name)) {
      console.log(`testTag`, `preferences-${name} 不存在`)
      return false
    }

    try {
      const pref = this.prefs.get(name)
      // 写入数据
      await pref.put(key, value)
      // 刷入磁盘
      await pref.flush()
      console.log('testTag', `preferences-${name} 数据写入成功`)
    } catch (e) {
      console.log('testTag', `preferences-${name} 数据写入失败:`, JSON.stringify(e))
    }
  }
  // 查询数据
  async getPreferences(name: string, key: string, defaultValue: preferences.ValueType) {
    if (!this.prefs.has(name)) {
      console.log(`testTag`, `preferences-${name} 不存在`)
      return false
    }
    try {
      const pref = this.prefs.get(name)
      // 查询数据
      const value = await pref.get(key, defaultValue)
      console.log('testTag', `preferences-${name} 数据查询成功:`, value)
      return value
    } catch (e) {
      console.log('testTag', `preferences-${name} 数据查询失败:`, JSON.stringify(e))
      return ''
    }
  }
  // 删除数据
  async delPreferences(name: string, key:string) {
    if (!this.prefs.has(name)) {
      console.log(`testTag`, `preferences-${name} 不存在`)
      return false
    }
    try {
      const pref = this.prefs.get(name)
      // 查询数据
      await pref.delete(key)
      console.log('testTag', `preferences-${name} 数据删除成功`)
    } catch (e) {
      console.log('testTag', `preferences-${name} 数据删除失败:`, JSON.stringify(e))
      return ''
    }
  }
  // 删除所有
  async delAllPreferences(name: string | Array<string>, key:string) {
    let nameList = []
    if (name.length) {
      // 数组
      nameList = [...name]
    } else {
      nameList = [name]
    }
    const hasTag = nameList.filter(item => this.prefs.has(item))
    if (!hasTag || (hasTag && hasTag.length === 0)) {
      console.log(`testTag`, `preferences-${name} 不存在`)
      return false
    }

    try {
      nameList.forEach((item, index) => {
        const pref = this.prefs.get(item)
        pref.clear()
        console.log('testTag', `preferences-${index}_${name} 全部删除成功`)
      })
    } catch (e) {
      console.log('testTag', `preferences-${nameList} 删除失败:`, JSON.stringify(e))
      return ''
    }
  }
}

const putils = new PreferencesUtil()

export default putils as PreferencesUtil
  • 入口文件初始化preferences工具类

在入口文件EntryAbility.ts下添加如下代码

async onCreate(want, launchParam) {
    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
    // 全局初始化
    await PreferencesUtil.initPreferences(this.context, 'MyAppPreferences');
    // 如果有多个可以继续初始化
    // await PreferencesUtil.initPreferences(this.context, 'MyOrderPreferences');
}
  • 使用例子,这里用的是父子组件,一般都是全局跨组件的才会使用持久化数据,建议使用@Provide/@Consume传递参数值,这里就简单操作了,使用@Prop
//Index组件(父组件)
import PreferencesUtil from '../utils/PreferencesUtil'
@Entry
@Component
struct Index {
  @State fontSize: number = 16

  build() {
    Row() {
      Column() {
        DemoBuilder({ fontSize: this.fontSize })

        Row() {
          Button("字体20")
            .width(100)
            .height(40)
            .fontSize(18)
            .fontColor(Color.White)
            .backgroundColor(Color.Blue)
            .margin(10)
            .onClick(async () => {
              this.fontSize = 22
              await PreferencesUtil.putPreferences("MyAppPreferences", "fontSize", 22)
              console.log('添加成功')
            })
          Button("字体30")
            .width(100)
            .height(40)
            .fontSize(18)
            .fontColor(Color.White)
            .backgroundColor(Color.Blue)
            .margin(10)
            .onClick(async () => {
              this.fontSize = 30
              await PreferencesUtil.putPreferences("MyAppPreferences", "fontSize", 30)
              console.log('添加成功30')
            })
        }.margin({top: 30})
      }

    }
    .height('100%')
  }
}
// 子组件
import PreferencesUtil from '../../utils/PreferencesUtil'
@Component
export struct DemoBuilder {
  @Prop fontSize: number
  // 自定义构建函数
  @Builder showRepeatCode() {
    Text("重复UI调用")
      .fontColor(Color.Red)
      .fontSize(this.fontSize)
  }

  async aboutToAppear() {
    this.fontSize = await PreferencesUtil.getPreferences("MyAppPreferences","fontSize",16) as number
  }

  build() {
    Column() {
      this.showRepeatCode()
      Text("测试字体大小")
        .fontColor(Color.Blue)
        .fontSize(this.fontSize)
        .margin({top: 30})
      // myGlobalBuilder()
    }
  }
}

在这里插入图片描述

2.2 关系型数据库

关系型数据库(RDB)是基于SQLite组件提供的本地数据库,用于管理应用中的结构化数据。例如:记账本、备忘录

在这里插入图片描述

  • 步骤1:初始化数据库

    • 导入关系型数据库模块
    import relationalStore from '@ohos.data.relationalStore'
    
    • 初始化数据库表
    // rdb配置
    const config = {
      name: 'MyApplication.db', // 数据库文件名
      securityLevel: relationalStore.SecurityLevel.S1 // 数据库安全级别
    }
    // 初始化表的SQL
    const sql = `CREATE TABLE IF NOT EXISTS TASK (
                  ID INTEGER PRIMARY KEY,
                  NAME TEXT NOT NULL,
                  FINISHED bit
                )`
    
    // 获取rdb
    relationalStore.getRdbStore(this.context, config, (err, rdbStore) => {
      // 执行sql,后续的所有增删改查都是使用rdbStore对象
      rdbStore.executeSql(sql)
    })
    
  • 步骤2:增、删、改、查数据

    • 新增数据
    // 准备数据
    const task = { id: 1, name: '测试1', finished: false }
    // 新增
    this.rdbStore.insert(this.tableName, task)
    
    • 修改
    // 要更新的数据
    const task = { finished: true }
    // 查询条件,RdbPredicates就是条件谓词
    const predicates = new relationalStore.RdbPredicates(this.tableName)
    predicates.equalTo('ID', id)
    // 执行更新
    this.rdbStore.update(task, predicates)
    
    • 删除
    // 查询条件
    const predicates = new relationalStore.RdbPredicates(this.tableName)
    predicates.equalTo('ID', id)
    // 执行删除
    this.rbdStore.delete(predicates)
    
    • 查询数据
    // 查询条件
    const predicates = new relationalStore.RdbPredicates(this.tableName)
    // 执行查询 ['ID', 'NAME', 'FINISHED'] => 需要查询的字段
    const result = await this.rdbStore.query(predicates, ['ID', 'NAME', 'FINISHED'])
    
    // 解析结果 (接触过数据库都知道,查询数据一般返回的是一个结果集)
    // 准备数据保存结果
    let tasks: any[] = []
    // 循环遍历结果集,判断是否遍历到最后一行
    while(!result.isAtLastRow) {
      // 指针移动到下一行数据
      result.goToNextRow()
      // 根据字段名获取字段index,从而获取字段值
      const id = result.getLong(result.getColumnIndex("ID"))
      const name = result.getString(result.getColumnIndex("NAME"))
      
      tasks.push({id, name})
    }
    

关系数据库使用示例:

// 创建TaskModel.ets
/**
* 这里为什么创建.ets文件?
* 原因:如果是创建ts文件,那么就不能使用ArkTs的状态管理,所需要创建为.ets文件
* 另外,项目初始化时,如果入口文件EntryAbility文件如果是.ts,则需要改为.ets文件** ,因为.ts文件不能导入.ets文件
*/
import relationalStore from '@ohos.data.relationalStore';

/**
 * 说明:如果是跨组件使用TaskModel,那么需要加上@Observed进行属性观察,
 * 本例子所有代码都在当前组件写了,所以就不添加@Observed了
 */
// @Observed
class TaskModel {
  // 存储rdbStore
  private rdbStore: relationalStore.RdbStore
  // 表名
  private tableName: string = 'TASK' // 这里写死了,后续优化可以抽取为参数传值
  /**
   * 初始化数据表
   * @param context
   */
  async initTaskModel(context) {
    // rdb 配置
    const config = {
      name: 'MyApplication.db',
      securityLevel: relationalStore.SecurityLevel.S1
    }
    // 初始化数据表
    const sql = `CREATE TABLE IF NOT EXISTS TASK (
                  ID INTEGER PRIMARY KEY,
                  NAME TEXT NOT NULL,
                  FINISHED bit
                )`
    try {
      // 获取rdb
      const rdbStore = await relationalStore.getRdbStore(context, config)
      // 执行SQL
      rdbStore.executeSql(sql)
      this.rdbStore = rdbStore
    } catch (e) {
      console.log('testTag', '获取rdbStore失败', JSON.stringify(e))
    }
  }

  /**
   * 查询task列表数据
   * 注:这里后续有需要可以抽取成公用方法,有点麻烦,先简单写,嘿嘿
   */
  async getTaskList() {
    // 查询条件
    const predicates = new relationalStore.RdbPredicates(this.tableName)
    // 执行查询
    const result = await this.rdbStore.query(predicates, ['ID', 'NAME', 'FINISHED'])

    // 解析结果 (接触过数据库都知道,查询数据一般返回的是一个结果集)
    // 准备数据保存结果
    let tasks: any[] = []
    // 循环遍历结果集,判断是否遍历到最后一行
    while(!result.isAtLastRow) {
      // 指针移动到下一行数据
      result.goToNextRow()
      // 根据字段名获取字段index,从而获取字段值
      const id = result.getLong(result.getColumnIndex("ID"))
      const name = result.getString(result.getColumnIndex("NAME"))
      const finished = result.getLong(result.getColumnIndex("FINISHED"))

      tasks.push({id, name, finished})
    }

    console.log('testTag', '数据查询成功', JSON.stringify(tasks))
    return tasks
  }

  /**
   * 删除任务
   * @param id
   * @returns
   */
  delTask(id: number): Promise<number> {
    // 查询条件
    const predicates = new relationalStore.RdbPredicates(this.tableName)
    predicates.equalTo('ID', id)
    // 执行删除
    return this.rdbStore.delete(predicates)
  }

  /**
   * 更新任务列表
   * @param id
   * @param finished
   * @returns
   */
  updateTask(id: number, finished: boolean): Promise<number> {
    // 更新的数据
    const data = { finished }
    // 查询条件
    const predicates = new relationalStore.RdbPredicates(this.tableName)
    predicates.equalTo('ID', id)
    // 执行删除
    return this.rdbStore.update(data, predicates)
  }
  /**
   * 添加任务
   * @param name
   * @param finished
   * @returns
   */
  addTask(name: string, finished: boolean): Promise<number> {
    // 新增
    return this.rdbStore.insert(this.tableName, { name, finished })
  }
}

const model = new TaskModel()
export default model as TaskModel

初始化TaskModel

// EntryAbility.ets文件加入如下代码
async onCreate(want, launchParam) {
    ...
    // 初始化数据表
    await TaskModel.initTaskModel(this.context)
}

使用TaskModel

import TaskModel from '../model/TaskModel'

@Entry
@Component
struct Index {
  @State taskName: string = ''
  @State taskList: any[] = []

  async aboutToAppear() {
    this.handleRefreshList()
  }

  async handleRefreshList() {
    this.taskList = await TaskModel.getTaskList()
  }

  @Builder delButton($$: { id: number }) {
    Button('删除')
      .fontSize(18)
      .fontColor(Color.White)
      .backgroundColor(Color.Red)
      .margin({ left: 15 })
      .onClick(async () => {
        await TaskModel.delTask($$.id)
        this.handleRefreshList()
      })
  }

  build() {
    Row() {
      Column() {
        Row() {
          TextInput({ placeholder: '请输入任务名称', text: this.taskName })
            .fontSize(20)
            .width(300)
            .onChange(val => {
              this.taskName = val
            })
          Button("添加")
            .height(40)
            .fontSize(20)
            .fontColor(Color.White)
            .backgroundColor(Color.Blue)
            .layoutWeight(1)
            .margin({left: 10})
            .onClick(async () => {
              await TaskModel.addTask(this.taskName, false)
              this.handleRefreshList()
              // 清空输入框
              this.taskName = ''
            })
        }.margin({bottom: 20})
        .justifyContent(FlexAlign.SpaceBetween)

        // 渲染列表
        List({ space: 10 }) {
          ForEach(this.taskList, (item, index) => {
            ListItem() {
              Row() {
                Text(item.name)
                  .fontSize(24)
                  .fontColor("#ff6602")
                  .fontWeight(FontWeight.Bold)
                Checkbox()
                  .select(!!item.finished)
                  .selectedColor("#ff6602")
                  .onChange(async (value) => {
                    await TaskModel.updateTask(item.id, value)
                    // 这里可以重新请求一次查询数据,也可以不加
                  })
              }
              .width("90%")
              .borderWidth(1)
              .borderColor("#dddddd")
              .padding(10)
              .justifyContent(FlexAlign.SpaceBetween)
            }
            .swipeAction({
              end: this.delButton({ id: item.id })
            })
          })
        }
        .alignListItem(ListItemAlign.Center)
      }
      .justifyContent(FlexAlign.Start)
      .height("100%")
    }
    .height('100%')
  }
}

效果图:

在这里插入图片描述

总结

本文主要是对HarmonyOS的HTTP网络请求做了简单的了解,以及对数据持久化的使用示例,如有不足之处,请指正,谢谢查阅!!

  • 34
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值