OpenHarmony实战开发之分布式账本

289 篇文章 0 订阅
29 篇文章 0 订阅

简介

Demo基于Open Harmony系统使用ETS语言进行编写,本Demo主要通过设备认证、分布式拉起、分布式数据管理等功能来实现。

应用效果

  1. 设备认证,获取同一个局域网内的设备ID,并拉起应用

image.png

  1. 添加数据并在另一台设备显示该数据

image.png

开发步骤

1.新建Openharmony ETS工程

在DevEco Studio中点击File -> New Project ->[Standard]Empty Ability->Next,Language 选择ETS语言,最后点击Finish即创建成功。

image.png

2.界面代码编写
首页界面

image.png

 build() {
 Flex({ direction: FlexDirection.Column}) {
 //发现设备
 Button('发现设备', { type: ButtonType.Normal, stateEffect: true })
 .borderRadius(8)
 .backgroundColor(0x317aff).width(90)
 .onClick(() =>{
 this.fun()
 })
 //设备认证
 Button('authDevice', { type: ButtonType.Normal, stateEffect: true })
 .borderRadius(8)
 .backgroundColor(0x317aff).width(90)
 .onClick(() =>{
 this.authDevice()
 })

 // 拉起应用
 Button('拉起应用', { type: ButtonType.Normal, stateEffect: true })
 .borderRadius(8)
 .backgroundColor(0x317aff).width(90)
 .onClick(() =>{
 this.startAb()
 })

 Stack({
 alignContent:Alignment.TopEnd
 }){
 Text('家庭账本')
 .fontSize(20)
 .fontWeight(FontWeight.Bold)
 .width('100%')
 .margin({left:12})
 .onClick(() =>{
 //          routePage()
 this.fun()
 })
 Image("/picture/add.png")
 .width(40)
 .height(40)
 .align(Alignment.Start)
 .margin({
 right:12
 }).onClick(() =>{
 routePage()
 })

 }
 .width(350)
 .height(60)
 .margin({
 top:10,
 bottom:10
 })
 Flex({
 direction: FlexDirection.Column, alignItems:ItemAlign.Start,
 }){

 Text("2022年12月")
 .fontSize(20)
 .fontColor(Color.White)

 Text("结余")
 .fontSize(20)
 .fontColor(Color.White)
 .margin({
 top:30
 }).align(Alignment.Start)

 Text("总支出0|总收入0")
 .fontSize(16)
 .fontColor(Color.White)
 .margin({
 top:10
 }).align(Alignment.Start)
 }
 .backgroundColor("#665A5A")
 .height(230)
 .layoutWeight(1)
 .padding(10)
 .onClick(() =>{
 routePage()
 })

 Tabs() {
 TabContent() {
 ProjectList()
 }
 .tabBar("项目")

 TabContent() {
 Statistics()
 }
 .tabBar("统计")
 }
 }
 .width('100%')
 .height('100%')
 .padding({
 left:12,right:12
 })

 }

底部TabContent 项目模块


@Component
struct ProjectList {
 remoteDataManager = new RemoteDataManager()
 @State ProjectData: Array<any> = []
 TestData:any[] =  []
 TestKvData: Array<any> = []
 kvManager = null
 kvStore = null
 STORE_ID = 'store_accountbook'
 aboutToAppear(){
 try {
 const config = {
 userInfo: {
 userId: '0',
 userType: 0
 },
 bundleName: 'com.example.accountbookets'
 }
 factory.createKVManager(config).then((manager) => {
 this.kvManager = manager
 let options =
 {
 createIfMissing: true,
 encrypt: false,
 backup: false,
 autoSync: true,
 kvStoreType: 1,
 securityLevel: 3
 }
 this.kvManager.getKVStore(this.STORE_ID, options).then((store) => {
 this.kvStore = store
 this.kvStore.get("key2").then((data) => {
 this.ProjectData = JSON.parse(data)
 })

 }).catch((err) => {
 })

 }).catch((err) => {
 })
 } catch (e) {
 }
 }

