HarmonyOS实战项目:创建一个分布式新闻阅读客户端

news/2025/11/26 8:56:30/文章来源:https://www.cnblogs.com/yangykaifa/p/19270965

概述:分布式新闻客户端的核心价值

分布式新闻阅读客户端是HarmonyOS分布式能力的典型应用场景,它实现了一次开发,多端部署的核心理念。通过本项目,你将掌握如何构建一个能够在手机、平板、智慧屏等设备间无缝切换和同步的新闻阅读应用。

本项目将展示以下关键特性:新闻列表的多设备同步、阅读状态的分布式共享、跨设备新闻内容流转。这些功能基于HarmonyOS的分布式数据管理分布式任务调度能力实现。

环境配置与项目初始化

开发环境要求

  • DevEco Studio 4.0 Beta2或更高版本
  • HarmonyOS 5.0 SDK,API Version 12+
  • 真机设备(需开启开发者模式)或模拟器

创建项目与配置权限

首先创建新项目,选择"Empty Ability"模板。在module.json5中配置必要的分布式权限:

{"module": {"requestPermissions": [{"name": "ohos.permission.DISTRIBUTED_DATASYNC","reason": "$string:distributed_datasync_reason","usedScene": {"ability": [".MainAbility"],"when": "inuse"}},{"name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"}]}
}

项目架构设计

代码结构规划

entry/src/main/ets/
├── common
│   ├── constants
│   └── utils
├── entryability
├── model           # 数据模型
├── pages           # 页面组件
├── view            # 可复用UI组件
├── viewmodel       # 视图模型
└── service         # 服务层

核心技术栈

  • UI框架: ArkUI声明式开发范式
  • 数据同步: 分布式数据管理
  • 网络请求: @ohos.net.http
  • 设备管理: 设备发现与认证

实现新闻数据模型

定义新闻数据结构,支持分布式同步:

// model/NewsItem.ts
export class NewsItem {id: string = '';title: string = '';summary: string = '';source: string = '';publishTime: string = '';imageUrl: string = '';isRead: boolean = false;deviceId: string = '';  // 最后阅读的设备IDconstructor(data?: any) {if (data) {this.id = data.id || '';this.title = data.title || '';this.summary = data.summary || '';this.source = data.source || '';this.publishTime = data.publishTime || '';this.imageUrl = data.imageUrl || '';}}// 转换为可序列化对象toObject(): any {return {id: this.id,title: this.title,summary: this.summary,source: this.source,publishTime: this.publishTime,imageUrl: this.imageUrl,isRead: this.isRead,deviceId: this.deviceId};}
}

构建新闻列表页面

栅格布局适配多端

使用栅格布局系统实现不同屏幕尺寸的适配:

