鸿蒙应用的网络请求和数据处理:从HTTP到本地缓存的完整方案

网络请求是现代应用的核心功能。无论是获取用户数据、上传文件还是实时通信,都需要与服务器进行网络交互。但网络请求涉及许多复杂的问题:网络延迟、错误处理、数据缓存、离线支持等。

本文将为你讲解如何在鸿蒙应用中优雅地处理网络请求和数据,包括HTTP请求、JSON解析、错误处理、数据缓存等。通过学习这些内容,你将能够构建更加健壮和高效的网络层。

HTTP请求的基础实现

鸿蒙提供了http模块来进行HTTP请求。基础的HTTP请求实现相对简单,但要做好错误处理和超时控制。

基础GET请求

typescript 复制代码
import http from '@ohos.net.http'

async function fetchData(url: string): Promise<string> {
  let httpRequest = http.createHttpRequest()
  
  try {
    await httpRequest.request(url, {
      method: http.RequestMethod.GET,
      connectTimeout: 10000,
      readTimeout: 10000
    })
    
    let result = httpRequest.getResponseBody()
    return result as string
  } catch (error) {
    console.error('Request failed:', error)
    throw error
  } finally {
    httpRequest.destroy()
  }
}

POST请求和请求头

typescript 复制代码
async function postData(url: string, data: object): Promise<string> {
  let httpRequest = http.createHttpRequest()
  
  try {
    await httpRequest.request(url, {
      method: http.RequestMethod.POST,
      header: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer token'
      },
      extraData: JSON.stringify(data),
      connectTimeout: 10000,
      readTimeout: 10000
    })
    
    let result = httpRequest.getResponseBody()
    return result as string
  } catch (error) {
    console.error('Request failed:', error)
    throw error
  } finally {
    httpRequest.destroy()
  }
}

JSON数据解析

从服务器获取的数据通常是JSON格式。正确的JSON解析对于数据处理至关重要。

基础JSON解析

typescript 复制代码
interface User {
  id: number
  name: string
  email: string
}

async function fetchUser(userId: number): Promise<User> {
  let response = await fetchData(`https://api.example.com/users/${userId}`)
  let user = JSON.parse(response) as User
  return user
}

处理复杂的JSON结构

typescript 复制代码
interface Post {
  id: number
  title: string
  content: string
  author: User
  comments: Comment[]
}

interface Comment {
  id: number
  text: string
  author: User
}

async function fetchPost(postId: number): Promise<Post> {
  let response = await fetchData(`https://api.example.com/posts/${postId}`)
  let post = JSON.parse(response) as Post
  return post
}

JSON序列化

typescript 复制代码
function serializeUser(user: User): string {
  return JSON.stringify(user)
}

function serializeUsers(users: User[]): string {
  return JSON.stringify(users, null, 2)
}

错误处理和重试机制

网络请求经常会失败。实现良好的错误处理和重试机制可以提高应用的稳定性。

基础错误处理

typescript 复制代码
enum RequestError {
  NETWORK_ERROR = 'NETWORK_ERROR',
  TIMEOUT = 'TIMEOUT',
  SERVER_ERROR = 'SERVER_ERROR',
  PARSE_ERROR = 'PARSE_ERROR'
}

class RequestException extends Error {
  constructor(
    public code: RequestError,
    message: string
  ) {
    super(message)
  }
}

async function fetchDataWithErrorHandling(url: string): Promise<string> {
  try {
    let httpRequest = http.createHttpRequest()
    await httpRequest.request(url, {
      method: http.RequestMethod.GET,
      connectTimeout: 10000,
      readTimeout: 10000
    })
    
    let statusCode = httpRequest.getResponseCode()
    if (statusCode >= 400) {
      throw new RequestException(
        RequestError.SERVER_ERROR,
        `Server error: ${statusCode}`
      )
    }
    
    return httpRequest.getResponseBody() as string
  } catch (error) {
    if (error instanceof RequestException) {
      throw error
    }
    throw new RequestException(
      RequestError.NETWORK_ERROR,
      'Network request failed'
    )
  }
}

重试机制