 @Builder ProjectItem(image, name, des,time,money) {
 Flex({ direction: FlexDirection.Row,alignItems: ItemAlign.Center }){
 Image($r("app.media.icon1"))
 .width(30)
 .height(30)

 Column() {
 Text(name)
 .fontSize(16)
 .fontColor(Color.Black)
 Text('11:20')
 .fontSize(16)
 .fontColor(Color.Gray)
 }
 .alignItems(HorizontalAlign.Start)
 .margin({left:15})

 Text('HUAWEI')
 .fontSize(12)
 .fontColor(Color.Black)
 .margin({left:20})

 Text(des)
 .fontSize(14)
 .fontColor(Color.Gray)
 .margin({left:15})

 Column() {
 Text('-100')
 .fontSize(16)
 .fontColor(Color.Black)
 Text(time)
 .fontSize(16)
 .fontColor(Color.Gray)
 }
 .alignItems(HorizontalAlign.Start)
 .margin({left:20})
 }
 .width(400)
 .height(50)
 .margin({top:10})

 }

 build() {
 List() {
 ForEach(this.ProjectData, (item) => {
 ListItem() {
 this.ProjectItem(item.image, item.name, item.des,item.time,item.money)
 }
 .onClick(() => {
 })

 }, (item) => JSON.stringify(item)) {

 }

 }
 }
}

底部TabContent 统计模块

@Component
struct Statistics{
 build(){
 Flex({ direction: FlexDirection.Row}){

 Tabs() {
 TabContent() {
 PayList()
 }
 .tabBar("支出分类")

 TabContent() {

 }
 .tabBar("成员分类")
 }
 }
 }
}

统计模块里面的TabContent


@Component
struct PayList {

 private PayData: PayBean[] = initializeOnStartup()
 @Builder PayItem(previewUrl, title, describe) {
 Flex({ direction: FlexDirection.Row,alignItems: ItemAlign.Center }){
 Image(previewUrl)
 .width(30)
 .height(30)

 Text(title)
 .fontSize(16)
 .fontColor(Color.Black)
 .margin({left:8})

 Text('100%')
 .fontSize(12)
 .fontColor(Color.Black)
 .margin({left:8})

 Progress({ value: 20, total: 150, style: ProgressStyle.Linear }).color(Color.Red).value(150).width(200)

 Text('-100')
 .fontSize(14)
 .fontColor(Color.Gray)
 .margin({left:8})
 }
 .width(400)
 .height(50)
 .margin({top:10})

 }

 build() {
 List() {
 ForEach(this.PayData, (item) => {
 ListItem() {
 this.PayItem(item.image, item.name, item.des)
 }
 .onClick(() => {
 console.info("点击我")
 router.push({
 uri: "pages/VideoPlayer",

 })
 })
 }, (item) => JSON.stringify(item)) {

 }
 }
 }
}
2) add.ets页面

image.png

