本文字数:约3500字 | 预计阅读时间:15分钟
前置知识:建议先学习本系列前四篇,特别是状态管理与数据绑定
实战价值:掌握网络请求,让应用从本地走向云端,实现数据动态更新
系列导航:本文是《鸿蒙开发系列》第5篇,下篇将讲解本地数据存储方案
一、网络请求基础:从本地到云端 在移动应用中,网络请求是实现动态内容、用户交互和数据同步的核心功能。鸿蒙提供了强大的网络能力支持,让我们能够轻松地连接云端服务。
1.1 网络请求的三种方式 HTTP/HTTPS请求:最常用的RESTful API调用方式
WebSocket:实时双向通信,适合聊天、实时数据推送
Socket:底层网络通信,适合自定义协议
二、HTTP请求全解析 2.1 权限配置 在module.json5中添加网络权限:
json
复制
下载
{ "module": { "requestPermissions": [ { "name": "ohos.permission.INTERNET" }, { "name": "ohos.permission.GET_NETWORK_INFO" } ] } } 2.2 基础GET请求 typescript
复制
下载
import http from '@ohos.net.http';
// 创建HTTP请求对象 let httpRequest = http.createHttp();
// GET请求示例 async function getRequest() { try { let response = await httpRequest.request( 'jsonplaceholder.typicode.com/posts/1', { method: http.RequestMethod.GET, connectTimeout: 60000, readTimeout: 60000, header: { 'Content-Type': 'application/json' } } );
console.log('Response Code:', response.responseCode);
console.log('Headers:', response.header);
// 解析响应数据
let result = JSON.parse(response.result.toString());
console.log('Response Data:', result);
return result;
} catch (error) { console.error('GET Request Error:', error); throw error; } } 2.3 POST请求与参数传递 typescript
复制
下载
async function postRequest() { try { let response = await httpRequest.request( 'jsonplaceholder.typicode.com/posts', { method: http.RequestMethod.POST, header: { 'Content-Type': 'application/json' }, extraData: JSON.stringify({ title: 'foo', body: 'bar', userId: 1 }), connectTimeout: 60000, readTimeout: 60000 } );
return JSON.parse(response.result.toString());
} catch (error) { console.error('POST Request Error:', error); throw error; } } 2.4 多类型请求示例 typescript
复制
下载
// PUT请求 - 更新资源
async function putRequest(id: number, data: any) {
let response = await httpRequest.request(
https://jsonplaceholder.typicode.com/posts/${id},
{
method: http.RequestMethod.PUT,
header: {
'Content-Type': 'application/json'
},
extraData: JSON.stringify(data)
}
);
return JSON.parse(response.result.toString());
}
// DELETE请求 - 删除资源
async function deleteRequest(id: number) {
let response = await httpRequest.request(
https://jsonplaceholder.typicode.com/posts/${id},
{
method: http.RequestMethod.DELETE
}
);
return response.responseCode === 200;
}
// 带查询参数的请求
async function getWithParams(params: Record<string, string>) {
let url = 'jsonplaceholder.typicode.com/posts';
let queryString = Object.keys(params)
.map(key => ${encodeURIComponent(key)}=${encodeURIComponent(params[key])})
.join('&');
if (queryString) {
url += ?${queryString};
}
let response = await httpRequest.request( url, { method: http.RequestMethod.GET } );
return JSON.parse(response.result.toString()); } 三、网络请求封装:创建通用HTTP客户端 为了提升代码复用性和可维护性,我们需要封装一个通用的HTTP客户端。
3.1 HTTP客户端类 typescript
复制
下载
import http from '@ohos.net.http';
// 定义响应类型 interface ApiResponse<T = any> { code: number; message: string; data: T; success: boolean; }
// 定义请求配置 interface RequestConfig { url: string; method: http.RequestMethod; headers?: Record<string, string>; params?: Record<string, any>; data?: any; timeout?: number; }
class HttpClient { private httpRequest: http.HttpRequest; private baseURL: string; private defaultHeaders: Record<string, string>;
constructor(baseURL: string = '') { this.httpRequest = http.createHttp(); this.baseURL = baseURL; this.defaultHeaders = { 'Content-Type': 'application/json', 'Accept': 'application/json' }; }
// 设置默认请求头 setDefaultHeaders(headers: Record<string, string>) { this.defaultHeaders = { ...this.defaultHeaders, ...headers }; }
// 处理URL和查询参数
private buildURL(url: string, params?: Record<string, any>): string {
let fullURL = this.baseURL ? ${this.baseURL}${url} : url;
if (params) {
const queryString = Object.keys(params)
.filter(key => params[key] !== undefined && params[key] !== null)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
.join('&');
if (queryString) {
fullURL += `${fullURL.includes('?') ? '&' : '?'}${queryString}`;
}
}
return fullURL;
}
// 发起请求 async request<T = any>(config: RequestConfig): Promise<ApiResponse> { try { // 构建完整URL const fullURL = this.buildURL(config.url, config.params);
// 合并请求头
const headers = {
...this.defaultHeaders,
...config.headers
};
// 发起请求
const response = await this.httpRequest.request(
fullURL,
{
method: config.method,
header: headers,
extraData: config.data ? JSON.stringify(config.data) : undefined,
connectTimeout: config.timeout || 60000,
readTimeout: config.timeout || 60000
}
);
// 处理响应
const responseData = response.result.toString();
let data: any;
try {
data = responseData ? JSON.parse(responseData) : null;
} catch {
data = responseData;
}
// 判断请求是否成功(这里根据业务需求调整)
const isSuccess = response.responseCode >= 200 && response.responseCode < 300;
return {
code: response.responseCode,
message: isSuccess ? '请求成功' : '请求失败',
data: data,
success: isSuccess
};
} catch (error) {
console.error('HTTP Request Error:', error);
return {
code: -1,
message: error.message || '网络请求失败',
data: null as any,
success: false
};
}
}
// 快捷方法 async get<T = any>(url: string, params?: Record<string, any>, headers?: Record<string, string>) { return this.request({ url, method: http.RequestMethod.GET, params, headers }); }
async post<T = any>(url: string, data?: any, headers?: Record<string, string>) { return this.request({ url, method: http.RequestMethod.POST, data, headers }); }
async put<T = any>(url: string, data?: any, headers?: Record<string, string>) { return this.request({ url, method: http.RequestMethod.PUT, data, headers }); }
async delete<T = any>(url: string, params?: Record<string, any>, headers?: Record<string, string>) { return this.request({ url, method: http.RequestMethod.DELETE, params, headers }); } }
// 创建全局实例 export const httpClient = new HttpClient('jsonplaceholder.typicode.com'); 3.2 请求拦截与响应拦截 typescript
复制
下载
class EnhancedHttpClient extends HttpClient { private requestInterceptors: Array<(config: RequestConfig) => RequestConfig> = []; private responseInterceptors: Array<(response: ApiResponse) => ApiResponse> = [];
// 添加请求拦截器 addRequestInterceptor(interceptor: (config: RequestConfig) => RequestConfig) { this.requestInterceptors.push(interceptor); }
// 添加响应拦截器 addResponseInterceptor(interceptor: (response: ApiResponse) => ApiResponse) { this.responseInterceptors.push(interceptor); }
// 重写请求方法 async request<T = any>(config: RequestConfig): Promise<ApiResponse> { // 执行请求拦截器 let processedConfig = { ...config }; for (const interceptor of this.requestInterceptors) { processedConfig = interceptor(processedConfig); }
// 发起请求
const response = await super.request<T>(processedConfig);
// 执行响应拦截器
let processedResponse = { ...response };
for (const interceptor of this.responseInterceptors) {
processedResponse = interceptor(processedResponse);
}
return processedResponse;
} }
// 使用示例 const enhancedClient = new EnhancedHttpClient();
// 添加请求拦截器(添加认证token)
enhancedClient.addRequestInterceptor((config) => {
const token = localStorage.get('token'); // 假设有localStorage
if (token) {
config.headers = {
...config.headers,
'Authorization': Bearer ${token}
};
}
return config;
});
// 添加响应拦截器(统一错误处理) enhancedClient.addResponseInterceptor((response) => { if (response.code === 401) { // token过期,跳转到登录页 router.push('/login'); }
if (!response.success) { // 统一错误提示 showToast(response.message); }
return response; }); 四、数据处理与类型安全 4.1 定义数据类型 typescript
复制
下载
// 用户类型 interface User { id: number; name: string; email: string; phone: string; website: string; company: { name: string; catchPhrase: string; bs: string; }; }
// 文章类型 interface Post { id: number; userId: number; title: string; body: string; }
// 评论类型 interface Comment { id: number; postId: number; name: string; email: string; body: string; } 4.2 API服务层 typescript
复制
下载
// api/user.ts import { httpClient } from '../utils/HttpClient';
export class UserApi { // 获取用户列表 static async getUsers(): Promise<User[]> { const response = await httpClient.get<User[]>('/users'); return response.success ? response.data : []; }
// 获取单个用户
static async getUser(id: number): Promise<User | null> {
const response = await httpClient.get(/users/${id});
return response.success ? response.data : null;
}
// 创建用户 static async createUser(user: Omit<User, 'id'>): Promise<User | null> { const response = await httpClient.post('/users', user); return response.success ? response.data : null; }
// 更新用户
static async updateUser(id: number, user: Partial): Promise<User | null> {
const response = await httpClient.put(/users/${id}, user);
return response.success ? response.data : null;
}
// 删除用户
static async deleteUser(id: number): Promise {
const response = await httpClient.delete(/users/${id});
return response.success;
}
}
// api/post.ts export class PostApi { // 获取文章列表 static async getPosts(params?: { userId?: number; page?: number; limit?: number; }): Promise<Post[]> { const response = await httpClient.get<Post[]>('/posts', params); return response.success ? response.data : []; }
// 获取用户的所有文章 static async getUserPosts(userId: number): Promise<Post[]> { return this.getPosts({ userId }); }
// 获取文章的评论
static async getPostComments(postId: number): Promise<Comment[]> {
const response = await httpClient.get<Comment[]>(/posts/${postId}/comments);
return response.success ? response.data : [];
}
}
五、状态管理与网络请求结合
5.1 创建数据存储
typescript
复制
下载
// store/UserStore.ts import { UserApi } from '../api/UserApi'; import { PostApi } from '../api/PostApi'; import { User, Post, Comment } from '../models';
class UserStore { // 用户列表 private _users: User[] = []; private _currentUser: User | null = null; private _userPosts: Map<number, Post[]> = new Map(); private _loading: boolean = false; private _error: string | null = null;
// 获取用户列表 async loadUsers() { this._loading = true; this._error = null;
try {
this._users = await UserApi.getUsers();
} catch (error) {
this._error = error.message;
console.error('加载用户失败:', error);
} finally {
this._loading = false;
}
}
// 获取用户详情 async loadUser(id: number) { this._loading = true; this._error = null;
try {
this._currentUser = await UserApi.getUser(id);
} catch (error) {
this._error = error.message;
console.error('加载用户详情失败:', error);
} finally {
this._loading = false;
}
}
// 获取用户的文章 async loadUserPosts(userId: number) { this._loading = true;
try {
const posts = await PostApi.getUserPosts(userId);
this._userPosts.set(userId, posts);
} catch (error) {
console.error('加载用户文章失败:', error);
} finally {
this._loading = false;
}
}
// 获取用户文章(带缓存) async getUserPosts(userId: number): Promise<Post[]> { // 先检查缓存 if (this._userPosts.has(userId)) { return this._userPosts.get(userId)!; }
// 没有缓存则请求网络
await this.loadUserPosts(userId);
return this._userPosts.get(userId) || [];
}
// Getter方法 get users(): User[] { return this._users; }
get currentUser(): User | null { return this._currentUser; }
get loading(): boolean { return this._loading; }
get error(): string | null { return this._error; } }
export const userStore = new UserStore(); 5.2 在组件中使用 typescript
复制
下载
@Entry @Component struct UserListPage { @State users: User[] = []; @State loading: boolean = false; @State error: string | null = null;
aboutToAppear() { this.loadUsers(); }
async loadUsers() { this.loading = true; this.error = null;
try {
this.users = await UserApi.getUsers();
} catch (error) {
this.error = error.message;
console.error('加载用户列表失败:', error);
} finally {
this.loading = false;
}
}
build() { Column({ space: 20 }) { // 标题 Text('用户列表') .fontSize(24) .fontWeight(FontWeight.Bold)
// 加载状态
if (this.loading) {
LoadingProgress()
.width(50)
.height(50)
Text('加载中...')
.fontSize(14)
.fontColor('#999999')
}
// 错误提示
if (this.error) {
Column({ space: 10 }) {
Image($r('app.media.error'))
.width(60)
.height(60)
Text(this.error)
.fontSize(14)
.fontColor('#FF3B30')
Button('重试')
.width(120)
.height(40)
.onClick(() => {
this.loadUsers();
})
}
.padding(40)
}
// 用户列表
if (!this.loading && !this.error) {
List({ space: 12 }) {
ForEach(this.users, (user: User) => {
ListItem() {
UserItem({ user: user })
}
})
}
.layoutWeight(1)
}
// 刷新按钮
Button('刷新数据')
.width(200)
.height(50)
.margin({ top: 20 })
.onClick(() => {
this.loadUsers();
})
}
.width('100%')
.height('100%')
.padding(20)
} }
@Component struct UserItem { @Prop user: User;
build() { Row({ space: 15 }) { // 头像(使用首字母) Column() { Text(this.user.name.charAt(0).toUpperCase()) .fontSize(20) .fontColor(Color.White) } .width(50) .height(50) .backgroundColor('#007DFF') .borderRadius(25) .justifyContent(FlexAlign.Center)
// 用户信息
Column({ space: 5 }) {
Text(this.user.name)
.fontSize(16)
.fontWeight(FontWeight.Bold)
Text(this.user.email)
.fontSize(14)
.fontColor('#666666')
Text(this.user.company.name)
.fontSize(12)
.fontColor('#999999')
}
.alignItems(HorizontalAlign.Start)
.layoutWeight(1)
}
.width('100%')
.padding(15)
.backgroundColor(Color.White)
.borderRadius(12)
.shadow({ radius: 4, color: '#10000000' })
} } 六、网络状态检测 6.1 检测网络状态 typescript
复制
下载
import network from '@ohos.net.network';
class NetworkManager { private netHandle: network.NetHandle;
constructor() { this.netHandle = network.getDefaultNet(); }
// 检查网络是否可用 async isNetworkAvailable(): Promise { try { const netCapabilities = await this.netHandle.getNetCapabilities(); return netCapabilities.hasCapability(network.NetCap.NET_CAPABILITY_INTERNET); } catch (error) { console.error('检查网络状态失败:', error); return false; } }
// 获取网络类型 async getNetworkType(): Promise { try { const netCapabilities = await this.netHandle.getNetCapabilities();
if (netCapabilities.hasCapability(network.NetCap.NET_CAPABILITY_WIFI)) {
return 'WiFi';
} else if (netCapabilities.hasCapability(network.NetCap.NET_CAPABILITY_CELLULAR)) {
return '移动网络';
} else if (netCapabilities.hasCapability(network.NetCap.NET_CAPABILITY_ETHERNET)) {
return '有线网络';
} else {
return '未知网络';
}
} catch (error) {
return '未知';
}
}
// 监听网络状态变化 subscribeToNetworkChanges(callback: (isConnected: boolean) => void) { network.on('netAvailable', () => { callback(true); });
network.on('netCapabilitiesChange', () => {
this.isNetworkAvailable().then(callback);
});
network.on('netUnavailable', () => {
callback(false);
});
} }
export const networkManager = new NetworkManager(); 6.2 在应用中使用网络检测 typescript
复制
下载
@Entry @Component struct NetworkAwareApp { @State isOnline: boolean = true; @State networkType: string = '未知';
aboutToAppear() { // 检查初始网络状态 this.checkNetworkStatus();
// 监听网络状态变化
networkManager.subscribeToNetworkChanges((connected) => {
this.isOnline = connected;
this.checkNetworkStatus();
});
}
async checkNetworkStatus() { this.isOnline = await networkManager.isNetworkAvailable(); this.networkType = await networkManager.getNetworkType(); }
build() { Column({ space: 20 }) { // 网络状态指示器 Row({ space: 10 }) { Image(this.isOnline ? r('app.media.offline')) .width(24) .height(24)
Text(this.isOnline ? `在线 (${this.networkType})` : '离线')
.fontSize(14)
.fontColor(this.isOnline ? '#34C759' : '#FF3B30')
}
.padding(10)
.backgroundColor('#F5F5F5')
.borderRadius(8)
// 主要内容
if (this.isOnline) {
UserListPage()
} else {
Column({ space: 20 }) {
Image($r('app.media.offline'))
.width(120)
.height(120)
Text('网络连接已断开')
.fontSize(18)
.fontWeight(FontWeight.Bold)
Text('请检查您的网络连接后重试')
.fontSize(14)
.fontColor('#666666')
Button('重试连接')
.width(200)
.height(50)
.onClick(() => {
this.checkNetworkStatus();
})
}
.padding(40)
}
}
.width('100%')
.height('100%')
} } 七、性能优化与缓存策略 7.1 内存缓存 typescript
复制
下载
class MemoryCache { private cache: Map<string, { data: any, timestamp: number }> = new Map(); private defaultTTL: number = 5 * 60 * 1000; // 5分钟
// 设置缓存 set(key: string, data: any, ttl?: number) { this.cache.set(key, { data, timestamp: Date.now() + (ttl || this.defaultTTL) }); }
// 获取缓存 get<T = any>(key: string): T | null { const item = this.cache.get(key);
if (!item) return null;
// 检查是否过期
if (Date.now() > item.timestamp) {
this.cache.delete(key);
return null;
}
return item.data;
}
// 删除缓存 delete(key: string) { this.cache.delete(key); }
// 清空缓存 clear() { this.cache.clear(); } }
// 使用缓存的HTTP客户端 class CachedHttpClient extends HttpClient { private cache = new MemoryCache();
async getWithCache<T = any>(
url: string,
params?: Record<string, any>,
ttl?: number
): Promise<T | null> {
// 生成缓存key
const cacheKey = ${url}:${JSON.stringify(params || {})};
// 尝试从缓存获取
const cachedData = this.cache.get<T>(cacheKey);
if (cachedData) {
console.log('从缓存获取数据:', url);
return cachedData;
}
// 缓存未命中,发起网络请求
const response = await this.get<T>(url, params);
if (response.success && response.data) {
// 缓存数据
this.cache.set(cacheKey, response.data, ttl);
}
return response.data;
} } 7.2 图片加载优化 typescript
复制
下载
@Component struct OptimizedImage { @Prop src: string; @State isLoading: boolean = true; @State hasError: boolean = false; @State loadedImage: PixelMap | null = null;
// 图片缓存 private static imageCache: Map<string, PixelMap> = new Map();
aboutToAppear() { this.loadImage(); }
async loadImage() { // 检查缓存 if (OptimizedImage.imageCache.has(this.src)) { this.loadedImage = OptimizedImage.imageCache.get(this.src)!; this.isLoading = false; return; }
this.isLoading = true;
this.hasError = false;
try {
// 创建HTTP请求
const httpRequest = http.createHttp();
const response = await httpRequest.request(
this.src,
{
method: http.RequestMethod.GET,
expectDataType: http.HttpDataType.IMAGE
}
);
// 获取图片数据
this.loadedImage = response.result as PixelMap;
// 缓存图片
OptimizedImage.imageCache.set(this.src, this.loadedImage);
} catch (error) {
this.hasError = true;
console.error('图片加载失败:', error);
} finally {
this.isLoading = false;
}
}
build() { Stack() { // 加载中显示占位符 if (this.isLoading) { Column() .width('100%') .height('100%') .backgroundColor('#F5F5F5') }
// 显示图片
if (this.loadedImage && !this.isLoading) {
Image(this.loadedImage)
.width('100%')
.height('100%')
.objectFit(ImageFit.Cover)
}
// 加载失败显示错误图标
if (this.hasError) {
Image($r('app.media.error'))
.width(40)
.height(40)
}
}
} } 八、错误处理与重试机制 8.1 智能重试机制 typescript
复制
下载
class RetryableHttpClient extends HttpClient { private maxRetries: number = 3; private retryDelay: number = 1000; // 1秒
setRetryConfig(maxRetries: number, retryDelay: number) { this.maxRetries = maxRetries; this.retryDelay = retryDelay; }
async requestWithRetry<T = any>(config: RequestConfig): Promise<ApiResponse> { let lastError: Error | null = null;
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
try {
const response = await super.request<T>(config);
// 如果请求成功,直接返回
if (response.success) {
return response;
}
// 如果是服务器错误,可能需要重试
if (response.code >= 500) {
console.warn(`服务器错误,第${attempt}次重试...`);
lastError = new Error(response.message);
} else {
// 客户端错误,不需要重试
return response;
}
} catch (error) {
console.warn(`请求失败,第${attempt}次重试...`, error);
lastError = error;
}
// 如果不是最后一次尝试,等待一段时间后重试
if (attempt < this.maxRetries) {
await this.delay(this.retryDelay * attempt); // 指数退避
}
}
// 所有重试都失败了
return {
code: -1,
message: lastError?.message || '请求失败',
data: null as any,
success: false
};
}
private delay(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } } 九、实战:新闻客户端实现 typescript
复制
下载
@Entry @Component struct NewsApp { @State articles: any[] = []; @State loading: boolean = false; @State currentCategory: string = 'technology';
private httpClient = new HttpClient('newsapi.org/v2');
aboutToAppear() { this.loadNews(); }
async loadNews() { this.loading = true;
try {
const response = await this.httpClient.get('/everything', {
q: this.currentCategory,
apiKey: 'YOUR_API_KEY', // 实际使用时替换为你的API Key
pageSize: 20,
language: 'zh'
});
if (response.success) {
this.articles = response.data.articles || [];
}
} catch (error) {
console.error('加载新闻失败:', error);
} finally {
this.loading = false;
}
}
build() { Column() { // 分类标签 Scroll(.horizontal) { Row({ space: 15 }) { ForEach(['technology', 'business', 'sports', 'health', 'science'], (category: string) => { Text(this.getCategoryName(category)) .fontSize(16) .fontColor(this.currentCategory === category ? '#007DFF' : '#333333') .fontWeight(this.currentCategory === category ? FontWeight.Bold : FontWeight.Normal) .padding({ left: 15, right: 15, top: 8, bottom: 8 }) .backgroundColor(this.currentCategory === category ? '#E6F2FF' : '#F5F5F5') .borderRadius(20) .onClick(() => { this.currentCategory = category; this.loadNews(); }) } ) } .padding({ left: 16, right: 16, top: 12, bottom: 12 }) } .scrollBar(BarState.Off)
// 新闻列表
if (this.loading) {
LoadingProgress()
.width(50)
.height(50)
.margin({ top: 100 })
} else {
List({ space: 12 }) {
ForEach(this.articles, (article: any, index: number) => {
ListItem() {
NewsItem({ article: article })
}
})
}
.layoutWeight(1)
}
}
.width('100%')
.height('100%')
}
private getCategoryName(category: string): string { const names: Record<string, string> = { 'technology': '科技', 'business': '商业', 'sports': '体育', 'health': '健康', 'science': '科学' }; return names[category] || category; } }
@Component struct NewsItem { @Prop article: any;
build() { Column({ space: 10 }) { // 图片 if (this.article.urlToImage) { OptimizedImage({ src: this.article.urlToImage }) .width('100%') .height(200) .objectFit(ImageFit.Cover) .borderRadius(12) }
// 标题
Text(this.article.title)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
// 描述
Text(this.article.description)
.fontSize(14)
.fontColor('#666666')
.maxLines(3)
.textOverflow({ overflow: TextOverflow.Ellipsis })
// 底部信息
Row() {
Text(this.article.source?.name || '未知来源')
.fontSize(12)
.fontColor('#999999')
Blank()
Text(this.formatDate(this.article.publishedAt))
.fontSize(12)
.fontColor('#999999')
}
.width('100%')
}
.padding(16)
.backgroundColor(Color.White)
.borderRadius(12)
.shadow({ radius: 4, color: '#10000000' })
}
private formatDate(dateString: string): string { try { const date = new Date(dateString); return date.toLocaleDateString('zh-CN'); } catch { return dateString; } } } 十、总结与下期预告 10.1 本文要点回顾 HTTP请求基础:GET、POST、PUT、DELETE请求的使用
HTTP客户端封装:创建可复用的HTTP工具类
数据处理:类型安全的数据解析和API服务层
状态管理结合:网络请求与UI状态同步
网络检测:实时监控网络状态变化
性能优化:缓存策略和图片加载优化
错误处理:智能重试机制和统一错误处理
实战案例:完整的新闻客户端实现
10.2 下期预告:《鸿蒙开发之:本地数据存储方案》 下篇文章将深入讲解:
Preferences轻量级存储
关系型数据库的使用
文件操作与序列化
数据加密与安全存储
实战:实现应用的离线模式
动手挑战 任务1:天气预报应用 要求:
使用和风天气API或其他天气API
显示当前天气、温度、湿度、风速等信息
支持城市搜索和切换
实现天气数据缓存,减少网络请求
任务2:图片浏览应用 要求:
使用Unsplash API获取高质量图片
实现图片懒加载和无限滚动
支持图片收藏功能(本地存储)
实现图片分享功能
任务3:API封装优化 要求:
实现请求取消功能(防止重复请求)
实现请求优先级控制
实现离线队列(离线时保存请求,网络恢复后自动发送)
实现请求统计和性能监控
将你的代码分享到评论区,我会挑选优秀实现进行详细点评!
常见问题解答 Q:如何处理HTTPS证书问题? A:鸿蒙默认信任合法CA颁发的证书。如果是自签名证书,需要在config.json中配置网络安全配置。
Q:如何上传文件? A:使用FormData形式上传:
typescript
复制
下载
const formData = new FormData(); formData.append('file', fileObject); formData.append('name', 'filename');
await httpClient.post('/upload', formData, { 'Content-Type': 'multipart/form-data' }); Q:如何处理大文件下载? A:使用分片下载和断点续传,可以通过设置range头部实现。
Q:如何防止API密钥泄露? A:不要在客户端代码中硬编码API密钥,应该通过后端服务中转请求,或者使用动态令牌。
PS:现在HarmonyOS应用开发者认证正在做活动,初级和高级都可以免费学习及考试,赶快加入班级学习啦:【注意,考试只能从此唯一链接进入】 developer.huawei.com/consumer/cn…