typescript 复制代码
async function fetchDataWithRetry(
  url: string,
  maxRetries: number = 3
): Promise<string> {
  let lastError: Error | null = null
  
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fetchDataWithErrorHandling(url)
    } catch (error) {
      lastError = error as Error
      if (i < maxRetries - 1) {
        // 指数退避:等待时间随着重试次数增加而增加
        let delay = Math.pow(2, i) * 1000
        await new Promise(resolve => setTimeout(resolve, delay))
      }
    }
  }
  
  throw lastError
}

数据缓存策略

频繁的网络请求会浪费带宽和电池电量。实现合理的缓存策略可以显著提升应用性能。

内存缓存

typescript 复制代码
class MemoryCache<T> {
  private cache: Map<string, {data: T, timestamp: number}> = new Map()
  private ttl: number // 缓存过期时间(毫秒)
  
  constructor(ttl: number = 5 * 60 * 1000) {
    this.ttl = ttl
  }
  
  set(key: string, data: T): void {
    this.cache.set(key, {
      data,
      timestamp: Date.now()
    })
  }
  
  get(key: string): T | null {
    let item = this.cache.get(key)
    if (!item) {
      return null
    }
    
    if (Date.now() - item.timestamp > this.ttl) {
      this.cache.delete(key)
      return null
    }
    
    return item.data
  }
  
  clear(): void {
    this.cache.clear()
  }
}

let userCache = new MemoryCache<User>(10 * 60 * 1000)

async function fetchUserWithCache(userId: number): Promise<User> {
  let cacheKey = `user_${userId}`
  
  // 检查缓存
  let cachedUser = userCache.get(cacheKey)
  if (cachedUser) {
    return cachedUser
  }
  
  // 从网络获取
  let user = await fetchUser(userId)
  
  // 存入缓存
  userCache.set(cacheKey, user)
  
  return user
}

本地存储缓存

typescript 复制代码
import preferences from '@ohos.data.preferences'

class PersistentCache {
  private preferences: preferences.Preferences | null = null
  
  async init(context: any): Promise<void> {
    this.preferences = await preferences.getPreferences(context, 'app_cache')
  }
  
  async set(key: string, value: string): Promise<void> {
    if (!this.preferences) {
      throw new Error('Cache not initialized')
    }
    await this.preferences.put(key, value)
    await this.preferences.flush()
  }
  
  async get(key: string): Promise<string | null> {
    if (!this.preferences) {
      throw new Error('Cache not initialized')
    }
    let value = await this.preferences.get(key, null)
    return value as string | null
  }
  
  async remove(key: string): Promise<void> {
    if (!this.preferences) {
      throw new Error('Cache not initialized')
    }
    await this.preferences.delete(key)
    await this.preferences.flush()
  }
}

let persistentCache = new PersistentCache()

async function fetchUserWithPersistentCache(userId: number): Promise<User> {
  let cacheKey = `user_${userId}`
  
  // 检查本地缓存
  let cachedData = await persistentCache.get(cacheKey)
  if (cachedData) {
    return JSON.parse(cachedData) as User
  }
  
  // 从网络获取
  let user = await fetchUser(userId)
  
  // 存入本地缓存
  await persistentCache.set(cacheKey, JSON.stringify(user))
  
  return user
}

离线支持

应用应该能够在网络不可用时继续工作。实现离线支持需要合理的缓存策略和用户提示。

检查网络连接

typescript 复制代码
import connection from '@ohos.net.connection'

async function isNetworkAvailable(): Promise<boolean> {
  try {
    let netHandle = await connection.getDefaultNetConnection()
    let netCapabilities = await connection.getNetCapabilities(netHandle)
    return netCapabilities.hasCapability(connection.NetCapsType.NET_CAPABILITY_INTERNET)
  } catch (error) {
    return false
  }
}

async function fetchDataWithOfflineSupport(url: string): Promise<string> {
  let isOnline = await isNetworkAvailable()
  
  if (isOnline) {
    try {
      return await fetchDataWithRetry(url)
    } catch (error) {
      // 网络请求失败,尝试使用缓存
      let cachedData = await persistentCache.get(url)
      if (cachedData) {
        console.warn('Using cached data due to network error')
        return cachedData
      }
      throw error
    }
  } else {
    // 离线模式,使用缓存
    let cachedData = await persistentCache.get(url)
    if (cachedData) {
      return cachedData
    }
    throw new Error('No network connection and no cached data available')
  }
}