build() {
 Flex({ direction: FlexDirection.Column,alignItems: ItemAlign.Center}) {

 Flex({ direction: FlexDirection.Row,alignItems: ItemAlign.Center})
 {
 Image("/picture/icon_back.png")
 .width(35)
 .height(35)
 .onClick(() =>{
 router.push({
 uri: "pages/index",

 })
 })

 Text("加一笔")
 .fontSize(20)
 .fontWeight(FontWeight.Bold)
 .margin({left:20})
 }.margin({top:10})
 .padding({left:20})
 .height(100)
 .width(500)
 Stack({
 alignContent: Alignment.TopStart
 }){
 Tabs() {
 TabContent() {
 pay({payTime:$strTime,payRemark:$strRemark,payType:$strType})
 }
 .tabBar("支出")

 TabContent() {
 Income()
 }
 .tabBar("收入")
 }
 .height(450)
 }.width(500)
 .height(500)
 Flex({
 direction: FlexDirection.Row,alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center
 }){
 Text("输入金额")
 .fontColor(Color.Black)
 .fontSize(20)
 .margin({
 right:15
 })
 .width(100)
 TextInput({ placeholder: '100', text:this.strMoney })
 .type(InputType.Normal)
 .placeholderColor(Color.Gray)
 .placeholderFont({ size: 20, weight: 2})
 .enterKeyType(EnterKeyType.Search)
 .caretColor(Color.Green)
 .width(250)
 .height(40)
 .borderRadius('20px')
 }
 .width(400)
 .height(50)

 Text('保存')
 .fontColor(Color.White)
 .fontSize(20)
 .margin({
 top:20
 })
 .textAlign(TextAlign.Center)
 .width(380)
 .height(80)
 .backgroundColor("#FE4F16")
 .onClick(() =>{

 TestData.push({image:"/picture/icon1.png",title:'canyin',des:'ceshi',time:'2021',money:'50'})
 if (AppStorage.Get("key1") == null) {
 AppStorage.SetAndLink("key1", TestData)
 this.remoteDataManager.dataChange("key2", JSON.stringify(TestData))
 }else{
 this.TestStorageData = AppStorage.Get("key1")
 //
//            this.TestStorageData.push({image:"/picture/icon1.png",title:'canyin',des:'beizhu',time:'2021',money:'50'})
 //具体代码
 this.TestStorageData.push({image:"/picture/icon1.png",title:this.strType,des:this.strRemark,time:this.strTime,money:this.strMoney})
 AppStorage.SetAndLink("key1", this.TestStorageData)
 let str = JSON.stringify(this.TestStorageData)
 this.TestKvData = JSON.parse(str)
 this.remoteDataManager.dataChange("key2", JSON.stringify(this.TestKvData))
 }
 router.push({
 uri: "pages/index",

 })
 })

 }
 .width('100%')
 .height('100%')
 }

add页面支出模块


@Component
struct pay{
 @Link payTime:string
 @Link payRemark:string
 @Link payType:string
 @State private index:number = 0
 @State strType:string = "canyin"
 @State AccountData: Array<any> = [
 { previewUrl: "/picture/icon1.png", title: "canyin" ,number:0},
 { previewUrl: "/picture/icon2_2.png", title: "gouwu" ,number:1},
 { previewUrl: "/picture/icon3_3.png", title: "jiaotong" ,number:2},
 { previewUrl: "/picture/icon4_4.png", title: "fuwu" ,number:3},
 { previewUrl: "/picture/icon5_5.png", title: "jiaoyu" ,number:4},
 { previewUrl: "/picture/icon6_6.png", title: "yundong" ,number:5},
 { previewUrl: "/picture/icon7_7.png", title: "luxing" ,number:6},
 { previewUrl: "/picture/icon8_8.png", title: "yiliao" ,number:7},
//    { previewUrl: "/picture/icon9_9.png", title: "生活" ,number:9},
//    { previewUrl: "/picture/icon10_10.png", title: "宠物" ,number:10},
 ]