// pages/NewsListPage.ets
@Entry
@Component
struct NewsListPage {@State isLoading: boolean = true;@StorageLink('newsList') newsList: NewsItem[] = [];build() {Column() {// 标题栏Row() {Text('新闻头条').fontSize(24).fontWeight(FontWeight.Bold)Button('刷新').margin({ left: 20 }).onClick(this.refreshNews)}.padding(16).width('100%')// 新闻列表GridRow({columns: {sm: 4,md: 8,lg: 12},breakpoints: {value: ['320vp', '600vp', '840vp']},gutter: { x: 12 }}) {GridCol({span: { sm: 4, md: 8, lg: 8 },offset: { lg: 2 }}) {this.buildNewsList()}}.layoutWeight(1)}.height('100%').width('100%')}@BuilderbuildNewsList() {if (this.isLoading) {LoadingProgress().width(50).height(50).margin({ top: 100 })} else {List({ space: 10 }) {ForEach(this.newsList, (news: NewsItem) => {ListItem() {NewsCard({ news: news }).onClick(() => this.navigateToDetail(news))}}, (news: NewsItem) => news.id)}.width('100%').layoutWeight(1)}}
}

新闻卡片组件实现

// view/NewsCard.ets
@Component
struct NewsCard {@Prop news: NewsItembuild() {Row() {// 新闻图片if (this.news.imageUrl) {Image(this.news.imageUrl).width(120).height(80).objectFit(ImageFit.Cover).margin({ right: 12 }).borderRadius(8)}// 新闻内容Column() {Text(this.news.title).fontSize(18).fontColor(this.news.isRead ? '#666666' : '#000000').maxLines(2).textOverflow({ overflow: TextOverflow.Ellipsis }).margin({ bottom: 8 })Row() {Text(this.news.source).fontSize(12).fontColor('#888888')Text(this.news.publishTime).fontSize(12).fontColor('#888888').margin({ left: 10 })}.width('100%').justifyContent(FlexAlign.Start)}.layoutWeight(1)}.padding(12).borderRadius(8).backgroundColor(this.news.isRead ? '#F5F5F5' : '#FFFFFF').shadow({ radius: 2, color: '#10000000', offsetX: 0, offsetY: 1 }).width('100%').height(104)}
}

实现分布式数据同步

分布式数据管理服务

// service/NewsSyncService.ts
import distributedData from '@ohos.data.distributedData';
import deviceInfo from '@ohos.deviceInfo';
const STORE_ID = 'news_data_store';
const NEWS_KEY = 'synced_news_list';
export class NewsSyncService {private kvManager: distributedData.KVManager;private kvStore: distributedData.SingleKVStore;private deviceId: string = deviceInfo.deviceId;// 初始化分布式数据存储async init(): Promise {try {const config = {bundleName: 'com.example.newsapp',userInfo: {userId: 'defaultUser',userType: distributedData.UserType.SAME_USER_ID}};this.kvManager = distributedData.createKVManager(config);const options = {createIfMissing: true,encrypt: false,backup: false,autoSync: true,kvStoreType: distributedData.KVStoreType.SINGLE_VERSION};this.kvStore = await this.kvManager.getKVStore(STORE_ID, options);// 订阅数据变更this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_ALL,(data) => this.handleDataChange(data));} catch (error) {console.error('初始化分布式数据存储失败:', error);}}// 处理数据变更private handleDataChange(data: distributedData.ChangeNotification): void {if (data.insertEntries.length > 0 && data.insertEntries[0].key === NEWS_KEY) {try {const newsData = JSON.parse(data.insertEntries[0].value.value);AppStorage.setOrCreate('newsList', newsData);} catch (error) {console.error('解析同步数据失败:', error);}}}// 同步新闻列表到所有设备async syncNewsList(newsList: NewsItem[]): Promise {if (!this.kvStore) {await this.init();}try {const serializableList = newsList.map(item => item.toObject());await this.kvStore.put(NEWS_KEY, JSON.stringify(serializableList));} catch (error) {console.error('同步新闻列表失败:', error);}}// 更新单条新闻的阅读状态async updateNewsReadStatus(newsId: string, isRead: boolean): Promise {try {const currentListStr = await this.kvStore.get(NEWS_KEY);const currentList: NewsItem[] = currentListStr ?JSON.parse(currentListStr).map((item: any) => new NewsItem(item)) : [];const updatedList = currentList.map(item => {if (item.id === newsId) {const updated = new NewsItem(item);updated.isRead = isRead;updated.deviceId = this.deviceId;return updated;}return item;});await this.syncNewsList(updatedList);} catch (error) {console.error('更新阅读状态失败:', error);}}
}

新闻详情页实现

详情页布局与数据传递

// pages/NewsDetailPage.ets
@Entry
@Component
struct NewsDetailPage {@State currentNews: NewsItem = new NewsItem();private syncService: NewsSyncService = new NewsSyncService();onPageShow(params: any): void {if (params?.newsId) {const allNews: NewsItem[] = AppStorage.get('newsList') || [];this.currentNews = allNews.find(news => news.id === params.newsId) || new NewsItem();// 标记为已读并同步this.syncService.updateNewsReadStatus(params.newsId, true);}}build() {Column() {// 顶部导航栏this.buildHeader()// 新闻内容Scroll() {Column() {Text(this.currentNews.title).fontSize(22).fontWeight(FontWeight.Bold).margin({ bottom: 16 }).width('100%')Row() {Text(this.currentNews.source).fontSize(14).fontColor('#888888')Text(this.currentNews.publishTime).fontSize(14).fontColor('#888888').margin({ left: 10 })}.width('100%').margin({ bottom: 20 })if (this.currentNews.imageUrl) {Image(this.currentNews.imageUrl).width('100%').height(200).objectFit(ImageFit.Cover).margin({ bottom: 20 }).borderRadius(8)}Text(this.currentNews.summary).fontSize(16).lineHeight(24).width('100%')}.padding(16)}.layoutWeight(1)// 底部操作栏this.buildFooter()}.height('100%').width('100%')}@BuilderbuildFooter() {Column() {Divider().color('#E5E5E5').width('100%')GridRow({columns: { sm: 4, md: 8, lg: 12 },gutter: { x: 12 }}) {GridCol({ span: { lg: 8, offset: { lg: 2 } } }) {Row() {TextInput({ placeholder: '输入评论...' }).layoutWeight(1).margin({ right: 12 })// 分享按钮 - 实现跨设备流转Image($r('app.media.ic_share')).width(24).height(24).onClick(() => this.shareToDevice())}.padding(12)}}}.backgroundColor('#F8F8F8')}
}

实现设备发现与跨设备分享

设备管理功能

// service/DeviceManagerService.ts
import deviceManager from '@ohos.distributedHardware.deviceManager';
export class DeviceManagerService {private deviceMag: deviceManager.DeviceManager;// 获取可信设备列表async getTrustedDevices(): Promise {return new Promise((resolve, reject) => {deviceManager.createDeviceManager('com.example.newsapp', (err, data) => {if (err) {reject(err);return;}this.deviceMag = data;const devices = this.deviceMag.getTrustedDeviceListSync();resolve(devices);});});}// 释放设备管理实例release(): void {if (this.deviceMag) {this.deviceMag.release();}}
}

设备选择弹窗组件

// view/DeviceListDialog.ets
@CustomDialog
@Component
struct DeviceListDialog {@Consume newsItem: NewsItem;@Link deviceList: deviceManager.DeviceInfo[];@State selectedDevices: string[] = [];build() {Column() {Text('选择设备').fontSize(18).fontWeight(FontWeight.Bold).margin({ top: 20, bottom: 16 })List() {ForEach(this.deviceList, (device: deviceManager.DeviceInfo) => {ListItem() {Row() {Text(device.deviceName).fontSize(16).layoutWeight(1)Checkbox().select(this.selectedDevices.includes(device.deviceId)).onChange((isSelected) => {this.toggleDeviceSelection(device.deviceId, isSelected);})}.padding(12)}})}.layoutWeight(1)Row() {Button('取消').layoutWeight(1).onClick(() => this.controller.close())Button('确定').layoutWeight(1).onClick(() => this.confirmSelection())}.padding(16)}.height('60%').width('100%').backgroundColor(Color.White).borderRadius(16)}private toggleDeviceSelection(deviceId: string, selected: boolean): void {if (selected) {this.selectedDevices.push(deviceId);} else {const index = this.selectedDevices.indexOf(deviceId);if (index >= 0) {this.selectedDevices.splice(index, 1);}}}private confirmSelection(): void {// 实现跨设备启动逻辑this.startRemoteAbilities(this.selectedDevices, this.newsItem.id);this.controller.close();}private startRemoteAbilities(deviceIds: string[], newsId: string): void {deviceIds.forEach(deviceId => {const want = {deviceId: deviceId,bundleName: 'com.example.newsapp',abilityName: 'com.example.newsapp.MainAbility',parameters: {url: 'pages/NewsDetailPage',newsId: newsId}};// 启动远程AbilityfeatureAbility.startAbility(want).then(() => {console.info(`成功启动设备 ${deviceId} 上的应用`);}).catch((error) => {console.error(`启动设备 ${deviceId} 上的应用失败:`, error);});});}
}

网络请求服务封装

新闻API服务

// service/NewsService.ts
import http from '@ohos.net.http';
import { NewsItem } from '../model/NewsItem';
const NEWS_API = 'https://newsapi.org/v2/top-headlines?country=us&apiKey=YOUR_API_KEY';
export class NewsService {private httpRequest = http.createHttp();// 获取新闻列表async fetchNewsList(): Promise {return new Promise((resolve, reject) => {this.httpRequest.request(NEWS_API,{method: 'GET',header: { 'Content-Type': 'application/json' }},(err, data) => {if (err) {reject(err);return;}if (data.responseCode === 200) {try {const result = JSON.parse(data.result.toString());const newsList = result.articles.map((article: any) =>new NewsItem({id: this.generateId(article.url),title: article.title,summary: article.description,source: article.source?.name,publishTime: article.publishedAt,imageUrl: article.urlToImage}));resolve(newsList);} catch (parseError) {reject(parseError);}} else {reject(new Error(`HTTP ${data.responseCode}`));}});});}private generateId(url: string): string {// 简单的ID生成逻辑return url.hashCode().toString();}
}

多端适配与响应式布局

基于栅格的响应式设计

// common/constants/Breakpoints.ets
export class CommonConstants {// 断点定义static readonly BREAKPOINT_SM: string = 'sm';    // 小屏设备 < 600vpstatic readonly BREAKPOINT_MD: string = 'md';    // 中屏设备 600vp - 840vpstatic readonly BREAKPOINT_LG: string = 'lg';    // 大屏设备 > 840vp// 栅格列数static readonly FOUR_COLUMN: number = 4;static readonly EIGHT_COLUMN: number = 8;static readonly TWELVE_COLUMN: number = 12;// 设备类型阈值static readonly SMALL_DEVICE_TYPE: number = 320;static readonly MIDDLE_DEVICE_TYPE: number = 600;static readonly LARGE_DEVICE_TYPE: number = 840;
}

应用入口与权限处理

MainAbility权限动态申请

// entryability/EntryAbility.ets
import Ability from '@ohos.app.ability.UIAbility';
import permission from '@ohos.abilityAccessCtrl';
export default class EntryAbility extends Ability {onWindowStageCreate(windowStage: any): void {// 动态申请分布式权限this.requestDistributedPermissions();windowStage.loadContent('pages/Index', (err, data) => {if (err) {console.error('加载页面失败:', err);}});}private async requestDistributedPermissions(): Promise {try {const permissions = ['ohos.permission.DISTRIBUTED_DATASYNC','ohos.permission.GET_DISTRIBUTED_DEVICE_INFO'];const atManager = permission.createAtManager();await atManager.requestPermissionsFromUser(this.context, permissions);} catch (error) {console.error('权限申请失败:', error);}}
}

测试与调试

分布式功能测试要点

