HarmonyOS开发:网络层迁移——Retrofit/Alamofire到鸿蒙HTTP

举报
Jack20 发表于 2026/06/28 21:05:55 2026/06/28
【摘要】 HarmonyOS开发:网络层迁移——Retrofit/Alamofire到鸿蒙HTTP📌 核心要点:鸿蒙的@kit.NetworkKit提供了原生HTTP能力,虽然没有Retrofit的注解优雅,也没有Alamofire的链式调用丝滑,但配合TypeScript的泛型和装饰器,你能封装出同样好用的网络层。 背景与动机你项目里的网络层是怎么写的?Retrofit + OkHttp?Ala...

HarmonyOS开发:网络层迁移——Retrofit/Alamofire到鸿蒙HTTP

📌 核心要点:鸿蒙的@kit.NetworkKit提供了原生HTTP能力,虽然没有Retrofit的注解优雅,也没有Alamofire的链式调用丝滑,但配合TypeScript的泛型和装饰器,你能封装出同样好用的网络层。

背景与动机

你项目里的网络层是怎么写的?Retrofit + OkHttp?Alamofire + Moya?还是Axios?

不管你用哪个,网络层迁移都是绕不过去的坎。你的API定义、请求拦截器、序列化/反序列化、错误处理、证书校验——这些逻辑都要重新实现。

鸿蒙没有Retrofit,没有Alamofire,没有Axios。它有的是@kit.NetworkKit——一个功能完整但API风格完全不同的HTTP客户端。

你可能会觉得鸿蒙的HTTP API太"原始"了——没有注解定义接口,没有自动序列化,没有优雅的链式调用。但别急,用ArkTS的泛型和装饰器,你能封装出和Retrofit一样好用的网络层。

这篇文章,帮你把网络层从"能用"升级到"好用"。

核心原理

网络层架构对比

graph TB
    subgraph Retrofit["Retrofit 架构"]
        R1[注解定义接口] --> R2[动态代理]
        R2 --> R3[OkHttp 发送请求]
        R3 --> R4[Interceptor 拦截器]
        R4 --> R5[Converter 序列化]
        R5 --> R6[Call/Response]
    end
    
    subgraph Alamofire["Alamofire 架构"]
        A1[链式API调用] --> A2[Session 管理]
        A2 --> A3[RequestInterceptor]
        A3 --> A4[URLSession 底层]
        A4 --> A5[Response 序列化]
    end
    
    subgraph HarmonyHTTP["鸿蒙 HTTP 架构"]
        H1[http.createHttp] --> H2[request 方法]
        H2 --> H3[拦截器自定义封装]
        H3 --> H4[系统网络栈]
        H4 --> H5[手动JSON解析]
    end
    
    R4 -.->|概念对应| H3
    R5 -.->|概念对应| H5
    A3 -.->|概念对应| H3
    
    classDef retrofitStyle fill:#3DDC84,stroke:#2E7D32,color:#000,font-weight:bold
    classDef alamofireStyle fill:#F05138,stroke:#C73A25,color:#fff,font-weight:bold
    classDef arkStyle fill:#0A59F7,stroke:#0A3CC7,color:#fff,font-weight:bold
    
    class R1,R2,R3,R4,R5,R6 retrofitStyle
    class A1,A2,A3,A4,A5 alamofireStyle
    class H1,H2,H3,H4,H5 arkStyle

API映射对照

功能 Retrofit Alamofire 鸿蒙HTTP
GET请求 @GET注解 AF.request() http.request(GET)
POST请求 @POST注解 AF.request(.post) http.request(POST)
路径参数 @Path URL插值 手动拼接
查询参数 @Query parameters参数 extraData
请求体 @Body encodable JSON.stringify
请求头 @Header headers参数 header选项
文件上传 @Multipart upload() request(METHOD.POST)
拦截器 Interceptor RequestInterceptor 自定义封装
序列化 Converter ResponseSerializer JSON.parse
超时设置 OkHttp配置 Session配置 connectTimeout
证书校验 SSLSocketFactory ServerTrustManager caPath选项

代码实战

基础用法:Retrofit迁移到鸿蒙HTTP

先看一个典型的Retrofit接口定义:

// Retrofit: API接口定义
interface ApiService {
    @GET("users")
    suspend fun getUsers(@Query("page") page: Int): Response<List<User>>
    
    @GET("users/{id}")
    suspend fun getUserById(@Path("id") userId: String): Response<User>
    
    @POST("users")
    suspend fun createUser(@Body user: User): Response<User>
    
