Neo 构建鸿蒙应用【二】:技术路线全解
文章摘要 本文详细解析了Neo框架构建鸿蒙应用的技术路线,核心借鉴了JavaBean和IoC思想。主要技术机制包括:Service作为最小功能单元,采用四阶段生命周期管理;NeoModule采用Koin式模块声明组织服务;ServiceManager作为IoC容器处理依赖解析;Phase机制实现渐进式启动,通过四个预定义阶段(GLOBAL/BUSINESS/FEATURE/LAZY)优化启动性能。
Neo 构建鸿蒙应用【二】:技术路线全解
Neo 框架连载 · AI 辅助撰写 · GitHub
上一篇建立了四层架构的宏观视图。这一篇把 Neo 的全部技术机制讲完:Service、NeoModule、ServiceManager、Phase、Observable、StateBinder、Scope。
核心思路借鉴了 JavaBean 和 IoC——Java Boy 编程梦开始的地方。
Service:最小功能单元
所有领域建模被命名为 XXXService,继承 Service 基类。一个 Service 的完整生命周期:
constructor → register → init → loginCallback → load → (WORKING)
↑
reload ← unload ← logoutCallback
基类定义
export abstract class Service implements AppPropagation {
tag: string = this.constructor.name // 默认用类名作标识
context: Context | undefined
depServices: AppPropagation[] = [] // 依赖的服务列表
constructor(services: Service[]) {
this.depServices = services
}
register = (cxt: Context) => {
this.context = cxt
this.init()
// lifecycle → INITIALIZED
}
loginCallback = async () => {
this.load().then(res => {
if (res) {
// lifecycle → WORKING
serviceManager.refreshNode(this, this.lifecycle)
}
})
}
logoutCallback = async () => { /* unload → INITIALIZED */ }
init() {} // 初始化成员属性,不能做网络 IO
async load(): Promise<boolean> { return true } // 加载数据
async unload(): Promise<boolean> { return true } // 卸载清理
reload = () => { this.unload().then(() => this.load()) }
}
设计要点:
register是箭头函数属性,不是方法。ArkTS 中子类不能 override 父类的箭头函数属性,保证基类的生命周期管理不被绕过constructor(services: Service[])构造器注入,Spring 的思路init()不能做网络 IO——仅用于初始化成员属性load()返回Promise<boolean>——true 表示成功,状态转为 WORKING
实际例子
// AuthService.ets
export class AuthService extends Service {
private currentUser: UserInfo | null = null
private token: string = ''
constructor() { super([]) }
init() { this.currentUser = null; this.token = '' }
async load(): Promise<boolean> {
const saved = await this.loadFromStorage()
if (saved) { this.token = saved.token; this.currentUser = saved.user }
return true
}
async unload(): Promise<boolean> {
this.currentUser = null; this.token = ''
return true
}
}
NeoModule:Koin 式模块声明
Service 定义好了,如何组织起来?借鉴 Koin 的 module DSL 风格:
import { NeoModule, GLOBAL_PHASE, BUSINESS_PHASE } from 'neo'
const networkModule = new NeoModule('Network', [
{ tag: 'ApiService', phase: GLOBAL_PHASE, factory: () => new ApiService([]) },
{ tag: 'AuthService', phase: GLOBAL_PHASE, factory: () => new AuthService([]) },
])
const userModule = new NeoModule('User', [
{ tag: 'UserService', phase: BUSINESS_PHASE,
factory: () => new UserService([]),
dependencies: ['AuthService'] },
])
每个声明包含:
| 字段 | 说明 |
|---|---|
tag |
服务唯一标识 |
phase |
所属加载阶段 |
factory |
延迟创建工厂 |
dependencies |
依赖的其他服务 tag |
模块组合:
const appModule = networkModule.merge(userModule)
// 同名声明以当前模块优先
校验(load() 时自动调用):
const errors = appModule.validate()
// 检测缺失依赖 + 循环依赖(DFS)
ServiceManager:IoC 容器
中枢管理者,处理注册、依赖解析和生命周期。
核心流程
// EntryAbility.onCreate
serviceManager.register(this.context) // 注入上下文
serviceManager.loadModule(appModule) // 加载模块(校验、分组、注册)
await serviceManager.loginCallback() // 触发多阶段启动
依赖解析
内部维护两张映射:serviceMap(tag → 实例)和 dependentMap(被谁依赖)。loginCallback 触发时递归解析依赖链——加载 Service A 之前,确保其所有依赖已 WORKING:
// ServiceManager 内部(简化)
private ensureNodeWorking(node) {
await Promise.all(node.depServices.map(dep => this.ensureNodeWorking(dep)))
this.invokeLogin(node)
await this.waitForLifecycle(node.tag, ServiceLifeCycle.WORKING)
}
获取服务:
const userService = serviceManager.get<UserService>('UserService')!
const isReady = serviceManager.ready(userService)
Phase:渐进式启动
27 个 Service 不需要同时加载。
四个预定义阶段
| 阶段 | 优先级 | 策略 | 用途 |
|---|---|---|---|
| GLOBAL_PHASE | 10 | 串行等待 | 配置、数据库、网络、安全 |
| BUSINESS_PHASE | 20 | 串行等待 | 认证、用户、消息、通话 |
| FEATURE_PHASE | 30 | 并行触发 | 搜索、统计、主题、国际化 |
| LAZY_PHASE | 40 | 并行触发 | 小游戏、故事 |
策略含义:
- 串行等待(
waitForComplete: true):该阶段全部完成后才进入下一阶段 - 并行触发(
waitForComplete: false):触发后立即进入下一阶段,不等待完成
时间轴 ─────────────────────────────────────────────→
GLOBAL (串行) ████████████████
BUSINESS (串行) ████████████████
FEATURE (并行) ████ ← 不阻塞
LAZY (并行) ██ ← 不阻塞
↑ UI 可交互
启动耗时可控的核心:GLOBAL + BUSINESS 同步确保核心就绪,FEATURE + LAZY 异步不阻塞。优化手段就是把非必要 Service 从 BUSINESS 降到 FEATURE。
实战中的启动优化
我曾在实际项目中用这套机制做过启动优化(详见原文),当时 Neo 还不存在,但核心代码已经具备了 Service + Phase 的雏形。
优化成果固然可喜,但更重要的是整个结构变得可控了。因为每个 Service 的依赖关系、加载阶段、生命周期都是声明式的,我可以保证:
- 把某个 Service 从 BUSINESS 降到 FEATURE,不会导致依赖它的模块出问题——依赖解析是自动的
- 新增一个 Service 不需要改任何已有代码——在 AppModule 加一行声明即可
- 变更的影响范围是可预期的——约束由框架兜底
这不是黑盒优化,而是白盒优化。以后遇到启动性能问题,打开 AppModule 调整 Phase 归属即可——不需要重新分析依赖链,不需要画调用图,套路是固定的。
自定义阶段:
const CACHE_PHASE = createPhase({
name: 'CACHE', priority: 25, waitForComplete: false, description: '缓存预热'
})
Observable:Service 端的数据源
Service 加载了数据,如何让页面感知变化?
朴素做法是页面直接调 Service 方法拿返回值赋给 @State——但只在初始化时有效,其他页面的数据变更不会自动刷新。
Neo 的方案是 Observable——泛型观察者容器:
export class Observable<T> {
private value_: T
private listeners: Set<(value: T) => void> = new Set()
constructor(initialValue: T) { this.value_ = initialValue }
getValue(): T { return this.value_ }
setValue(newValue: T): void {
if (this.value_ === newValue) return
this.value_ = newValue
this.notify()
}
onChange(listener: (value: T) => void): () => void {
this.listeners.add(listener)
return () => { this.listeners.delete(listener) }
}
}
没有 Subject、没有 Operator、没有调度器。ArkTS 不支持 RxJS 那套管道,也没有必要引入。
Service 中使用
export class IMService extends Service {
private conversationsObs = new Observable<Conversation[]>([])
private messagesObs = new Observable<ChatMessage[]>([])
getConversationsObservable() { return this.conversationsObs }
getMessagesObservable() { return this.messagesObs }
async sendMessage(conversationId: string, content: string): Promise<ChatMessage> {
const msg = await this.doSend(conversationId, content)
const msgs = this.messagesObs.getValue()
this.messagesObs.setValue([...msgs, msg]) // 自动通知 UI
return msg
}
async load(): Promise<boolean> {
this.conversationsObs.setValue(await this.fetchConversations())
return true
}
}
模式:内部持有 Observable → getter 暴露 → setValue 触发变更。外部只能订阅,不能直接修改。
StateBinder:Service 到 @State 的桥
Observable 解决了通知问题,StateBinder 解决了写入 @State 的问题:
export class StateBinder {
static bind<T>(
observable: Observable<T>,
component: Object, // 页面组件实例
stateKey: string // @State 属性名
): () => void {
const record = component as Record<string, Object>
record[stateKey] = observable.getValue() as Object // 初始化
const unlisten = observable.onChange((newValue: T) => {
record[stateKey] = newValue as Object // 变更时自动写入
})
return unlisten
}
static bindAll(factories: Array<() => () => void>): () => void { /* 批量绑定 */ }
}
页面中使用
@Entry
@Component
struct ChatListPage {
@State conversations: Conversation[] = []
private unbind: (() => void) | undefined
aboutToAppear() {
const imSvc = serviceManager.get<IMService>('IMService')!
this.unbind = StateBinder.bind<Conversation[]>(
imSvc.getConversationsObservable(),
this as Object, // ArkTS 要求显式转换
'conversations'
)
}
aboutToDisappear() { this.unbind?.() }
build() {
List() {
ForEach(this.conversations, (conv: Conversation) => {
ListItem() { /* 渲染会话项 */ }
})
}
}
}
注意两点 ArkTS 限制:
this as Object不能省略——组件的this类型不允许直接传给Object参数- 显式泛型
<T>不能省略——ArkTS 不支持自动推断泛型给Record<string, Object>赋值
批量绑定:
aboutToAppear() {
this.unbind = StateBinder.bindAll([
() => StateBinder.bind<Conversation[]>(imSvc.getConversationsObservable(), this as Object, 'conversations'),
() => StateBinder.bind<ChatMessage[]>(imSvc.getMessagesObservable(), this as Object, 'messages'),
() => StateBinder.bind<number>(matchSvc.getCountObservable(), this as Object, 'matchCount'),
])
}
aboutToDisappear() { this.unbind?.() } // 一行解绑所有
Scope:页面级作用域
有些 Service 是页面专属的,页面离开时应该卸载:
import { scopeManager } from 'neo'
aboutToAppear() {
this.scope = scopeManager.createScope('ChatPage')
this.scope.bindTo(this) // aboutToDisappear 时自动 close
const chatRoomSvc = new ChatRoomService([])
this.scope.register(chatRoomSvc)
}
// 页面销毁 → scope.close() → unload 所有注册的服务
Scope 查找机制:优先从作用域内找,找不到回退到全局 ServiceManager。scope.get<T>('ChatRoomService') 获取页面级服务,scope.getGlobal<T>('AuthService') 获取全局服务。
小结
Neo 的完整技术路线:
| 组件 | 职责 |
|---|---|
| Service | 功能单元,声明式生命周期 |
| NeoModule | Koin 式模块声明,组合与校验 |
| ServiceManager | IoC 容器,依赖解析 |
| Phase | 多阶段加载,串行/并行策略 |
| Observable | Service 端可观察数据源 |
| StateBinder | Service 数据 → @State 响应式桥 |
| Scope | 页面级作用域,自动卸载 |
下一篇是最后一篇,用 SoulApp 示例串起全链路,并分享这个框架在工程实践中的一些感悟。
系列文章
- 一种通用中小型基于ArkTS的鸿蒙应用开发框架(华为开发者联盟)
- 01 架构困境与四层结构化设计
- 02 Neo 技术路线全解(本文)
- 03 实战 SoulApp 与工程感悟
更多推荐


所有评论(0)