 @Builder ProItem(previewUrl, title,number) {
 Stack() {
 Flex({
 direction: FlexDirection.Column
 }) {
 if (this.index == number) {
 if (number == 0) {
 Image("/picture/icon1.png")
 .width(60)
 .height(60)
 }else if (number == 1) {
 Image("/picture/icon2.png")
 .width(60)
 .height(60)

 }else if (number == 2) {
 Image("/picture/icon3.png")
 .width(60)
 .height(60)

 }else if (number == 3) {
 Image("/picture/icon4.png")
 .width(60)
 .height(60)
 }else if (number == 4) {
 Image("/picture/icon5.png")
 .width(60)
 .height(60)

 }else if (number == 5) {
 Image("/picture/icon6.png")
 .width(60)
 .height(60)

 }else if (number == 6) {
 Image("/picture/icon7.png")
 .width(60)
 .height(60)
 }else if (number == 7) {
 Image("/picture/icon8.png")
 .width(60)
 .height(60)
 }else if (number == 8) {
 Image("/picture/icon9.png")
 .width(60)
 .height(60)
 }else if (number == 9) {
 Image("/picture/icon10.png")
 .width(60)
 .height(60)
 }
 }else{
 if (number == 0) {
 Image("/picture/icon1_1.png")
 .width(60)
 .height(60)
 }else{
 Image(previewUrl)
 .width(60)
 .height(60)
 }

 }
 Column() {
 Text(title)
 .fontSize(16)
 .fontColor(Color.Black)
 }
 .alignItems(HorizontalAlign.Center)
 }

 }
 .height(100)
 .width(100)
 .margin({
 bottom: 16
 })

 }
 build(){
 Flex({direction: FlexDirection.Column}){

 Grid(){
 ForEach(this.AccountData, (item) => {
 GridItem() {
 this.ProItem(item.previewUrl, item.title,item.number)
 }
 .onClick(() => {
 console.info("点击我")
 this.index = item.number
 this.payType = this.AccountData[this.index].title
 })
 }, (item) => JSON.stringify(item)) {

 }
 }
 .rowsTemplate('1fr 1fr')
 .columnsTemplate('1fr 1fr 1fr 1fr')
 .columnsGap(8)
 .rowsGap(8)
 .height(200)
//      Time()
//      Remark()
 // ******************时间**********************
 Flex({
 direction: FlexDirection.Row,alignItems: ItemAlign.Center
 }){
 Text("时间")
 .fontColor(Color.Black)
 .fontSize(20)
 .margin({
 right:15
 })
 .width(70)

 TextInput({ placeholder: '输入收支时间', text: this.payTime })
 .type(InputType.Normal)
 .placeholderColor(Color.Gray)
 .placeholderFont({ size: 20, weight: 2})
 .enterKeyType(EnterKeyType.Search)
 .caretColor(Color.Green)
 .width(300)
 .height(40)
 .borderRadius('20px')
 .backgroundColor(Color.White)
 .onChange((value: string) => {
 this.payTime = value
 })

 }
 .margin({
 top:20,left:15
 })
 .width(200)
 //*******************备注********************
 Flex({
 direction: FlexDirection.Row,alignItems: ItemAlign.Center
 }){
 Text("备注")
 .fontColor(Color.Black)
 .fontSize(20)
 .margin({
 right:15
 })
 .width(70)

 TextInput({ placeholder: '输入说明', text: this.payRemark })
 .type(InputType.Normal)
 .placeholderColor(Color.Gray)
 .placeholderFont({ size: 20, weight: 2})
 .enterKeyType(EnterKeyType.Search)
 .caretColor(Color.Green)
 //        .layoutWeight(8)
 .height(40)
 .width(300)
 .borderRadius('20px')
 .backgroundColor(Color.White)
 .onChange((value: string) => {
 this.payRemark = value
 })
 }
 .margin({
 top:20,left:15
 })
 .width(50)
 .height(50)
 }
 .height('100%')
 .layoutWeight(1)

 }
}

收入模块代码

@Component
struct Income{

 build(){
 Flex({direction: FlexDirection.Column}){
 Time()
 Remark()

 }
 }
}

时间模块


@Component
struct Time{
 @State inputTime:string = ''
 build(){
 Flex({
 direction: FlexDirection.Row,alignItems: ItemAlign.Center
 }){
 Text("时间")
 .fontColor(Color.Black)
 .fontSize(20)
 .margin({
 right:15
 })
 .width(70)

 TextInput({ placeholder: '2021', text: this.inputTime })
 .type(InputType.Normal)
 .placeholderColor(Color.Gray)
 .placeholderFont({ size: 20, weight: 2})
 .enterKeyType(EnterKeyType.Search)
 .caretColor(Color.Green)
 .width(300)
 .height(40)
 .borderRadius('20px')
 .backgroundColor(Color.White)
 }
 .margin({
 top:20,left:15
 })
 .width(200)
 }
}

