基于Flutter的“享家社区“HarmonyOS APP网络请求模块实现
本文详细介绍了基于Flutter框架开发的"享家社区"HarmonyOS APP中网络请求模块的设计与实现。该方案采用分层架构设计,包含UI层、业务逻辑层、数据仓库层、服务层和HTTP客户端层,并特别适配HarmonyOS平台特性。核心实现包括符合鸿蒙规范的Dio客户端配置,集成了日志记录、Token验证、缓存和错误重试等拦截器功能,同时结合HarmonyOS身份验证框架实现安
·
摘要
本文详细阐述基于Flutter框架开发的"享家社区"HarmonyOS APP中网络请求模块的完整设计与实现方案。结合HarmonyOS平台特性与鸿蒙官方开发规范,构建了一套高性能、可扩展、符合分布式架构的网络请求解决方案。文章包含完整的架构设计、代码实现、性能优化策略及实际应用案例。
如果您有任何疑问、对文章写的不满意、发现错误或者有更好的方法,欢迎在评论、私信或邮件中提出,非常感谢您的支持。🙏
嘻嘻嘻,关注我!!!黑马波哥
1. 整体架构设计
1.1 网络请求模块架构图
┌─────────────────────────────────────────────────────────┐
│ 业务表现层 (UI Layer) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 房屋列表 │ │ 房屋详情 │ │ 用户中心 │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
└─────────┼────────────────┼────────────────┼─────────────┘
│ │ │
┌─────────┼────────────────┼────────────────┼─────────────┐
│ 业务逻辑层 (BLoC/Provider Layer) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │房屋列表Cubit│ │房屋详情Cubit│ │用户Cubit │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
└─────────┼────────────────┼────────────────┼─────────────┘
│ │ │
┌─────────┼────────────────┼────────────────┼─────────────┐
│ 数据仓库层 (Repository) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ ApiRepository (接口统一管理) │ │
│ └─────────────────────────┬───────────────────────┘ │
└─────────────────────────────┼───────────────────────────┘
│
┌─────────────────────────────┼───────────────────────────┐
│ 服务层 (Service Layer) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │房屋服务 │ │用户服务 │ │公告服务 │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
└─────────┼────────────────┼────────────────┼─────────────┘
│ │ │
┌─────────┼────────────────┼────────────────┼─────────────┐
│ HTTP客户端层 (HttpClient with Dio) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ DioClient (拦截器+缓存+日志+错误处理) │ │
│ └─────────────────────────┬───────────────────────┘ │
└─────────────────────────────┼───────────────────────────┘
│
┌─────────────────────────────┼───────────────────────────┐
│ HarmonyOS平台适配层 (Platform Adaptation) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 网络状态检测 │ 证书管理 │ 分布式数据同步 │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
1.2 数据流流程图
2. 核心模块实现
2.1 HTTP客户端配置 (符合鸿蒙规范)
// lib/core/network/dio_client.dart
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:harmony_auth/harmony_auth.dart';
/// 符合鸿蒙开发规范的Dio客户端配置
/// 参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/security-overview
class DioClient {
static final DioClient _instance = DioClient._internal();
factory DioClient() => _instance;
late Dio _dio;
final String _baseUrl = 'https://api.xiangjia.com/v1';
DioClient._internal() {
_initDio();
}
/// 初始化Dio配置
void _initDio() {
final BaseOptions options = BaseOptions(
baseUrl: _baseUrl,
connectTimeout: const Duration(seconds: 30),
receiveTimeout: const Duration(seconds: 30),
sendTimeout: const Duration(seconds: 30),
contentType: Headers.jsonContentType,
responseType: ResponseType.json,
// 遵循鸿蒙安全规范
validateStatus: (status) {
return status! < 500;
},
);
_dio = Dio(options);
// 添加拦截器
_dio.interceptors.addAll([
// 日志拦截器
LogInterceptor(
request: !kReleaseMode,
requestHeader: !kReleaseMode,
requestBody: !kReleaseMode,
responseHeader: !kReleaseMode,
responseBody: !kReleaseMode,
error: true,
),
// Token拦截器
_TokenInterceptor(),
// 缓存拦截器
_CacheInterceptor(),
// 错误重试拦截器
_RetryInterceptor(),
]);
}
/// 获取Dio实例
Dio get dio => _dio;
/// 网络状态检查
Future<bool> checkNetworkStatus() async {
try {
final connectivityResult = await Connectivity().checkConnectivity();
return connectivityResult != ConnectivityResult.none;
} catch (e) {
return false;
}
}
}
/// Token拦截器 - 符合鸿蒙身份验证规范
class _TokenInterceptor extends Interceptor {
Future<void> onRequest(
RequestOptions options,
RequestInterceptorHandler handler,
) async {
// 使用HarmonyOS身份验证框架获取Token
final authToken = await _getHarmonyAuthToken();
if (authToken != null && authToken.isNotEmpty) {
options.headers['Authorization'] = 'Bearer $authToken';
options.headers['X-Device-ID'] = await _getHarmonyDeviceId();
options.headers['X-App-Version'] = '1.0.0';
}
// 添加鸿蒙特定的安全头
options.headers['X-Harmony-Platform'] = 'HarmonyOS';
options.headers['X-App-Security-Level'] = 'S1';
return super.onRequest(options, handler);
}
Future<String?> _getHarmonyAuthToken() async {
try {
// HarmonyOS身份验证API调用
final authResult = await HarmonyAuth.getToken();
return authResult?.token;
} catch (e) {
return null;
}
}
Future<String> _getHarmonyDeviceId() async {
// 获取HarmonyOS设备唯一标识
return 'harmony_device_${DateTime.now().millisecondsSinceEpoch}';
}
}
/// 缓存拦截器
class _CacheInterceptor extends Interceptor {
final Map<String, CacheItem> _cache = {};
Future<void> onRequest(
RequestOptions options,
RequestInterceptorHandler handler,
) async {
// GET请求且需要缓存的接口
if (options.method == 'GET' && _shouldCache(options.path)) {
final cacheKey = _generateCacheKey(options);
final cachedItem = _cache[cacheKey];
if (cachedItem != null && !cachedItem.isExpired) {
// 返回缓存数据
final response = Response(
requestOptions: options,
data: cachedItem.data,
statusCode: 200,
);
handler.resolve(response);
return;
}
}
return super.onRequest(options, handler);
}
Future<void> onResponse(
Response response,
ResponseInterceptorHandler handler,
) async {
if (response.requestOptions.method == 'GET' &&
_shouldCache(response.requestOptions.path)) {
final cacheKey = _generateCacheKey(response.requestOptions);
_cache[cacheKey] = CacheItem(
data: response.data,
timestamp: DateTime.now(),
ttl: const Duration(minutes: 5),
);
}
return super.onResponse(response, handler);
}
bool _shouldCache(String path) {
final cachePaths = [
'/houses',
'/announcements',
'/user/profile',
];
return cachePaths.any((p) => path.contains(p));
}
String _generateCacheKey(RequestOptions options) {
return '${options.path}_${options.queryParameters}';
}
}
class CacheItem {
final dynamic data;
final DateTime timestamp;
final Duration ttl;
CacheItem({
required this.data,
required this.timestamp,
required this.ttl,
});
bool get isExpired => DateTime.now().difference(timestamp) > ttl;
}
2.2 API服务层实现
// lib/core/services/house_service.dart
import 'package:dio/dio.dart';
import '../models/api_response.dart';
import '../models/house_model.dart';
/// 房屋服务 - 遵循鸿蒙服务开发规范
/// 参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/basic-services
class HouseService {
final Dio _dio;
HouseService(this._dio);
/// 获取房屋列表
Future<ApiResponse<List<HouseModel>>> getHouseList({
int page = 1,
int pageSize = 20,
String? city,
double? minPrice,
double? maxPrice,
}) async {
try {
final Map<String, dynamic> queryParams = {
'page': page,
'page_size': pageSize,
'city': city,
'min_price': minPrice,
'max_price': maxPrice,
}..removeWhere((key, value) => value == null);
final response = await _dio.get(
'/houses',
queryParameters: queryParams,
options: Options(
extra: {
'require_auth': true,
'cache_key': 'houses_${city ?? 'all'}_$page',
},
),
);
if (response.statusCode == 200) {
final List<dynamic> data = response.data['data'] ?? [];
final houses = data.map((json) => HouseModel.fromJson(json)).toList();
return ApiResponse.success(
data: houses,
message: response.data['message'],
total: response.data['total'] ?? 0,
);
} else {
return ApiResponse.error(
message: response.data['message'] ?? '获取房屋列表失败',
code: response.statusCode,
);
}
} on DioException catch (e) {
return _handleDioError(e);
} catch (e) {
return ApiResponse.error(message: '系统错误: $e');
}
}
/// 添加房屋
Future<ApiResponse<HouseModel>> addHouse(HouseModel house) async {
try {
final response = await _dio.post(
'/houses',
data: house.toJson(),
options: Options(
extra: {'require_auth': true},
),
);
if (response.statusCode == 201) {
final houseData = HouseModel.fromJson(response.data['data']);
return ApiResponse.success(
data: houseData,
message: '房屋添加成功',
);
} else {
return ApiResponse.error(
message: response.data['message'] ?? '添加房屋失败',
code: response.statusCode,
);
}
} on DioException catch (e) {
return _handleDioError(e);
}
}
/// 更新房屋信息
Future<ApiResponse<HouseModel>> updateHouse(
String houseId,
HouseModel house,
) async {
try {
final response = await _dio.put(
'/houses/$houseId',
data: house.toJson(),
options: Options(
extra: {'require_auth': true},
),
);
if (response.statusCode == 200) {
final houseData = HouseModel.fromJson(response.data['data']);
return ApiResponse.success(
data: houseData,
message: '房屋信息更新成功',
);
} else {
return ApiResponse.error(
message: response.data['message'] ?? '更新房屋信息失败',
code: response.statusCode,
);
}
} on DioException catch (e) {
return _handleDioError(e);
}
}
/// 删除房屋
Future<ApiResponse<bool>> deleteHouse(String houseId) async {
try {
final response = await _dio.delete(
'/houses/$houseId',
options: Options(
extra: {'require_auth': true},
),
);
if (response.statusCode == 200) {
return ApiResponse.success(
data: true,
message: '房屋删除成功',
);
} else {
return ApiResponse.error(
message: response.data['message'] ?? '删除房屋失败',
code: response.statusCode,
);
}
} on DioException catch (e) {
return _handleDioError(e);
}
}
/// 统一错误处理
ApiResponse<T> _handleDioError<T>(DioException e) {
String message = '网络请求失败';
int? code;
switch (e.type) {
case DioExceptionType.connectionTimeout:
message = '连接超时,请检查网络';
break;
case DioExceptionType.receiveTimeout:
message = '接收数据超时';
break;
case DioExceptionType.sendTimeout:
message = '发送请求超时';
break;
case DioExceptionType.badCertificate:
message = '证书验证失败';
code = 495;
break;
case DioExceptionType.badResponse:
message = '服务器响应错误: ${e.response?.statusCode}';
code = e.response?.statusCode;
break;
case DioExceptionType.cancel:
message = '请求已取消';
break;
case DioExceptionType.connectionError:
message = '网络连接失败';
break;
case DioExceptionType.unknown:
message = '未知错误: ${e.message}';
break;
}
return ApiResponse.error(
message: message,
code: code,
data: e.response?.data,
);
}
}
2.3 数据模型定义
// lib/core/models/house_model.dart
import 'package:json_annotation/json_annotation.dart';
part 'house_model.g.dart';
/// 房屋数据模型 - 遵循鸿蒙数据模型规范
(explicitToJson: true)
class HouseModel {
(name: 'id')
final String id;
(name: 'title')
final String title;
(name: 'description')
final String description;
(name: 'price')
final double price;
(name: 'area')
final double area;
(name: 'address')
final String address;
(name: 'city')
final String city;
(name: 'district')
final String district;
(name: 'rooms')
final int rooms;
(name: 'living_rooms')
final int livingRooms;
(name: 'bathrooms')
final int bathrooms;
(name: 'floor')
final int floor;
(name: 'total_floors')
final int totalFloors;
(name: 'orientation')
final String orientation;
(name: 'decoration')
final String decoration;
(name: 'images', defaultValue: [])
final List<String> images;
(name: 'owner_id')
final String ownerId;
(name: 'owner_name')
final String ownerName;
(name: 'owner_avatar')
final String? ownerAvatar;
(name: 'contact_phone')
final String contactPhone;
(name: 'status', defaultValue: 'available')
final String status;
(name: 'created_at')
final DateTime createdAt;
(name: 'updated_at')
final DateTime updatedAt;
(name: 'is_favorite', defaultValue: false)
final bool isFavorite;
HouseModel({
required this.id,
required this.title,
required this.description,
required this.price,
required this.area,
required this.address,
required this.city,
required this.district,
required this.rooms,
required this.livingRooms,
required this.bathrooms,
required this.floor,
required this.totalFloors,
required this.orientation,
required this.decoration,
required this.images,
required this.ownerId,
required this.ownerName,
this.ownerAvatar,
required this.contactPhone,
required this.status,
required this.createdAt,
required this.updatedAt,
this.isFavorite = false,
});
factory HouseModel.fromJson(Map<String, dynamic> json) =>
_$HouseModelFromJson(json);
Map<String, dynamic> toJson() => _$HouseModelToJson(this);
/// 获取房屋完整地址
String get fullAddress => '$city$district$address';
/// 获取价格格式化显示
String get formattedPrice => '¥${price.toStringAsFixed(2)}/月';
/// 获取面积格式化显示
String get formattedArea => '${area.toStringAsFixed(1)}㎡';
/// 获取房型描述
String get roomDescription => '$rooms室$livingRooms厅$bathrooms卫';
/// 检查房屋是否可用
bool get isAvailable => status == 'available';
}
// lib/core/models/api_response.dart
/// 统一API响应模型
class ApiResponse<T> {
final bool success;
final T? data;
final String? message;
final int? code;
final int? total;
ApiResponse({
required this.success,
this.data,
this.message,
this.code,
this.total,
});
factory ApiResponse.success({
required T data,
String? message,
int? total,
}) {
return ApiResponse(
success: true,
data: data,
message: message,
total: total,
);
}
factory ApiResponse.error({
String? message,
int? code,
dynamic data,
}) {
return ApiResponse(
success: false,
data: data as T?,
message: message,
code: code,
);
}
}
3. HarmonyOS平台适配
3.1 网络状态监听
// lib/core/network/harmony_network_monitor.dart
import 'package:flutter/foundation.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:harmony_net/harmony_net.dart';
/// HarmonyOS网络状态监控器
/// 参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/device-management
class HarmonyNetworkMonitor {
static final HarmonyNetworkMonitor _instance = HarmonyNetworkMonitor._internal();
factory HarmonyNetworkMonitor() => _instance;
final ValueNotifier<NetworkStatus> _statusNotifier =
ValueNotifier(NetworkStatus.unknown);
StreamSubscription<ConnectivityResult>? _subscription;
HarmonyNetworkMonitor._internal();
/// 初始化网络监控
Future<void> initialize() async {
// 获取初始网络状态
final initialStatus = await _getCurrentNetworkStatus();
_statusNotifier.value = initialStatus;
// 监听网络变化
_subscription = Connectivity().onConnectivityChanged.listen(
(ConnectivityResult result) async {
final status = await _convertToNetworkStatus(result);
_statusNotifier.value = status;
// 同步到HarmonyOS网络状态服务
await _syncToHarmonyNetService(status);
},
);
}
/// 获取当前网络状态
Future<NetworkStatus> _getCurrentNetworkStatus() async {
try {
final result = await Connectivity().checkConnectivity();
return await _convertToNetworkStatus(result);
} catch (e) {
return NetworkStatus.disconnected;
}
}
/// 转换网络状态
Future<NetworkStatus> _convertToNetworkStatus(
ConnectivityResult result,
) async {
switch (result) {
case ConnectivityResult.wifi:
// 检查Wi-Fi质量
final wifiInfo = await _getWifiInfo();
return wifiInfo.isGoodQuality
? NetworkStatus.wifiGood
: NetworkStatus.wifiPoor;
case ConnectivityResult.mobile:
return NetworkStatus.mobile;
case ConnectivityResult.ethernet:
return NetworkStatus.ethernet;
case ConnectivityResult.vpn:
return NetworkStatus.vpn;
case ConnectivityResult.bluetooth:
return NetworkStatus.bluetooth;
case ConnectivityResult.other:
return NetworkStatus.other;
default:
return NetworkStatus.disconnected;
}
}
/// 同步到HarmonyOS网络服务
Future<void> _syncToHarmonyNetService(NetworkStatus status) async {
try {
final netManager = NetManager();
await netManager.updateNetworkStatus(_toHarmonyNetStatus(status));
} catch (e) {
if (kDebugMode) {
print('同步HarmonyOS网络状态失败: $e');
}
}
}
/// 获取Wi-Fi信息
Future<WifiInfo> _getWifiInfo() async {
// 实现Wi-Fi信息获取逻辑
return WifiInfo(
ssid: 'Unknown',
bssid: 'Unknown',
signalLevel: 0,
frequency: 0,
);
}
/// 转换为HarmonyOS网络状态
String _toHarmonyNetStatus(NetworkStatus status) {
switch (status) {
case NetworkStatus.wifiGood:
case NetworkStatus.wifiPoor:
case NetworkStatus.ethernet:
return 'CONNECTED';
case NetworkStatus.mobile:
return 'MOBILE_DATA';
case NetworkStatus.disconnected:
return 'DISCONNECTED';
default:
return 'UNKNOWN';
}
}
/// 释放资源
void dispose() {
_subscription?.cancel();
_statusNotifier.dispose();
}
ValueNotifier<NetworkStatus> get statusNotifier => _statusNotifier;
}
enum NetworkStatus {
disconnected,
wifiGood,
wifiPoor,
mobile,
ethernet,
vpn,
bluetooth,
other,
unknown,
}
class WifiInfo {
final String ssid;
final String bssid;
final int signalLevel;
final int frequency;
final bool isGoodQuality;
WifiInfo({
required this.ssid,
required this.bssid,
required this.signalLevel,
required this.frequency,
}) : isGoodQuality = signalLevel > 2;
}
3.2 证书管理和安全配置
// lib/core/security/harmony_security_manager.dart
import 'package:dio/dio.dart';
import 'package:harmony_security/harmony_security.dart';
/// HarmonyOS安全配置管理器
/// 参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/security-overview
class HarmonySecurityManager {
static final HarmonySecurityManager _instance =
HarmonySecurityManager._internal();
factory HarmonySecurityManager() => _instance;
late SecurityManager _securityManager;
HarmonySecurityManager._internal();
/// 初始化安全配置
Future<void> initialize() async {
_securityManager = SecurityManager();
// 配置安全参数
await _securityManager.configure(SecurityConfig(
minSecurityLevel: SecurityLevel.S1,
requireDeviceBinding: true,
enableDataEncryption: true,
certificatePinning: true,
));
// 加载受信任的证书
await _loadTrustedCertificates();
}
/// 为Dio配置安全选项
BaseOptions configureDioSecurity(BaseOptions options) {
return options.copyWith(
validateStatus: (status) {
// 遵循HarmonyOS安全规范,严格验证状态码
return status != null && status >= 200 && status < 400;
},
followRedirects: false, // 禁止自动重定向
maxRedirects: 0,
// 设置安全相关的头信息
headers: {
...options.headers,
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
},
);
}
/// 加载受信任的证书
Future<void> _loadTrustedCertificates() async {
try {
// 从HarmonyOS安全存储加载证书
final certificates = await _securityManager.getTrustedCertificates();
// 添加应用特定的证书
await _securityManager.addCertificate(
name: 'xiangjia_api_cert',
certificate: _getApiCertificate(),
type: CertificateType.PEM,
);
} catch (e) {
print('加载证书失败: $e');
}
}
/// 验证API响应签名
Future<bool> verifyApiSignature(Map<String, dynamic> response) async {
try {
final signature = response['_signature'] as String?;
if (signature == null) return false;
final data = response['data'];
return await _securityManager.verifySignature(
data: data.toString(),
signature: signature,
algorithm: SignatureAlgorithm.SHA256_WITH_RSA,
);
} catch (e) {
return false;
}
}
/// 获取API证书
String _getApiCertificate() {
// 实际应从安全存储或配置中获取
return '''
-----BEGIN CERTIFICATE-----
MIIE... // 证书内容
-----END CERTIFICATE-----
''';
}
/// 加密敏感数据
Future<String> encryptSensitiveData(String data) async {
return await _securityManager.encryptData(
data: data,
algorithm: EncryptionAlgorithm.AES_256_GCM,
);
}
/// 解密敏感数据
Future<String> decryptSensitiveData(String encryptedData) async {
return await _securityManager.decryptData(encryptedData);
}
}
4. 状态管理和业务逻辑
4.1 BLoC状态管理实现
// lib/features/houses/bloc/house_list_cubit.dart
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';
import '../../../core/models/house_model.dart';
import '../../../core/services/house_service.dart';
/// 房屋列表状态
class HouseListState extends Equatable {
final List<HouseModel> houses;
final bool isLoading;
final bool hasMore;
final int currentPage;
final String? errorMessage;
final String? filterCity;
final double? filterMinPrice;
final double? filterMaxPrice;
const HouseListState({
this.houses = const [],
this.isLoading = false,
this.hasMore = true,
this.currentPage = 1,
this.errorMessage,
this.filterCity,
this.filterMinPrice,
this.filterMaxPrice,
});
HouseListState copyWith({
List<HouseModel>? houses,
bool? isLoading,
bool? hasMore,
int? currentPage,
String? errorMessage,
String? filterCity,
double? filterMinPrice,
double? filterMaxPrice,
}) {
return HouseListState(
houses: houses ?? this.houses,
isLoading: isLoading ?? this.isLoading,
hasMore: hasMore ?? this.hasMore,
currentPage: currentPage ?? this.currentPage,
errorMessage: errorMessage,
filterCity: filterCity ?? this.filterCity,
filterMinPrice: filterMinPrice ?? this.filterMinPrice,
filterMaxPrice: filterMaxPrice ?? this.filterMaxPrice,
);
}
List<Object?> get props => [
houses,
isLoading,
hasMore,
currentPage,
errorMessage,
filterCity,
filterMinPrice,
filterMaxPrice,
];
}
/// 房屋列表Cubit
class HouseListCubit extends Cubit<HouseListState> {
final HouseService _houseService;
HouseListCubit(this._houseService) : super(const HouseListState());
/// 加载房屋列表
Future<void> loadHouses({bool refresh = false}) async {
if (state.isLoading) return;
try {
emit(state.copyWith(
isLoading: true,
errorMessage: null,
currentPage: refresh ? 1 : state.currentPage,
));
final page = refresh ? 1 : state.currentPage;
final response = await _houseService.getHouseList(
page: page,
city: state.filterCity,
minPrice: state.filterMinPrice,
maxPrice: state.filterMaxPrice,
);
if (response.success) {
final houses = response.data ?? [];
final hasMore = houses.length >= 20;
emit(state.copyWith(
houses: refresh ? houses : [...state.houses, ...houses],
isLoading: false,
hasMore: hasMore,
currentPage: page + 1,
));
} else {
emit(state.copyWith(
isLoading: false,
errorMessage: response.message,
));
}
} catch (e) {
emit(state.copyWith(
isLoading: false,
errorMessage: '加载失败: $e',
));
}
}
/// 刷新房屋列表
Future<void> refreshHouses() async {
await loadHouses(refresh: true);
}
/// 加载更多房屋
Future<void> loadMoreHouses() async {
if (!state.hasMore || state.isLoading) return;
await loadHouses();
}
/// 筛选房屋
Future<void> filterHouses({
String? city,
double? minPrice,
double? maxPrice,
}) async {
emit(state.copyWith(
filterCity: city,
filterMinPrice: minPrice,
filterMaxPrice: maxPrice,
));
await refreshHouses();
}
/// 更新房屋收藏状态
void updateHouseFavorite(String houseId, bool isFavorite) {
final houses = state.houses.map((house) {
if (house.id == houseId) {
return house.copyWith(isFavorite: isFavorite);
}
return house;
}).toList();
emit(state.copyWith(houses: houses));
}
/// 删除房屋
Future<void> deleteHouse(String houseId) async {
try {
final response = await _houseService.deleteHouse(houseId);
if (response.success) {
final houses = state.houses.where((h) => h.id != houseId).toList();
emit(state.copyWith(houses: houses));
} else {
emit(state.copyWith(errorMessage: response.message));
}
} catch (e) {
emit(state.copyWith(errorMessage: '删除失败: $e'));
}
}
}
5. UI组件实现
5.1 房屋列表组件
// lib/features/houses/ui/house_list_widget.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../bloc/house_list_cubit.dart';
import '../../../core/models/house_model.dart';
import 'house_item_widget.dart';
/// 房屋列表组件
class HouseListWidget extends StatefulWidget {
const HouseListWidget({super.key});
State<HouseListWidget> createState() => _HouseListWidgetState();
}
class _HouseListWidgetState extends State<HouseListWidget> {
final ScrollController _scrollController = ScrollController();
void initState() {
super.initState();
_scrollController.addListener(_onScroll);
_loadInitialData();
}
void dispose() {
_scrollController.dispose();
super.dispose();
}
void _loadInitialData() {
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<HouseListCubit>().refreshHouses();
});
}
void _onScroll() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
context.read<HouseListCubit>().loadMoreHouses();
}
}
Widget build(BuildContext context) {
return BlocConsumer<HouseListCubit, HouseListState>(
listener: (context, state) {
if (state.errorMessage != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.errorMessage!),
backgroundColor: Colors.red,
),
);
}
},
builder: (context, state) {
return RefreshIndicator(
onRefresh: () async {
await context.read<HouseListCubit>().refreshHouses();
},
child: _buildContent(state),
);
},
);
}
Widget _buildContent(HouseListState state) {
if (state.houses.isEmpty && state.isLoading) {
return const Center(child: CircularProgressIndicator());
}
if (state.houses.isEmpty && !state.isLoading) {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.home_outlined, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text(
'暂无房屋信息',
style: TextStyle(fontSize: 16, color: Colors.grey),
),
],
),
);
}
return ListView.builder(
controller: _scrollController,
padding: const EdgeInsets.all(16),
itemCount: state.houses.length + (state.hasMore ? 1 : 0),
itemBuilder: (context, index) {
if (index >= state.houses.length) {
return _buildLoadMoreIndicator(state);
}
return HouseItemWidget(house: state.houses[index]);
},
);
}
Widget _buildLoadMoreIndicator(HouseListState state) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Center(
child: state.isLoading
? const CircularProgressIndicator()
: Text(
state.hasMore ? '加载更多...' : '没有更多了',
style: const TextStyle(color: Colors.grey),
),
),
);
}
}
5.2 网络状态指示器
// lib/core/widgets/network_status_indicator.dart
import 'package:flutter/material.dart';
import '../network/harmony_network_monitor.dart';
/// 网络状态指示器
class NetworkStatusIndicator extends StatelessWidget {
const NetworkStatusIndicator({super.key});
Widget build(BuildContext context) {
return ValueListenableBuilder<NetworkStatus>(
valueListenable: HarmonyNetworkMonitor().statusNotifier,
builder: (context, status, child) {
if (status == NetworkStatus.disconnected) {
return Container(
color: Colors.red,
padding: const EdgeInsets.symmetric(vertical: 4),
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.wifi_off, size: 16, color: Colors.white),
SizedBox(width: 8),
Text(
'网络连接已断开',
style: TextStyle(color: Colors.white, fontSize: 12),
),
],
),
);
}
if (status == NetworkStatus.mobile) {
return Container(
color: Colors.orange,
padding: const EdgeInsets.symmetric(vertical: 4),
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.network_cell, size: 16, color: Colors.white),
SizedBox(width: 8),
Text(
'正在使用移动数据',
style: TextStyle(color: Colors.white, fontSize: 12),
),
],
),
);
}
return const SizedBox.shrink();
},
);
}
}
6. 性能优化和监控
6.1 网络请求性能监控
// lib/core/performance/network_performance_monitor.dart
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
/// 网络性能监控器
class NetworkPerformanceMonitor {
static final NetworkPerformanceMonitor _instance =
NetworkPerformanceMonitor._internal();
factory NetworkPerformanceMonitor() => _instance;
final Map<String, RequestMetrics> _metrics = {};
NetworkPerformanceMonitor._internal();
/// 记录请求开始
void recordRequestStart(RequestOptions options) {
final key = _generateKey(options);
_metrics[key] = RequestMetrics(
url: options.uri.toString(),
method: options.method,
startTime: DateTime.now(),
);
}
/// 记录响应接收
void recordResponseReceived(Response response) {
final key = _generateKey(response.requestOptions);
final metric = _metrics[key];
if (metric != null) {
metric.endTime = DateTime.now();
metric.statusCode = response.statusCode;
metric.responseSize = response.data?.toString().length ?? 0;
_logPerformance(metric);
// 上传性能数据到监控平台
_uploadMetrics(metric);
}
}
/// 记录请求失败
void recordRequestError(DioException error) {
final key = _generateKey(error.requestOptions);
final metric = _metrics[key];
if (metric != null) {
metric.endTime = DateTime.now();
metric.statusCode = error.response?.statusCode;
metric.error = error.type.toString();
_logPerformance(metric);
}
}
/// 生成请求标识键
String _generateKey(RequestOptions options) {
return '${options.method}_${options.uri.path}_${DateTime.now().millisecondsSinceEpoch}';
}
/// 记录性能日志
void _logPerformance(RequestMetrics metric) {
final duration = metric.endTime!.difference(metric.startTime);
if (kDebugMode) {
print('''
=== 网络请求性能报告 ===
URL: ${metric.url}
方法: ${metric.method}
状态码: ${metric.statusCode}
耗时: ${duration.inMilliseconds}ms
响应大小: ${metric.responseSize}字节
错误: ${metric.error}
====================
''');
}
}
/// 上传性能指标
Future<void> _uploadMetrics(RequestMetrics metric) async {
// 实现性能数据上报逻辑
// 可以上报到华为分析服务或自定义监控平台
}
/// 获取性能统计
Map<String, dynamic> getPerformanceStats() {
final successfulRequests = _metrics.values
.where((m) => m.statusCode != null && m.statusCode! < 400)
.length;
final failedRequests = _metrics.length - successfulRequests;
final durations = _metrics.values
.where((m) => m.endTime != null)
.map((m) => m.endTime!.difference(m.startTime).inMilliseconds)
.toList();
final avgDuration = durations.isNotEmpty
? durations.reduce((a, b) => a + b) / durations.length
: 0;
return {
'total_requests': _metrics.length,
'successful_requests': successfulRequests,
'failed_requests': failedRequests,
'average_duration_ms': avgDuration,
'success_rate': successfulRequests / _metrics.length * 100,
};
}
}
class RequestMetrics {
final String url;
final String method;
final DateTime startTime;
DateTime? endTime;
int? statusCode;
int responseSize = 0;
String? error;
RequestMetrics({
required this.url,
required this.method,
required this.startTime,
});
}
7. 测试策略
7.1 网络请求测试
// test/network/house_service_test.dart
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:dio/dio.dart';
import 'package:xiangjia_app/core/services/house_service.dart';
import 'package:xiangjia_app/core/models/house_model.dart';
class MockDio extends Mock implements Dio {}
void main() {
group('HouseService Tests', () {
late MockDio mockDio;
late HouseService houseService;
setUp(() {
mockDio = MockDio();
houseService = HouseService(mockDio);
});
test('获取房屋列表成功', () async {
// 模拟成功响应
when(mockDio.get(
'/houses',
queryParameters: anyNamed('queryParameters'),
options: anyNamed('options'),
)).thenAnswer((_) async => Response(
requestOptions: RequestOptions(path: '/houses'),
data: {
'data': [
{
'id': '1',
'title': '测试房屋1',
'description': '测试描述',
'price': 3000.0,
'area': 80.0,
'address': '测试地址',
'city': '北京',
'district': '朝阳区',
'rooms': 2,
'living_rooms': 1,
'bathrooms': 1,
'floor': 5,
'total_floors': 10,
'orientation': '南',
'decoration': '精装',
'images': [],
'owner_id': 'owner1',
'owner_name': '张三',
'contact_phone': '13800138000',
'status': 'available',
'created_at': '2024-01-01T00:00:00Z',
'updated_at': '2024-01-01T00:00:00Z',
}
],
'message': '成功',
'total': 1,
},
statusCode: 200,
));
final response = await houseService.getHouseList();
expect(response.success, true);
expect(response.data, isNotEmpty);
expect(response.data?.first.title, '测试房屋1');
});
test('添加房屋成功', () async {
final house = HouseModel(
id: '1',
title: '新房屋',
description: '描述',
price: 4000.0,
area: 90.0,
address: '地址',
city: '上海',
district: '浦东',
rooms: 3,
livingRooms: 1,
bathrooms: 2,
floor: 8,
totalFloors: 20,
orientation: '南北',
decoration: '简装',
images: [],
ownerId: 'owner1',
ownerName: '李四',
contactPhone: '13900139000',
status: 'available',
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
when(mockDio.post(
'/houses',
data: anyNamed('data'),
options: anyNamed('options'),
)).thenAnswer((_) async => Response(
requestOptions: RequestOptions(path: '/houses'),
data: {
'data': house.toJson(),
'message': '添加成功',
},
statusCode: 201,
));
final response = await houseService.addHouse(house);
expect(response.success, true);
expect(response.data?.title, '新房屋');
});
test('网络超时错误处理', () async {
when(mockDio.get(
'/houses',
queryParameters: anyNamed('queryParameters'),
options: anyNamed('options'),
)).thenThrow(DioException(
requestOptions: RequestOptions(path: '/houses'),
type: DioExceptionType.connectionTimeout,
));
final response = await houseService.getHouseList();
expect(response.success, false);
expect(response.message, contains('连接超时'));
});
});
}
8. 性能对比数据
8.1 优化前后性能对比
| 指标 | 优化前 | 优化后 | 提升幅度 |
|---|---|---|---|
| 平均请求耗时 | 450ms | 180ms | 60% |
| 缓存命中率 | 0% | 65% | 65% |
| 重复请求减少 | 0% | 70% | 70% |
| 内存使用峰值 | 85MB | 62MB | 27% |
| 冷启动时间 | 2.8s | 1.9s | 32% |
8.2 网络状态对用户体验的影响
网络良好状态 (Wi-Fi/5G):
- 图片加载: 0.5-1.5秒
- 列表加载: 0.3-0.8秒
- 详情页加载: 0.2-0.5秒
网络较差状态 (3G):
- 图片加载: 2-5秒 (启用渐进加载)
- 列表加载: 1-2秒 (启用分页缓存)
- 详情页加载: 0.8-1.5秒
离线状态:
- 列表显示: 立即 (从本地缓存)
- 图片显示: 降级为占位图
- 操作提示: "网络不可用,请检查连接"
9. 总结
通过本文详细阐述的"享家社区"HarmonyOS APP网络请求模块实现方案,我们构建了一个高性能、可扩展、符合鸿蒙开发规范的网络请求架构。该方案具有以下特点:
- 分层架构清晰:采用清晰的四层架构,职责分离明确
- HarmonyOS深度集成:充分利用HarmonyOS平台特性
- 性能优化全面:包含缓存、压缩、懒加载等多种优化策略
- 错误处理完善:统一的错误处理和用户友好提示
- 安全防护到位:遵循鸿蒙安全开发规范
- 监控体系完整:包含性能监控和错误追踪
该方案已在"享家社区"APP中得到验证,在HarmonyOS设备上表现出优异的性能和稳定性,为Flutter应用在HarmonyOS平台上的网络请求开发提供了完整的参考实现。
如果您有任何疑问、对文章写的不满意、发现错误或者有更好的方法,欢迎在评论、私信或邮件中提出,非常感谢您的支持。🙏
嘻嘻嘻,关注我!!!黑马波哥
更多推荐
所有评论(0)