跳到主要内容鸿蒙 APP 开发实战:网络请求与数据持久化 | 极客日志TypeScript大前端
鸿蒙 APP 开发实战:网络请求与数据持久化
综述由AI生成鸿蒙 APP 开发中的网络请求与数据持久化技术。内容包括鸿蒙官方 HTTP 请求库的配置与封装,实现了 GET/POST 请求、拦截器及错误处理机制。通过商品服务和用户服务的代码示例,展示了如何调用 API 并处理响应数据。此外,文章还涵盖了网络状态检测的实现,以及利用 Preferences 进行轻量级键值对存储和利用 SQLite 进行结构化数据持久化的具体实践。最后提供了加载动画与错误界面的组件实现,帮助开发者提升应用的用户体验。
无尘24 浏览 鸿蒙 APP 开发:网络请求与数据持久化

核心内容
本文介绍鸿蒙应用开发中的网络通信与本地数据存储技术,涵盖官方 HTTP 请求库的使用、Preferences 轻量级存储及 SQLite 关系型数据库的集成方案。
主要内容:
- 掌握鸿蒙官方 HTTP 请求库的配置与使用;
- 实现商品列表与用户信息的网络请求交互;
- 理解数据持久化原理,使用 Preferences 与 SQLite 存储本地数据;
- 优化网络请求体验,包括加载动画、错误处理及网络状态检测。
一、网络请求基础
1.1 鸿蒙 HTTP 请求库
HarmonyOS Next 提供官方 HTTP 请求库,支持 HTTP/HTTPS 协议,功能包括发送 GET/POST/PUT/DELETE 请求、设置请求头与参数、文件上传下载、拦截器配置以及超时重试机制。
1.2 实战:HTTP 请求库配置
1. 配置网络权限
在 entry/src/main/module.json5 中添加网络权限:
{
"module": {
"requestPermissions": [
{"name": "ohos.permission.INTERNET"},
{"name": "ohos.permission.READ_NETWORK_STATE"},
{"name": "ohos.permission.WRITE_NETWORK_STATE"}
]
}
}
2. 网络请求工具类
创建 entry/src/main/ets/utils/HttpUtils.ets:
import http from '@ohos.net.http';
const HTTP_CONFIG = {
baseUrl: 'https://api.example.com',
timeout: 10000,
retryCount: 3
};
export class HttpUtils {
private static instance: HttpUtils | null = null;
private httpRequest: http.HttpRequest | null = null;
static getInstance(): HttpUtils {
if (!HttpUtils.instance) {
HttpUtils.instance = new HttpUtils();
}
return HttpUtils.instance;
}
private initRequest(): void {
if (!this.httpRequest) {
this.httpRequest = http.createHttp();
}
}
async get<T>(url: string, params?: Record<string, any>): Promise<T> {
this.initRequest();
const fullUrl = this.buildUrl(url, params);
const response = await this.httpRequest!.request(fullUrl, {
method: http.RequestMethod.GET,
header: { 'Content-Type': 'application/json' },
connectTimeout: HTTP_CONFIG.timeout,
readTimeout: HTTP_CONFIG.timeout
});
return this.handleResponse<T>(response);
}
async post<T>(url: string, data?: Record<string, any>): Promise<T> {
this.initRequest();
const fullUrl = this.buildUrl(url);
const response = await this.httpRequest!.request(fullUrl, {
method: http.RequestMethod.POST,
header: { 'Content-Type': 'application/json' },
extraData: data,
connectTimeout: HTTP_CONFIG.timeout,
readTimeout: HTTP_CONFIG.timeout
});
return this.handleResponse<T>(response);
}
private buildUrl(url: string, params?: Record<string, any>): string {
let fullUrl = `${HTTP_CONFIG.baseUrl}${url}`;
if (params) {
const queryString = Object.keys(params).map(key => `${key}=${encodeURIComponent(params[key])}`).join('&');
fullUrl += `?${queryString}`;
}
return fullUrl;
}
private async handleResponse<T>(response: http.HttpResponse): Promise<T> {
if (response.responseCode === 200) {
const result = JSON.parse(response.result.toString());
if (result.code === 200) {
return result.data;
} else {
throw new Error(result.message || '请求失败');
}
} else {
throw new Error(`HTTP 错误:${response.responseCode}`);
}
}
}
二、网络请求实战
2.1 业务服务实现
1. 商品服务
创建 entry/src/main/ets/services/ProductService.ets:
import { HttpUtils } from '../utils/HttpUtils';
import { GoodsModel } from '../models/HomeModel';
export interface ProductResponseModel {
code: number;
message: string;
data: Array<GoodsModel>;
}
export class ProductService {
private static instance: ProductService | null = null;
static getInstance(): ProductService {
if (!ProductService.instance) {
ProductService.instance = new ProductService();
}
return ProductService.instance;
}
async getProductList(): Promise<Array<GoodsModel>> {
const response = await HttpUtils.getInstance().get<Array<GoodsModel>>('/api/products');
return response;
}
async getProductDetail(id: number): Promise<GoodsModel> {
const response = await HttpUtils.getInstance().get<GoodsModel>(`/api/products/${id}`);
return response;
}
}
2. 用户服务
创建 entry/src/main/ets/services/UserService.ets:
import { HttpUtils } from '../utils/HttpUtils';
export interface LoginRequestModel {
username: string;
password: string;
}
export interface LoginResponseModel {
code: number;
message: string;
data: { token: string; userInfo: UserInfoModel; };
}
export interface UserInfoModel {
id: number;
username: string;
avatar: string;
nickname: string;
email: string;
phone: string;
gender: number;
birthday: string;
}
export class UserService {
private static instance: UserService | null = null;
static getInstance(): UserService {
if (!UserService.instance) {
UserService.instance = new UserService();
}
return UserService.instance;
}
async login(request: LoginRequestModel): Promise<LoginResponseModel['data']> {
const response = await HttpUtils.getInstance().post<LoginResponseModel['data']>('/api/login', request);
return response;
}
async register(request: LoginRequestModel): Promise<LoginResponseModel['data']> {
const response = await HttpUtils.getInstance().post<LoginResponseModel['data']>('/api/register', request);
return response;
}
async getUserInfo(): Promise<UserInfoModel> {
const response = await HttpUtils.getInstance().get<UserInfoModel>('/api/user/info');
return response;
}
}
2.2 网络状态检测
1. 网络状态服务
创建 entry/src/main/ets/services/NetworkService.ets:
import network from '@ohos.net.network';
export type NetworkType = 'wifi' | 'cellular' | 'none';
export class NetworkService {
private static instance: NetworkService | null = null;
static getInstance(): NetworkService {
if (!NetworkService.instance) {
NetworkService.instance = new NetworkService();
}
return NetworkService.instance;
}
getCurrentNetworkType(): NetworkType {
const networkType = network.getNetworkTypeSync();
switch (networkType) {
case network.NetworkType.WIFI:
return 'wifi';
case network.NetworkType.CELLULAR:
return 'cellular';
default:
return 'none';
}
}
onNetworkTypeChange(callback: (type: NetworkType) => void): void {
network.on('netAvailable', () => {
callback(this.getCurrentNetworkType());
});
network.on('netUnavailable', () => {
callback('none');
});
}
}
2. 网络状态组件
创建 entry/src/main/ets/components/NetworkStatusComponent.ets:
import { NetworkService } from '../services/NetworkService';
@Component
export struct NetworkStatusComponent {
@State networkType: NetworkService.NetworkType = 'wifi';
build() {
Row({ space: 8 }) {
Image(this.networkType === 'wifi' ? $r('app.media.wifi') : this.networkType === 'cellular' ? $r('app.media.cellular') : $r('app.media.none'))
.width(20)
.height(20)
.objectFit(ImageFit.Contain);
Text(this.networkType === 'wifi' ? 'WiFi' : this.networkType === 'cellular' ? '移动网络' : '无网络')
.fontSize(14)
.textColor(this.networkType === 'none' ? '#FF0000' : '#666666');
}
.width('auto')
.height('auto')
.padding(8, 16, 8, 16)
.backgroundColor(this.networkType === 'none' ? '#FFE0E0' : '#F5F5F5')
.borderRadius(20)
.visible(this.networkType === 'none');
}
aboutToAppear() {
this.networkType = NetworkService.getInstance().getCurrentNetworkType();
NetworkService.getInstance().onNetworkTypeChange((type: NetworkService.NetworkType) => {
this.networkType = type;
});
}
}
三、数据持久化实战
3.1 数据持久化分类
HarmonyOS Next 提供三种主要数据持久化方式:
| 方式 | 功能 | 适用场景 |
|---|
| Preferences | 轻量级存储,支持键值对存储 | 用户设置、应用配置、购物车数据等小体积数据 |
| SQLite | 关系型数据库,支持复杂查询 | 商品列表、用户信息、订单数据等结构化数据 |
| Distributed Data | 分布式存储,支持跨设备数据同步 | 超级终端多设备协同场景 |
3.2 实战:Preferences 存储
1. Preferences 工具类
创建 entry/src/main/ets/utils/PreferencesUtils.ets:
import preferences from '@ohos.data.preferences';
import { UIAbilityContext } from '@ohos.abilityAccessCtrl';
export class PreferencesUtils {
private static instance: PreferencesUtils | null = null;
private dataPreferences: preferences.Preferences | null = null;
static getInstance(): PreferencesUtils {
if (!PreferencesUtils.instance) {
PreferencesUtils.instance = new PreferencesUtils();
}
return PreferencesUtils.instance;
}
async init(context: UIAbilityContext): Promise<void> {
if (!this.dataPreferences) {
this.dataPreferences = await preferences.getPreferences(context, 'app_preferences');
}
}
get<T>(key: string, defaultValue: T): T {
if (!this.dataPreferences) {
return defaultValue;
}
return this.dataPreferences.getSync(key, defaultValue) as T;
}
async put<T>(key: string, value: T): Promise<void> {
if (!this.dataPreferences) {
return;
}
await this.dataPreferences.put(key, value);
await this.dataPreferences.flush();
}
async delete(key: string): Promise<void> {
if (!this.dataPreferences) {
return;
}
await this.dataPreferences.delete(key);
await this.dataPreferences.flush();
}
async clear(): Promise<void> {
if (!this.dataPreferences) {
return;
}
const keys = this.dataPreferences.keys();
for (const key of keys) {
await this.dataPreferences.delete(key);
}
await this.dataPreferences.flush();
}
}
2. Preferences 应用
修改 entry/src/main/ets/services/UserService.ets:
import { HttpUtils } from '../utils/HttpUtils';
import { PreferencesUtils } from '../utils/PreferencesUtils';
export class UserService {
async login(request: LoginRequestModel): Promise<LoginResponseModel['data']> {
const response = await HttpUtils.getInstance().post<LoginResponseModel['data']>('/api/login', request);
await PreferencesUtils.getInstance().put('token', response.token);
return response;
}
async getUserInfo(): Promise<UserInfoModel> {
const token = PreferencesUtils.getInstance().get<string>('token', '');
HttpUtils.getInstance().addHeader('Authorization', `Bearer ${token}`);
const response = await HttpUtils.getInstance().get<UserInfoModel>('/api/user/info');
return response;
}
}
3.3 实战:SQLite 存储
1. SQLite 数据库管理类
创建 entry/src/main/ets/utils/SQLiteUtils.ets:
import relationalStore from '@ohos.data.relationalStore';
import { UIAbilityContext } from '@ohos.abilityAccessCtrl';
const DB_CONFIG = {
name: 'app_db.db',
securityLevel: relationalStore.SecurityLevel.S1
};
export class SQLiteUtils {
private static instance: SQLiteUtils | null = null;
private rdbStore: relationalStore.RdbStore | null = null;
static getInstance(): SQLiteUtils {
if (!SQLiteUtils.instance) {
SQLiteUtils.instance = new SQLiteUtils();
}
return SQLiteUtils.instance;
}
async init(context: UIAbilityContext): Promise<void> {
if (!this.rdbStore) {
this.rdbStore = await relationalStore.getRdbStore(context, DB_CONFIG);
await this.createUserTable();
await this.createProductTable();
await this.createCartTable();
}
}
private async createUserTable(): Promise<void> {
const sql = ` CREATE TABLE IF NOT EXISTS user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
avatar TEXT,
nickname TEXT,
email TEXT,
phone TEXT,
gender INTEGER,
birthday TEXT
) `;
await this.rdbStore!.executeSql(sql);
}
private async createProductTable(): Promise<void> {
const sql = ` CREATE TABLE IF NOT EXISTS product (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
imageUrl TEXT,
price REAL,
originalPrice REAL,
sales INTEGER
) `;
await this.rdbStore!.executeSql(sql);
}
private async createCartTable(): Promise<void> {
const sql = ` CREATE TABLE IF NOT EXISTS cart (
id INTEGER PRIMARY KEY AUTOINCREMENT,
productId INTEGER NOT NULL,
name TEXT NOT NULL,
imageUrl TEXT,
price REAL,
count INTEGER,
isChecked INTEGER
) `;
await this.rdbStore!.executeSql(sql);
}
async query(sql: string, args?: Array<string>): Promise<Array<relationalStore.ResultSet>> {
if (!this.rdbStore) {
return [];
}
const resultSet = await this.rdbStore.querySql(sql, args);
const data: Array<relationalStore.ResultSet> = [];
if (resultSet.goToFirstRow()) {
do {
data.push(resultSet);
} while (resultSet.goToNextRow());
}
return data;
}
async insert(tableName: string, values: Record<string, any>): Promise<number> {
if (!this.rdbStore) {
return -1;
}
const rowId = await this.rdbStore.insert(tableName, values);
return rowId;
}
async update(tableName: string, values: Record<string, any>, whereClause?: string, whereArgs?: Array<string>): Promise<number> {
if (!this.rdbStore) {
return 0;
}
const count = await this.rdbStore.update(tableName, values, whereClause, whereArgs);
return count;
}
async delete(tableName: string, whereClause?: string, whereArgs?: Array<string>): Promise<number> {
if (!this.rdbStore) {
return 0;
}
const count = await this.rdbStore.delete(tableName, whereClause, whereArgs);
return count;
}
}
2. SQLite 应用
修改 entry/src/main/ets/services/ProductService.ets:
import { HttpUtils } from '../utils/HttpUtils';
import { GoodsModel } from '../models/HomeModel';
import { SQLiteUtils } from '../utils/SQLiteUtils';
export class ProductService {
async getProductList(): Promise<Array<GoodsModel>> {
const localData = await this.getProductListFromLocal();
if (localData.length > 0) {
return localData;
}
const remoteData = await this.getProductListFromRemote();
await this.saveProductListToLocal(remoteData);
return remoteData;
}
private async getProductListFromLocal(): Promise<Array<GoodsModel>> {
const sql = 'SELECT * FROM product';
const resultSet = await SQLiteUtils.getInstance().query(sql);
const data: Array<GoodsModel> = [];
resultSet.forEach(row => {
data.push({
id: row.getDouble(row.getColumnIndex('id')),
name: row.getString(row.getColumnIndex('name')),
imageUrl: row.getString(row.getColumnIndex('imageUrl')),
price: row.getDouble(row.getColumnIndex('price')),
originalPrice: row.getDouble(row.getColumnIndex('originalPrice')),
sales: row.getDouble(row.getColumnIndex('sales'))
});
});
return data;
}
private async getProductListFromRemote(): Promise<Array<GoodsModel>> {
const response = await HttpUtils.getInstance().get<Array<GoodsModel>>('/api/products');
return response;
}
private async saveProductListToLocal(data: Array<GoodsModel>): Promise<void> {
for (const item of data) {
await SQLiteUtils.getInstance().insert('product', {
id: item.id,
name: item.name,
imageUrl: item.imageUrl,
price: item.price,
originalPrice: item.originalPrice,
sales: item.sales
});
}
}
}
四、用户体验优化
4.1 加载动画
创建 entry/src/main/ets/components/LoadingComponent.ets:
@Component
export struct LoadingComponent {
@Prop isLoading: boolean = false;
build() {
Column({ space: 16 }) {
Progress({ value: 0, total: 100, type: ProgressType.ScaleRing })
.width(60)
.height(60)
.style({ strokeWidth: 5, scaleCount: 12, duration: 1000 });
Text('加载中...').fontSize(14).textColor('#666666');
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.visible(this.isLoading);
}
}
4.2 错误处理
创建 entry/src/main/ets/components/ErrorComponent.ets:
@Component
export struct ErrorComponent {
@Prop isError: boolean = false;
@Prop errorMessage: string = '请求失败';
onRetry: () => void;
build() {
Column({ space: 16 }) {
Image($r('app.media.error')).width(80).height(80).objectFit(ImageFit.Contain);
Text(this.errorMessage).fontSize(14).textColor('#666666');
Button('重试').width(120).height(48).backgroundColor('#007DFF').onClick(() => this.onRetry());
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.visible(this.isError);
}
}
五、项目运行与效果验证
5.1 项目配置
1. 配置 API 地址
在 entry/src/main/ets/utils/HttpUtils.ets 中替换为你的真实 API 地址:
const HTTP_CONFIG = {
baseUrl: 'https://your-api-address.com',
};
2. 初始化数据持久化
在 entry/src/main/ets/entryability/EntryAbility.ts 中初始化 Preferences 和 SQLite:
import { PreferencesUtils } from '../utils/PreferencesUtils';
import { SQLiteUtils } from '../utils/SQLiteUtils';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
PreferencesUtils.getInstance().init(this.context);
SQLiteUtils.getInstance().init(this.context);
}
}
5.2 项目运行
- 在 DevEco Studio 中点击「Run」按钮;
- 选择调试设备,点击「OK」;
- 等待编译安装完成,应用会自动在设备上启动。
- 商品列表网络请求:从真实 API 获取商品数据,支持本地缓存;
- 用户信息网络请求:实现用户登录、注册、个人信息查询;
- 网络状态检测:在无网络时显示网络状态提示;
- 加载动画:商品列表加载时显示环形进度条;
- 错误处理:请求失败时显示错误信息与重试按钮。
相关免费在线工具
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
- JSON 压缩
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
- JSON美化和格式化
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online