备注模块

@Component
struct Remark{
 @State inputRemark:string = ''
 build(){
 Flex({
 direction: FlexDirection.Row,alignItems: ItemAlign.Center
 }){
 Text("备注")
 .fontColor(Color.Black)
 .fontSize(20)
 .margin({
 right:15
 })
 .width(70)

 TextInput({ placeholder: 'ceshe', text: this.inputRemark })
 .type(InputType.Normal)
 .placeholderColor(Color.Gray)
 .placeholderFont({ size: 20, weight: 2})
 .enterKeyType(EnterKeyType.Search)
 .caretColor(Color.Green)
//        .layoutWeight(8)
 .height(40)
 .width(300)
 .borderRadius('20px')
 .backgroundColor(Color.White)

 }
 .margin({
 top:20,left:15
 })
 .width(50)
 .height(50)

 }
}
3.设备认证

设备认证是依赖 DeviceManager 组件来实现的,详细代码参考源码RemoteDeviceModel.ets

1.创建DeviceManager实例


 registerDeviceListCallback(callback) {
 if (typeof (this.#deviceManager) === 'undefined') {
 deviceManager.createDeviceManager('com.example.tictactoegame', (error, value) => {
 if (error) return
 this.#deviceManager = value;
 this.registerDeviceListCallback_(callback);
 });
 } else {
 this.registerDeviceListCallback_(callback);
 }
 }

2.查询可信设备列表


 var list = this.#deviceManager.getTrustedDeviceListSync();
 if (typeof (list) != 'undefined' && typeof (list.length) != 'undefined') {
 this.deviceList = list;
 }

3.注册设备上下线监听

this.#deviceManager.on('deviceStateChange', (data) => {
 switch (data.action) {
 case 0:
 this.deviceList[this.deviceList.length] = data.device;
 this.callback();
 if (this.authCallback != null) {
 this.authCallback();
 this.authCallback = null;
 }
 break;
 case 2:
 if (this.deviceList.length > 0) {
 for (var i = 0; i < this.deviceList.length; i++) {
 if (this.deviceList[i].deviceId === data.device.deviceId) {
 this.deviceList[i] = data.device;
 break;
 }
 }
 }
 this.callback();
 break;
 case 1:
 if (this.deviceList.length > 0) {
 var list = [];
 for (var i = 0; i < this.deviceList.length; i++) {
 if (this.deviceList[i].deviceId != data.device.deviceId) {
 list[i] = data.device;
 }
 }
 this.deviceList = list;
 }
 this.callback();
 break;
 default:
 break;
 }
 });

4.设备发现

this.#deviceManager.on('deviceFound', (data) => {
 for (let i = 0; i < this.discoverList.length; i++) {
 if (that.discoverList[i].deviceId === data.device.deviceId) {
 return;
 }
 }
 this.discoverList[this.discoverList.length] = data.device;
 this.callback();
 });

5.设备认证


authDevice(deviceInfo, callback){
 let extraInfo = {
 "targetPkgName": 'com.example.tictactoegame',
 "appName": 'com.example.tictactoegame',
 "appDescription": 'com.example.tictactoegame',
 "business": '0'
 };
 let authParam = {
 "authType": 1,
 "appIcon": '',
 "appThumbnail": '',
 "extraInfo": extraInfo
 };
 this.#deviceManager.authenticateDevice(deviceInfo, authParam, (err, data) => {
 if (err) {
 this.authCallback = null;
 } else {
 this.authCallback = callback;
 }
 });
 }
4.数据管理

分布式数据管理 依赖@ohos.data.distributedData模块实现,详细参考源码RemoteDataManager.ets

1.导入该模块


import factory from '@ohos.data.distributedData';