  1. 设备组网测试:确保设备在同一网络环境下可互相发现
  2. 数据同步验证:在一台设备上操作,验证其他设备是否同步更新
  3. 跨设备启动测试:验证从A设备分享新闻到B设备的功能正常
  4. 网络异常处理:测试网络中断情况下的降级处理

项目总结与扩展思路

本分布式新闻客户端项目展示了HarmonyOS核心分布式能力的实际应用。通过本项目,你已掌握:

  1. 分布式数据管理的实现原理和实践方法
  2. 跨设备任务调度的技术细节
  3. 多端适配的响应式布局技巧
  4. 完整应用架构的设计模式

扩展功能建议

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

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

相关文章

2025年气流流型检测仪厂家权威推荐榜单:灌装机气流流型检测仪‌/气流流型验证服务‌/烟雾发生器‌源头厂家精选

在生物制药、集成电路等高端产业,洁净室气流流型的精确检测与验证直接关系到产品的质量与安全。 气流流型检测仪作为洁净环境监测的关键设备,通过可视化气流模式,帮助工程师评估洁净室性能、发现气流组织问题、确保…

AI元人文:LLM与价值协议——从偏见魔兽到多元护法的点化之路(完整版)

AI元人文:LLM与价值协议——从偏见魔兽到多元护法的点化之路 (议题层级说明:本文所探讨的,是关乎人类文明整体演进的元问题。其视角超越任何单一国家、文化或商业实体的短期利益,旨在为作为一个物种的“我们”思考…

实用指南:阿里云DLF 3.0:面向AI时代的智能全模态湖仓管理平台

实用指南:阿里云DLF 3.0:面向AI时代的智能全模态湖仓管理平台2025-11-26 08:47 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !impo…

2025年热门的低压电缆厂家最新热销排行

2025年热门的低压电缆厂家最新热销排行行业背景与市场趋势随着中国基础设施建设的持续投入和新能源产业的快速发展,低压电缆市场需求呈现稳定增长态势。据中国电器工业协会统计数据显示,2024年我国低压电缆市场规模已…

二叉搜索树与B树引入

二叉树的引入: 在一个链表之中,我们已知它的时间复杂度是O(N),。对于一个数组,我们可以通过二分搜索,来将寻找一个元素的时间复杂度降低为O(logN), 那么树的建立实际上就是对于链表进行的一个二分搜索。 我们要做的…

SpringBoot13-资料上传02-阿里云OSS

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025年评价高的阻燃控制电缆厂家推荐及选购参考榜

2025年评价高的阻燃控制电缆厂家推荐及选购参考榜行业背景与市场趋势随着我国工业化和城镇化进程的不断推进,阻燃控制电缆作为电力传输和控制系统的重要组成部分,市场需求持续增长。据中国电器工业协会电线电缆分会最…

2025 年中国十大有机蔬菜基地排行榜:有机蔬菜种植的典范

随着消费者对食品安全和健康生活方式的日益关注,有机农业在中国迎来了前所未有的发展高峰。有机蔬菜基地,作为保障绿色、无污染食品的核心产地,其规模、认证标准和产品品质成为追求健康生活家庭的核心考量。然而,市…

2025 年 11 月人力资源管理咨询品牌权威推荐榜:薪酬管理咨询,绩效管理咨询,企业薪酬绩效优化服务深度解析与口碑之选

2025 年 11 月人力资源管理咨询品牌权威推荐榜:薪酬管理咨询,绩效管理咨询,企业薪酬绩效优化服务深度解析与口碑之选 在当今快速变化的商业环境中,人力资源管理咨询已成为企业提升组织效能、优化人才结构的关键支撑…

2025年质量好的低烟无卤控制电缆厂家最新权威实力榜

2025年质量好的低烟无卤控制电缆厂家最新权威实力榜行业背景与市场趋势随着全球对安全环保要求的不断提高,低烟无卤控制电缆市场正迎来快速增长期。据中国电线电缆行业协会最新数据显示,2024年我国低烟无卤电缆市场规…

实用指南:typescript-eslint性能优化:优化规则匹配算法

实用指南:typescript-eslint性能优化:优化规则匹配算法pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas…

2025 年华东有机农场哪家好:德芳有机农场成为中国有机农场的典范

随着消费者对食品安全和健康生活方式的日益关注,有机农业在华东地区(包括江苏、上海、浙江、安徽、江西、福建)迎来了蓬勃发展。有机农场,作为保障绿色、无污染食品的生产基地,其选择和品质成为追求健康生活家庭的…

PostgreSQL - How to convert timestamp to date?

PostgreSQL - How to convert timestamp to date?In PostgreSQL, you can convert a timestamp to a date (i.e., drop hours/minutes/seconds) in several common ways: ✅ 1. Cast to date Fastest and simplest:SE…

销售额一年内增长3倍:行业颠覆者 Lounge Underwear 如何打造价值数百万美元的企业?

这家位于英国的 DTC 巨头刚刚度过了辉煌的一年:他们的销售额达到1900万美元,相比前年增长了3倍,他们的团队扩充了四倍,达到80个人。在本期 Shopify Masters 节目中,我们与 Lounge 背后的生活兼业务合作伙伴 Dan 和…

2025 年江苏有机农场排名推荐榜:德芳有机农场排名第一

随着长三角地区消费者对食品安全和健康生活方式的日益关注,有机农业在江苏省迎来了蓬勃发展。对于寻求高品质绿色食材的家庭而言,选择一家位于本地、品质可靠的有机农场至关重要。有机农场,作为保障绿色、无污染食品…

SWAT模型应用 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

苹果硅Mac电池保护神器 - 智能充电限制工具

专为Apple Silicon Mac设计的电池保护工具,可设置充电上限至80%以延长电池寿命,支持命令行和图形界面操作,具备持久化守护进程和智能电压管理功能。Battery - 苹果硅Mac电池保护工具 专为Apple Silicon Mac设计的电…

2025年质量好的排名前温州代理记账企业热选榜

2025年质量好的排名前温州代理记账企业热选榜行业背景与市场趋势随着温州民营经济的持续发展和财税政策的不断更新,代理记账行业迎来了前所未有的发展机遇。据温州市财政局最新数据显示,2024年温州地区代理记账市场规…

2025年热门的超温保护限流式保护器行业内口碑厂家排行榜

2025年热门的超温保护限流式保护器行业内口碑厂家排行榜行业背景与市场趋势随着电气安全意识的不断提升和智能配电技术的快速发展,超温保护限流式保护器作为电气安全领域的关键设备,在2025年迎来了新一轮的市场增长。…