    @PUT("users/{id}")
    suspend fun updateUser(@Path("id") userId: String, @Body user: User): Response<User>
    
    @DELETE("users/{id}")
    suspend fun deleteUser(@Path("id") userId: String): Response<Unit>
}

// 使用
val api = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build()
    .create(ApiService::class.java)

val users = api.getUsers(page = 1)

鸿蒙的基础用法:

// 鸿蒙: 直接使用HTTP API
import { http } from '@kit.NetworkKit'

// 基础GET请求
async function getUsers(page: number): Promise<UserInfo[]> {
  let httpRequest = http.createHttp()
  try {
    let response = await httpRequest.request(
      `https://api.example.com/users?page=${page}`,
      {
        method: http.RequestMethod.GET,
        header: { 'Content-Type': 'application/json' },
        connectTimeout: 15000,
        readTimeout: 15000
      }
    )
    if (response.responseCode === 200) {
      return JSON.parse(response.result as string) as UserInfo[]
    }
    throw new Error(`HTTP ${response.responseCode}`)
  } finally {
    httpRequest.destroy()  // 别忘了销毁!
  }
}

// 基础POST请求
async function createUser(user: UserInfo): Promise<UserInfo> {
  let httpRequest = http.createHttp()
  try {
    let response = await httpRequest.request(
      'https://api.example.com/users',
      {
        method: http.RequestMethod.POST,
        header: { 'Content-Type': 'application/json' },
        extraData: JSON.stringify(user),
        connectTimeout: 15000,
        readTimeout: 15000
      }
    )
    if (response.responseCode === 200 || response.responseCode === 201) {
      return JSON.parse(response.result as string) as UserInfo
    }
    throw new Error(`HTTP ${response.responseCode}`)
  } finally {
    httpRequest.destroy()
  }
}

对比一下:

  • Retrofit用注解定义API,鸿蒙直接写函数
  • Retrofit自动序列化/反序列化,鸿蒙手动JSON.parse/stringify
  • Retrofit自动管理连接,鸿蒙需要手动destroy()

进阶用法:封装Retrofit风格的网络层

手动写每个请求太痛苦了。来封装一个Retrofit风格的网络层:

// 鸿蒙: 封装Retrofit风格的HTTP客户端

// 统一响应格式
interface ApiResponse<T> {
  code: number
  message: string
  data: T
}

// 请求配置
interface RequestConfig {
  url: string
  method: http.RequestMethod
  params?: Record<string, string>       // 查询参数
  data?: Object                         // 请求体
  headers?: Record<string, string>      // 自定义请求头
  timeout?: number                      // 超时时间
}

// 拦截器接口
interface HttpInterceptor {
  onRequest?(config: RequestConfig): RequestConfig    // 请求前拦截
  onResponse?<T>(response: ApiResponse<T>): ApiResponse<T>  // 响应后拦截
  onError?(error: HttpError): HttpError               // 错误拦截
}

// 错误类
class HttpError extends Error {
  code: number
  detail: string
  
  constructor(code: number, message: string, detail: string = '') {
    super(message)
    this.code = code
    this.detail = detail
  }
}

// HTTP客户端封装
class HttpClient {
  private baseUrl: string = ''
  private interceptors: HttpInterceptor[] = []
  private defaultHeaders: Record<string, string> = {
    'Content-Type': 'application/json'
  }
  
  // 设置基础URL
  setBaseUrl(url: string): HttpClient {
    this.baseUrl = url
    return this
  }
  
  // 添加拦截器
  addInterceptor(interceptor: HttpInterceptor): HttpClient {
    this.interceptors.push(interceptor)
    return this
  }
  
  // 设置默认请求头
  setDefaultHeaders(headers: Record<string, string>): HttpClient {
    this.defaultHeaders = { ...this.defaultHeaders, ...headers }
    return this
  }
  