2.创建KVManager实例,用于管理数据库对象


 registerDataListCallback(callback) {
 let that = this
 if (this.kvManager == null) {
 try {
 const config = {
 userInfo: {
 userId: '0',
 userType: 0
 },
 bundleName: 'com.example.tictactoegame'
 }
 factory.createKVManager(config).then((manager) => {
 that.kvManager = manager
 that.registerDataListCallback_(callback)
 }).catch((err) => {
 })
 } catch (e) {
 }
 } else {
 this.registerDataListCallback_(callback)
 }
 }

3.创建并获取KVStore数据库

registerDataListCallback_(callback) {
 let that = this
 if (that.kvManager == null) {
 callback()
 return
 }
 if (that.kvStore == null) {
 try {
 let options =
 {
 createIfMissing: true,
 encrypt: false,
 backup: false,
 autoSync: true,
 kvStoreType: 1,
 securityLevel: 3
 }
 this.kvManager.getKVStore(this.STORE_ID, options).then((store) => {
 that.kvStore = store
 that._registerDataListCallback_(callback)
 }).catch((err) => {
 })
 } catch (e) {
 }
 } else {
 this._registerDataListCallback_(callback)
 }
 }

4.订阅指定类型的数据变更通知


 _registerDataListCallback_(callback) {
 let that = this
 if (that.kvManager == null) {
 callback()
 return
 }
 this.kvStore.on('dataChange', 1, function(data) {
 if (data) {
 that.arr = data.updateEntries
 callback()
 }
 })
 }

5.添加指定类型键值对到数据库


 startAbilityContinuation(deviceId) {
 let wantValue = {
 bundleName: 'com.example.tictactoegame',
 abilityName: 'com.example.tictactoegame.MainAbility',
 deviceId: deviceId,
 parameters: {
 uri: 'pages/Fight'
 }
 };
 featureAbility.startAbility({ want: wantValue }).then(() => {
 router.replace({ uri: 'pages/Fight' })
 });
 }
5.远程拉起设备app

使用 FeatureAbility 模块的startAbility接口拉起远程设备app


 startAbilityContinuation(deviceId) {
 let wantValue = {
 bundleName: 'com.example.tictactoegame',
 abilityName: 'com.example.tictactoegame.MainAbility',
 deviceId: deviceId,
 parameters: {
 uri: 'pages/Fight'
 }
 };
 featureAbility.startAbility({ want: wantValue }).then(() => {
 router.replace({ uri: 'pages/Fight' })
 });
 }
6.添加数据

新建一个账单数据 添加到分布式数据

this.remoteDataManager.dataChange("key2", JSON.stringify(this.TestKvData))

在另一台设备监听并获取显示该条数据


 private onPageShow() {

 this.remoteDataManager.registerDataListCallback(() => {
 let arr = this.remoteDataManager.arr[0]
 this.strTest = arr.value.value
 this.ProjectData = JSON.parse(this.strTest)

 }

写在最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(OpenHarmony)资料用来跟着学习是非常有必要的。

想更深入的学习 OpenHarmony (鸿蒙南向)全栈开发的内容可点击领取一下学习文档,限时开源,先到先得~无套路领取!!

请点击→《鸿蒙南向开发学习路线》

OpenHarmony入门学习视频

image.png

鸿蒙南向开发实战教学视频

image.png

获取以上完整鸿蒙HarmonyOS学习文档,请点击→《鸿蒙南向开发学习路线》
《OpenHarmony源码解析》:
  • 搭建开发环境
  • Windows 开发环境的搭建
  • Ubuntu 开发环境搭建
  • Linux 与 Windows 之间的文件共享
  • ……
    image.png
系统架构分析:
  • 构建子系统
  • 启动流程
  • 子系统
  • 分布式任务调度子系统
  • 分布式通信子系统
  • 驱动子系统
  • ……
    image.png
OpenHarmony面试题(内含参考答案)

image.png

获取以上完整鸿蒙HarmonyOS学习文档,请点击→《鸿蒙南向开发学习路线》

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值