
简介基于HarmonyOS 4的新闻类App源代码hongmeng-headlines面向初入鸿蒙生态的移动端开发者旨在以真实项目串联UI搭建、数据通信与设备协同等核心技能。项目基于DevEco Studio构建包含完整的新闻列表与文章详情界面适合系统学习鸿蒙应用从零到一的工程实践也可作为课程设计或毕业设计的参考。压缩包内共78个文件以ets界面逻辑、json5/json配置为主辅以svg/png图标、ts构建脚本及tgz依赖整体仅22.75MB目录按模块划分便于逐段研读。目前已有1447人学习下载。通过源码可掌握组件化布局、界面设计、网络请求与JSON解析、本地数据存储、事件监听、生命周期管理、多设备适配、权限申请乃至分布式协同等关键知识点同时项目提供了清晰的基础模板便于在此基础上扩展推荐、搜索等个性化新闻功能兼具教学与二次开发价值。1. 为什么是 HarmonyOS 4新闻类 APP 的生态卡位与技术起点新闻类 APP 是移动端最普及、也最容易写僵的品类但如果在 HarmonyOS 4 上从零搭一套可编译的源代码反而比聊天、支付这类应用更轻——不碰复杂传感器、不依赖私有系统服务核心链路就是网络请求、列表渲染、Web 详情页三条线。下面顺着这三条线以 API 10 为基线用 ArkTS 串成一个最小可运行工程先定 Stage 模型下的模块边界再解决数据从哪来、列表怎么不卡、详情页怎么承接最后走到上架前的调试与自检。适合准备过 HarmonyOS 应用基础认证、想直接看代码落点的开发者也适合评估新闻类 App 迁移成本的客户端团队。2. HarmonyOS 4 新闻 APP 的代码骨架Stage 模型、模块划分与数据流设计拿到需求先别急着写 List。新闻类 App 会在冷启动、切后台、首屏加载三个场景被用户反复考验这决定了代码骨架先于功能细节。HarmonyOS 4 把所有应用入口收敛到 Stage 模型不少从 FA 模型迁过来的工程会在这一步返工。2.1 为什么 Stage 模型是新闻类 App 的默认起点Stage 模型把应用入口拆成 UIAbility 与 ExtensionAbility 两类能力。新闻客户端只需要一个主界面入口用 UIAbility 就够启动方式配成 singleton避免用户从桌面图标、最近任务、推送通知三个入口进来时拉起多份页面栈。module.json5 里和新闻场景直接相关的四个字段如下改动之前先读一遍。{ module: { name: entry, type: entry, abilities: [ { name: EntryAbility, srcEntry: ./ets/entryability/EntryAbility.ets, launchType: singleton, skills: [ { entities: [entity.system.home], actions: [action.system.home] } ] } ] } }launchType 默认是 singleton不建议为了“多窗口”改成 multiton。新闻场景没有多开诉求multiton 会让每次点击图标都新建一个 Ability返回键行为变得混乱。skills 里保留 Home 入口即可推送通知的跳转在 onNewWant 回调里做二次分发不需要注册多条 skill。冷启动路径上还有一件事EntryAbility 的 onWindowStageCreate 里只做窗口加载把网络服务这类全局依赖放到 Ability 的成员变量里初始化不要放在页面组件内部。页面组件随路由销毁重建而网络层希望只存在一份。2.2 新闻模块的工程目录与最小代码结构entry/src/main/ets/ ├── entryability/EntryAbility.ets ├── pages/ │ ├── Index.ets │ └── NewsDetail.ets ├── model/ │ └── NewsModels.ets ├── network/ │ ├── NewsService.ets │ └── HttpWrapper.ets ├── components/ │ ├── NewsList.ets │ └── NewsListItem.ets └── common/ └── Constants.ets这个划分刻意不引入 router 层。页面少时路由跳转直接用页面路由的 pushUrl 按字符串路径管理等页面超过五个再抽象路由表。model 只放类型定义和序列化方法network 只负责请求和解析components 里的 NewsListItem 接收一个 NewsItem 对象不直接触碰网络层。目录职责变更频率model定义 NewsItem、NewsChannel、分页结构低network请求、超时、重试、错误归一中components列表卡片、骨架屏、图片占位高pages组装页面状态与生命周期中变更频率最高的是 components信息流的卡片样式迭代基本都集中在这里。model 和 network 要尽量稳定接口响应结构变了顶多改改 model 字段UI 层不动。2.3 数据流设计从接口到 ListItem 的状态流转常见做法是给列表页做一个 ViewModel 容器把加载中、加载成功、加载失败、加载更多四个状态收拢到一个可观察对象里。这个容器不持有 ArkUI 组件只持有业务数据页面通过 State 持有容器的引用。class NewsFeedState { items: NewsItem[] [] isLoading: boolean false errorMsg: string page: number 1 hasMore: boolean true }页面在 onPageShow 里触发首次加载加载完成后整体赋值 items。不要在 NewsListItem 里各自发请求否则快速滑动时请求数量会失控。分页页码就放在容器里滑动到底部时只做 page1 的增量请求新数据追加到 items 尾部。ViewModel 别放进自定义组件。组件销毁后数据跟着清空从详情页返回列表时又会重新拉接口新闻流用户一天要进出详情页几十次重复请求量很可观。把容器放到 EntryAbility 的应用级单例里或者用 AppStorage 做跨页面缓存都比绑定在页面生命周期上可控。3. 拿到新闻数据的三种方式RSS 解析、JSON 接口与本地 Mock 数据新闻类 APP 开发早期最大的变量不是 UI 而是数据源。标题里“源代码”三个字让人以为核心是界面代码实际上一个能演示的新闻 App八成时间在跟数据格式打交道。三种数据源最常用按接入成本排个序。3.1 三种数据源的选型与适用阶段数据源格式适合阶段主要成本RSS 2.0XML快速验证阅读体验字段少、无图片尺寸、编码杂开放新闻 APIJSON产品原型与内测需要申请 Key、有配额限制本地 MockJSON 文件开发期与 UI 联调需维护假数据易与真实接口脱节RSS 内容老一点但胜在即时可得找两三个科技或财经博客的 feed先用真数据把排版调好再去接开放 API。不建议一上来就接大厂的新闻接口申请流程和限流规则会拖慢首屏联调。RSS 本质是 XMLArkTS 里用 ohos.convertxml 的 ConvertXML 类把 feed 转成对象再取 title、description、pubDate 字段够搭一个阅读器原型。商用场景不推荐 RSS很多站点在图片上做防盗链列表页会缺图。本地 Mock 经常被忽略。写一个挂在 assets 下的 news-mock.json字段结构和真实接口保持一致前端先把加载态、空态、失败态全部走通后端接口联调时才不会互相等。3.2 用 ArkTS 解析 JSON 新闻数据的最小实现开放新闻 API 通常返回固定结构错误码、数据列表、分页信息。先把响应体声明成类型再解析这一步不要偷懒。export interface NewsItem { id: number title: string summary: string source: string publishTime: string imageUrl: string contentUrl: string } export interface NewsResponse { code: number message: string data: { list: NewsItem[] hasMore: boolean nextPage: number } }然后用 HttpWrapper 封装 ohos.net.http。下面这段是请求函数的核心超时时间、请求头、错误处理都集中在这里import http from ohos.net.http export class NewsService { async fetchNews(page: number): PromiseNewsItem[] { const httpRequest http.createHttp() try { const resp await httpRequest.request( ${Constants.BASE_URL}/news?page${page}size20, { method: http.RequestMethod.GET, connectTimeout: 10000, readTimeout: 10000, header: { Content-Type: application/json } } ) const body JSON.parse(resp.result as string) as NewsResponse if (body.code ! 0) throw new Error(api error ${body.code}) return body.data.list } finally { httpRequest.destroy() } } }三个参数值得细说。第一createHttp 创建的实例用完必须 destroy复用全局实例在长时间运行后可能遇到连接无响应日志里表现为 promise 一直 pending。第二connectTimeout 与 readTimeout 在新闻场景建议都设 10 秒首屏数据超过 3 秒用户就流失但把超时压到 3 秒又会在弱网下频繁失败客户端做一次重试并记录第一次失败原因比盲目缩短超时有效。第三resp.result 默认是字符串务必先 JSON.parse 再断言成 NewsResponse直接强转会在字段缺失时拿到 undefined后续列表渲染直接崩。3.3 配置网络权限与明文流量跑真机前必做的两处修改真机跑新闻 App 第一步遇到请求报 2300006 错误码多半是网络权限没给。HarmonyOS 4 在 module.json5 里声明权限不在页面代码里做动态申请。{ module: { requestPermissions: [ { name: ohos.permission.INTERNET } ], metadata: [ { name: network_security_config, resource: $profile:network_config } ] } }如果接口地址是 http 明文还需要放行明文流量。在 resources/base/profile/network_config.xml 里写?xml version1.0 encodingutf-8? network-security-config base-config cleartextTrafficPermittedtrue trust-anchors certificates srcsystem / /trust-anchors /base-config /network-security-configmetadata 里 network_security_config 这个 key 是固定的resource 指向 profile 目录下的 xml 文件名不要写到 rawfile。正式切 https 后这段配置可以整段拿掉。调试时加载 DevEco 的 Log 面板过滤 ohos.net.http 能看到每个请求的失败行2300006 在权限缺失时出现频率最高其次是设备网络不可达按日志顺序排查比盲改代码快。真机请求报证书校验失败时先看系统时间是否准确证书校验在时间跳变的设备上会先失败。4. ArkUI 渲染新闻列表与详情页List、Web 组件与图片缓存列表渲染是新闻类 APP 体验的分水岭。方案一Scroll 加 ForEach 硬渲染数据量小的时候没问题新闻流数据几百条起步内存上涨明显。方案二直接用 List 加 LazyForEach只渲染可视区域这是 HarmonyOS 4 下新闻列表的默认解法。4.1 新闻列表用 LazyForEach 实现懒加载先写一个实现 IDataSource 的数据源类它负责告诉 List 总共多少条、每条数据是什么以及在数据追加时发通知。class NewsDataSource implements IDataSource { private items: NewsItem[] [] private listeners: DataChangeListener[] [] totalCount(): number { return this.items.length } getData(index: number): NewsItem { return this.items[index] } registerDataChangeListener(listener: DataChangeListener): void { this.listeners.push(listener) } unregisterDataChangeListener(listener: DataChangeListener): void { const idx this.listeners.indexOf(listener) if (idx 0) this.listeners.splice(idx, 1) } addItems(newItems: NewsItem[]): void { const start this.items.length this.items.push(...newItems) this.listeners.forEach(l l.onDataAdd(start, newItems.length)) } }这个类把新增数据的通知封装成 onDataAdd让 LazyForEach 增量刷新而不是全量重建。页面里这样组装 ListList({ space: 12, scroller: this.scroller }) { LazyForEach(this.dataSource, (item: NewsItem) { ListItem() { NewsListItem({ item: item }) } }, (item: NewsItem) ${item.id}_${item.publishTime}) } .cachedCount(4) .onReachEnd(() this.loadMore())LazyForEach 的第三个参数是键值生成函数必须能区分每条数据。用索引当 key 会导致插入或删除时复用错乱新闻流常有置顶、移除操作所以用 id 加时间戳组合。cachedCount(4) 表示首屏方向多缓存 4 条减少回滑白屏调到 10 不会让内存显著上涨但再大就失去懒加载意义了滑出屏幕的图片迟迟不释放。onReachEnd 在滚动到底部时触发分页。4.2 新闻详情页用 Web 组件承载富文本新闻详情最常见的数据形态是服务端下发的 HTML 片段或整页链接。原生解析 HTML 成本高图片、排版、字体都难还原详情页用 Web 组件加载 contentUrl 是行业统一做法。Web({ src: this.contentUrl, controller: this.controller }) .domStorageAccess(true) .javaScriptAccess(true) .onErrorReceive((event) { this.errorVisible true }) .onHttpErrorReceive((event) { console.error(http error: ${event.response.getResponseCode()}) })两个点要注意。第一domStorageAccess 要开部分新闻站点的懒加载脚本依赖 localStorage。第二 Web 页里用户滚动很多屏后返回再次进入会重建并回到顶部这是正常行为不需要做滚动位置恢复否则内存维护会变复杂。阅读字号是新闻 App 的高频设置项但设置方式要区分场景列表页的字号绑一个 State 驱动 Text 组件没问题详情页的字号要等页面加载完成后注入给 H5。this.controller.runJavaScript( document.body.style.fontSize ${this.fontSize}px; )runJavaScript 必须在 onPageEnd 回调之后再调用页面没加载完会报错。有些站点正文不是直接挂在 body 下需要再查一下文章容器的选择器这里只提供一个通用注入入口。4.3 图片加载占位图与内存控制列表卡片一般都有缩略图。Image 组件的加载、解码和缓存由系统接管开发阶段不需要引入三方图片库但两个参数要设置到位Image(item.imageUrl) .width(120) .height(80) .objectFit(ImageFit.Cover) .alt($r(app.media.placeholder)) .onError(() { this.showErrorPlaceholder true })参数作用建议值objectFit图片缩放模式Cover避免变形alt加载中与失败占位图本地资源onError加载失败回调切本地图或隐藏不要用 ImageFit.Contain新闻缩略图是固定宽高比裁剪Contain 会留白卡片视觉上参差。图片 URL 的域名有条件的话加进 network_config 的 domain-config省一次跳转等待。列表页面的加载更多状态建议单独放一个 footer 组件滚动到底部时显示转圈避免用弹窗打断滑动。5. 从源代码到上架抓包排查、性能数据与发布前自检代码写完之后从源代码到可发布版本之间还有一段路。这一章把调试、性能和上架前必须做的手续集中梳理一遍都是可复现的操作。5.1 新闻 App 抓包失败的三个排查点开发期抓包失败是最常见的求助场景。出现抓不到新闻请求时按下面顺序查能省下大量排查时间设备与抓包工具是否在同一网络。不在同一网段时流量根本过不去这是第一排查点。抓包证书是否装进系统信任区。只装用户证书时部分新闻接口在 HTTP/2 下会保持连接不触发抓包。目标接口是否为 HTTPS。证书没配对前只能看到连接握手看不到 HTTP 层内容。排查完这三项还抓不到把 App 完全杀掉重开。新闻类接口不少在启动后几秒内并发请求抓包工具的过滤条件设得过窄时容易漏掉。抓包的目的不是截取数据而是确认请求头、响应体和缓存策略是否符合预期。5.2 用 DevEco Profiler 看列表性能信息流卡顿不要凭感觉。DevEco Studio 的 Profiler 面板可以抓 ArkUI 渲染帧耗时。打开 Profiler 后操作列表快速滑动观察 Frame 和 Layout 两个指标。指标关注点合格线Frame平均帧耗时低于 16msLayout列表项布局时间单帧不超过 8msMemory图片与 Web 缓存占用占用不持续上涨如果 Layout 超时优先检查 NewsListItem 里是否有不必要的 State。放在子组件里但从不改动的字段不要用 State 修饰多余的装饰器会让整棵子树参与 diff。Profiler 里能看到每个自定义组件的创建耗时卡片创建超过 3ms 就该拆静态模板了。5.3 源码版本管理与发布前自检源代码与原始版本脱钩是二次修改的前提。调试用的 BASE_URL 不能进发布包由构建脚本替换或者维护一个单独的常量文件发布前只动这一处// common/ProfileConfig.ets export class ProfileConfig { static readonly BASE_URL: string https://debug.news.example.com // 发布前切换为 release 地址并由构建脚本注入 }调试包和发布包不要维护两套代码否则容易在发版时选错配置。发布前再走一遍这六项权限只留 INTERNET不开存储与位置权限隐私政策里写清采集哪些字段深色模式下列表卡片对比度足够断网时有错误提示而不是白屏连续启动退出三次无崩溃通过 HarmonyOS 应用基础认证后再提交审核。最后在 DevEco 里打 release 包用正式 keystore 签名勾选混淆开关。提交前的动作是卸载设备上的调试签名旧包再装正式包签名冲突会导致安装失败登录态和推送通道各跑一遍确认后走发布流程。本文还有配套的精品资源点击获取