最佳实践

使用拦截器统一处理请求

typescript 复制代码
class HttpClient {
  private requestInterceptors: Array<(config: any) => any> = []
  private responseInterceptors: Array<(response: any) => any> = []
  
  addRequestInterceptor(interceptor: (config: any) => any): void {
    this.requestInterceptors.push(interceptor)
  }
  
  addResponseInterceptor(interceptor: (response: any) => any): void {
    this.responseInterceptors.push(interceptor)
  }
  
  async request(url: string, config: any = {}): Promise<any> {
    // 执行请求拦截器
    for (let interceptor of this.requestInterceptors) {
      config = interceptor(config)
    }
    
    // 执行请求
    let response = await fetchDataWithRetry(url)
    
    // 执行响应拦截器
    for (let interceptor of this.responseInterceptors) {
      response = interceptor(response)
    }
    
    return response
  }
}

let httpClient = new HttpClient()

// 添加认证拦截器
httpClient.addRequestInterceptor((config) => {
  config.header = config.header || {}
  config.header['Authorization'] = 'Bearer token'
  return config
})

// 添加日志拦截器
httpClient.addResponseInterceptor((response) => {
  console.log('Response:', response)
  return response
})

分离数据层和UI层

typescript 复制代码
class UserRepository {
  async getUser(userId: number): Promise<User> {
    return await fetchUserWithPersistentCache(userId)
  }
  
  async getUsers(page: number, pageSize: number): Promise<User[]> {
    let url = `https://api.example.com/users?page=${page}&pageSize=${pageSize}`
    let response = await fetchDataWithOfflineSupport(url)
    return JSON.parse(response) as User[]
  }
}

@Component
struct UserList {
  @State users: User[] = []
  @State loading: boolean = false
  @State error: string = ''
  
  private repository = new UserRepository()
  
  async loadUsers() {
    this.loading = true
    try {
      this.users = await this.repository.getUsers(1, 20)
    } catch (error) {
      this.error = (error as Error).message
    } finally {
      this.loading = false
    }
  }
  
  build() {
    Column() {
      if (this.loading) {
        LoadingProgress()
      } else if (this.error) {
        Text(this.error)
      } else {
        List() {
          ForEach(this.users, (user: User) => {
            ListItem() {
              Text(user.name)
            }
          })
        }
      }
    }
  }
}

总结

网络请求和数据处理是应用开发的核心。通过实现良好的错误处理、缓存策略和离线支持,你可以构建更加健壮和高效的应用。

关键要点包括:

  • 使用正确的HTTP方法和请求头
  • 实现完善的错误处理和重试机制
  • 合理使用内存缓存和本地存储缓存
  • 支持离线模式
  • 分离数据层和UI层

现在就在你的项目中应用这些最佳实践吧。如果你有任何问题或想法,欢迎在评论区分享。

相关推荐
m0_738120722 小时前
渗透测试——靶机DC-5详细渗透getshell过程
网络·安全·web安全·ssh·php
kkk_皮蛋2 小时前
带宽估计 BWE (WebRTC 的智能网络优化核心)
网络·webrtc
web Rookie2 小时前
一文分清 TCP、UDP、HTTP:网络协议核心区别
网络协议·tcp/ip·http·udp
真正的醒悟2 小时前
AI中的网络世界
运维·服务器·网络
Watermelo6172 小时前
【前端实战】Axios 错误处理的设计与进阶封装,实现网络层面的数据与状态解耦
前端·javascript·网络·vue.js·网络协议·性能优化·用户体验
Web3VentureView2 小时前
Synbo与Superpool达成合作:共建链上募资早期入口
网络·金融·web3·区块链
chen_x_x2 小时前
域格模组使用http下载到文件系统以及分段下载
网络协议·http·xcode
TG:@yunlaoda360 云老大2 小时前
华为云国际站代理商VPC主要有什么作用呢?
网络·华为云·智能路由器
专业开发者2 小时前
安全防护蓝牙 ® 网状网络代理应用
网络·物联网·安全