  // 核心请求方法
  async request<T>(config: RequestConfig): Promise<T> {
    // 拼接完整URL
    let fullUrl = this.baseUrl + config.url
    if (config.params) {
      let queryString = Object.entries(config.params)
        .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
        .join('&')
      fullUrl += `?${queryString}`
    }
    
    // 合并请求头
    let headers = { ...this.defaultHeaders, ...config.headers }
    
    // 请求前拦截
    let processedConfig = { ...config, url: fullUrl, headers }
    for (let interceptor of this.interceptors) {
      if (interceptor.onRequest) {
        processedConfig = interceptor.onRequest(processedConfig)
      }
    }
    
    // 发送请求
    let httpRequest = http.createHttp()
    try {
      let response = await httpRequest.request(processedConfig.url, {
        method: processedConfig.method,
        header: processedConfig.headers,
        extraData: processedConfig.data ? JSON.stringify(processedConfig.data) : undefined,
        connectTimeout: processedConfig.timeout || 15000,
        readTimeout: processedConfig.timeout || 15000
      })
      
      // 处理响应
      if (response.responseCode >= 200 && response.responseCode < 300) {
        let result = JSON.parse(response.result as string) as ApiResponse<T>
        
        // 响应后拦截
        for (let interceptor of this.interceptors) {
          if (interceptor.onResponse) {
            result = interceptor.onResponse<T>(result)
          }
        }
        
        if (result.code === 0 || result.code === 200) {
          return result.data
        }
        throw new HttpError(result.code, result.message)
      }
      
      throw new HttpError(response.responseCode, `HTTP Error: ${response.responseCode}`)
    } catch (e) {
      let error = e instanceof HttpError ? e : new HttpError(-1, (e as Error).message)
      
      // 错误拦截
      for (let interceptor of this.interceptors) {
        if (interceptor.onError) {
          error = interceptor.onError(error)
        }
      }
      
      throw error
    } finally {
      httpRequest.destroy()
    }
  }
  
  // 便捷方法
  async get<T>(url: string, params?: Record<string, string>): Promise<T> {
    return this.request<T>({ url, method: http.RequestMethod.GET, params })
  }
  
  async post<T>(url: string, data?: Object): Promise<T> {
    return this.request<T>({ url, method: http.RequestMethod.POST, data })
  }
  
  async put<T>(url: string, data?: Object): Promise<T> {
    return this.request<T>({ url, method: http.RequestMethod.PUT, data })
  }
  
  async delete<T>(url: string): Promise<T> {
    return this.request<T>({ url, method: http.RequestMethod.DELETE })
  }
}

// 创建全局实例
export const apiClient = new HttpClient()
  .setBaseUrl('https://api.example.com/')
  .addInterceptor({
    onRequest: (config) => {
      // 自动添加Token
      let token = AppStorage.get<string>('token') || ''
      if (token) {
        config.headers = { ...config.headers, 'Authorization': `Bearer ${token}` }
      }
      console.info(`[HTTP] ${config.method} ${config.url}`)
      return config
    },
    onError: (error) => {
      if (error.code === 401) {
        // Token过期,跳转登录
        console.warn('Token过期,需要重新登录')
      }
      return error
    }
  })

使用封装后的客户端:

// 定义API服务(类似Retrofit的接口定义)
class ApiService {
  // GET请求
  async getUsers(page: number): Promise<UserInfo[]> {
    return apiClient.get<UserInfo[]>('users', { page: page.toString() })
  }
  
  async getUserById(id: string): Promise<UserInfo> {
    return apiClient.get<UserInfo>(`users/${id}`)
  }
  
  // POST请求
  async createUser(user: UserInfo): Promise<UserInfo> {
    return apiClient.post<UserInfo>('users', user)
  }
  
  // PUT请求
  async updateUser(id: string, user: UserInfo): Promise<UserInfo> {
    return apiClient.put<UserInfo>(`users/${id}`, user)
  }
  
  // DELETE请求
  async deleteUser(id: string): Promise<void> {
    return apiClient.delete<void>(`users/${id}`)
  }
}

export const apiService = new ApiService()

看到了吗?封装后的API调用方式和Retrofit一样简洁:

// 使用
let users = await apiService.getUsers(1)
let user = await apiService.getUserById('123')
let newUser = await apiService.createUser({ name: '张三', email: 'zhang@example.com' })

完整示例:Alamofire迁移到鸿蒙

Alamofire的链式调用很优雅,但鸿蒙的HTTP API不支持链式调用。用Promise封装可以模拟类似体验:

// 鸿蒙: 模拟Alamofire风格的链式调用
class ChainRequest {
  private config: RequestConfig
  private client: HttpClient
  
  constructor(client: HttpClient, config: RequestConfig) {
    this.client = client
    this.config = config
  }
  
  // 添加查询参数
  parameters(params: Record<string, string>): ChainRequest {
    this.config.params = { ...this.config.params, ...params }
    return this
  }
  
  // 添加请求头
  headers(headers: Record<string, string>): ChainRequest {
    this.config.headers = { ...this.config.headers, ...headers }
    return this
  }
  
  // 设置请求体
  body(data: Object): ChainRequest {
    this.config.data = data
    return this
  }
  
  // 设置超时
  timeout(seconds: number): ChainRequest {
    this.config.timeout = seconds * 1000
    return this
  }
  
  // 验证响应码
  validate(): ChainRequest {
    // 在拦截器中处理
    return this
  }
  
  // 发送请求
  async response<T>(): Promise<T> {
    return this.client.request<T>(this.config)
  }
  
  // 发送并解码JSON
  async responseDecodable<T>(): Promise<T> {
    return this.client.request<T>(this.config)
  }
}

// 扩展HttpClient
class AlamofireStyleClient extends HttpClient {
  // 链式请求
  requestChain(method: http.RequestMethod, url: string): ChainRequest {
    return new ChainRequest(this, { url, method })
  }
  
  // 便捷方法
  get(url: string): ChainRequest {
    return this.requestChain(http.RequestMethod.GET, url)
  }
  
  post(url: string): ChainRequest {
    return this.requestChain(http.RequestMethod.POST, url)
  }
  
  put(url: string): ChainRequest {
    return this.requestChain(http.RequestMethod.PUT, url)
  }
  
  delete(url: string): ChainRequest {
    return this.requestChain(http.RequestMethod.DELETE, url)
  }
}

// 使用(类似Alamofire风格)
let client = new AlamofireStyleClient().setBaseUrl('https://api.example.com/')

let users = await client.get('users')
  .parameters({ page: '1', size: '20' })
  .headers({ 'X-Custom-Header': 'value' })
  .timeout(30)
  .response<UserInfo[]>()

let newUser = await client.post('users')
  .body({ name: '张三', email: 'zhang@example.com' })
  .response<UserInfo>()

踩坑与注意事项

坑1:httpRequest必须destroy()

鸿蒙的http.createHttp()创建的请求对象,用完必须调用destroy()释放资源。忘了调?内存泄漏等着你。

// ❌ 错误:忘记destroy
let httpRequest = http.createHttp()
let response = await httpRequest.request(url)
// 没有destroy!

// ✅ 正确:用try/finally确保destroy
let httpRequest = http.createHttp()
try {
  let response = await httpRequest.request(url)
  // 处理响应
} finally {
  httpRequest.destroy()
}

坑2:extraData的类型限制

鸿蒙HTTP的extraData只支持string和Object两种类型。如果你传其他类型,请求会失败。

// ❌ 错误:直接传number
httpRequest.request(url, {
  method: http.RequestMethod.POST,
  extraData: 123  // 不支持!
})

// ✅ 正确:转成JSON字符串
httpRequest.request(url, {
  method: http.RequestMethod.POST,
  extraData: JSON.stringify({ value: 123 })
})

坑3:并发请求限制

鸿蒙对同一域名的并发请求数有限制(通常6个)。如果你同时发起大量请求,后面的请求会排队等待。建议实现请求队列管理。

坑4:HTTPS证书校验

鸿蒙的HTTPS证书校验和Android/iOS配置方式不同。如果你的后端用了自签名证书,需要额外配置:

// 鸿蒙: 自签名证书配置
let response = await httpRequest.request(url, {
  method: http.RequestMethod.GET,
  // 指定CA证书路径
  caPath: '/data/storage/el2/base/files/ca.crt'
})

坑5:网络权限声明

别忘了在module.json5中声明网络权限:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ]
  }
}

HarmonyOS 6适配说明

HarmonyOS 6在网络层有几个重要改进:

  1. HTTP/2支持:@kit.NetworkKit默认支持HTTP/2协议,多路复用减少了连接开销。

  2. WebSocket增强:新增了完整的WebSocket客户端API,支持自动重连和心跳检测。

  3. 请求缓存:新增了HTTP响应缓存能力,离线场景下可以返回缓存数据。

  4. 网络安全配置:新增了network_config.json配置文件,可以声明式配置证书校验规则、域名安全策略等。

  5. 网络状态监听增强:@kit.NetworkKit的connection模块可以更精确地监听网络状态变化,包括WiFi/蜂窝切换、信号强度变化等。

总结

网络层迁移的核心是封装——鸿蒙的原生HTTP API功能完整但不够优雅,用泛型和类封装后,体验不输Retrofit/Alamofire。

维度 学习难度 使用频率 重要程度
基础HTTP请求 ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
请求封装 ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
拦截器实现 ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐
序列化/反序列化 ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
错误处理 ⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐
HTTPS/证书 ⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐

别被鸿蒙HTTP的"原始"API吓到。花半天时间封装一个Retrofit风格的客户端,后续所有API调用都能享受优雅的开发体验。这笔投资绝对值得。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。