Flutter for OpenHarmony 实战:网络请求与JSON解析完全指南

Flutter for OpenHarmony 实战:网络请求与JSON解析完全指南

文章目录

  • [Flutter for OpenHarmony 实战:网络请求与JSON解析完全指南](#Flutter for OpenHarmony 实战:网络请求与JSON解析完全指南)
    • 摘要![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/57885e1e571d47c1b083946177d4636a.png)
    • 一、HTTP网络请求基础
      • [1.1 HTTP协议概述](#1.1 HTTP协议概述)
      • [1.2 RESTful API设计规范](#1.2 RESTful API设计规范)
      • [1.3 异步网络请求的必要性](#1.3 异步网络请求的必要性)
    • 二、http包使用详解
      • [2.1 http包简介](#2.1 http包简介)
      • [2.2 GET请求基础用法](#2.2 GET请求基础用法)
      • [2.3 带参数的GET请求](#2.3 带参数的GET请求)
      • [2.4 响应对象解析](#2.4 响应对象解析)
    • 三、异步编程与错误处理
      • [3.1 Dart异步编程模型](#3.1 Dart异步编程模型)
      • [3.2 并发请求处理](#3.2 并发请求处理)
      • [3.3 错误处理策略](#3.3 错误处理策略)
      • [3.4 超时处理](#3.4 超时处理)
    • 四、JSON数据解析技术
      • [4.1 JSON数据结构分析](#4.1 JSON数据结构分析)
      • [4.2 dart:convert基础使用](#4.2 dart:convert基础使用)
      • [4.3 解析嵌套JSON](#4.3 解析嵌套JSON)
      • [4.4 类型安全访问](#4.4 类型安全访问)
    • 五、数据模型类设计最佳实践
      • [5.1 为什么需要数据模型类](#5.1 为什么需要数据模型类)
      • [5.2 数据模型类设计](#5.2 数据模型类设计)
      • [5.3 使用模型类解析数据](#5.3 使用模型类解析数据)
      • [5.4 列表数据解析](#5.4 列表数据解析)
    • 六、API集成实战案例
      • [6.1 完整的请求封装](#6.1 完整的请求封装)
      • [6.2 在State中使用API](#6.2 在State中使用API)
      • [6.3 加载状态管理](#6.3 加载状态管理)
    • 七、性能优化与缓存策略
      • [7.1 减少不必要的请求](#7.1 减少不必要的请求)
      • [7.2 使用缓存](#7.2 使用缓存)
      • [7.3 请求去重](#7.3 请求去重)
    • 八、总结

摘要

网络请求和数据处理是移动应用开发的核心技能。本文以天气预报应用为例,深入讲解Flutter for OpenHarmony平台上的HTTP网络请求技术、JSON数据解析方法、异步编程模型以及错误处理策略。通过本文学习,读者将掌握Flutter中http包的使用技巧,了解数据模型设计的最佳实践,为开发复杂的网络应用打下坚实基础。


一、HTTP网络请求基础

1.1 HTTP协议概述

HTTP(HyperText Transfer Protocol)是应用层协议,定义了客户端与服务器之间数据传输的规范。

常见HTTP方法

  • GET:获取资源
  • POST:创建资源
  • PUT:更新资源
  • DELETE:删除资源

状态码含义

  • 200:请求成功
  • 400:客户端错误
  • 401:未授权
  • 404:资源未找到
  • 500:服务器错误

1.2 RESTful API设计规范

天气预报应用使用的是RESTful风格的API:

复制代码
基础URL: https://restapi.amap.com/v3/weather/weatherInfo

实时天气请求:
GET /v3/weather/weatherInfo?key=YOUR_KEY&city=110000&extensions=base

预报天气请求:
GET /v3/weather/weatherInfo?key=YOUR_KEY&city=110000&extensions=all

API参数说明

  • key:开发者密钥,用于身份验证
  • city:城市编码(adcode)
  • extensions:数据类型(base=实况,all=预报)

1.3 异步网络请求的必要性

移动应用中的网络请求必须异步执行,原因如下:

UI响应性

  • 网络请求耗时较长(几百毫秒到几秒)
  • 同步请求会阻塞UI线程,导致界面卡顿
  • 用户体验要求操作即时响应

系统稳定性

  • 主线程阻塞可能导致应用无响应(ANR)
  • 现代操作系统禁止在主线程执行网络操作

二、http包使用详解

2.1 http包简介

http是Dart官方推荐的轻量级HTTP客户端库:

主要特性

  • 简洁易用的API设计
  • 支持所有HTTP方法
  • 完善的异步支持
  • 自动处理编码和压缩

添加依赖

yaml 复制代码
dependencies:
  http: ^1.2.0

安装命令:

bash 复制代码
flutter pub get

2.2 GET请求基础用法

dart 复制代码
import 'package:http/http.dart' as http;

// 简单GET请求
Future<void> fetchData() async {
  final url = Uri.parse('https://api.example.com/data');
  final response = await http.get(url);

  if (response.statusCode == 200) {
    print(response.body);
  }
}

2.3 带参数的GET请求

dart 复制代码
// 方式一:URL拼接
final url = Uri.parse(
  'https://restapi.amap.com/v3/weather/weatherInfo?key=$_apiKey&city=$_cityCode&extensions=base'
);

// 方式二:使用queryParameters(推荐)
final url = Uri.https('restapi.amap.com', '/v3/weather/weatherInfo', {
  'key': _apiKey,
  'city': _cityCode,
  'extensions': 'base',
});

使用queryParameters的优势

  • 自动处理特殊字符转义
  • 代码可读性更好
  • 便于动态添加参数

2.4 响应对象解析

http.Response对象包含以下主要属性:

dart 复制代码
class Response {
  // 状态码
  int statusCode;

  // 响应体(字符串)
  String body;

  // 响应头
  Map<String, String> headers;

  // 请求信息
  Request request;
}

使用示例

dart 复制代码
final response = await http.get(url);

// 检查状态码
if (response.statusCode == 200) {
  // 获取响应体
  String data = response.body;

  // 获取响应头
  String contentType = response.headers['content-type'] ?? '';
}

三、异步编程与错误处理

3.1 Dart异步编程模型

Dart使用Future和async/await处理异步操作:

dart 复制代码
// 异步函数定义
Future<void> fetchWeatherData() async {
  // await等待异步操作完成
  final response = await http.get(url);
  // 继续执行后续代码
}

执行流程

3.2 并发请求处理

天气预报需要同时获取实时天气和预报数据:

dart 复制代码
Future<void> _fetchWeatherData() async {
  try {
    // 并发发起两个请求
    final currentResponse = await http.get(
      Uri.parse("$_baseUrl?key=$_apiKey&city=$_selectedCityCode&extensions=base"),
    );

    final forecastResponse = await http.get(
      Uri.parse("$_baseUrl?key=$_apiKey&city=$_selectedCityCode&extensions=all"),
    );

    // 处理响应...
  } catch (e) {
    // 错误处理
  }
}

改进:使用Future.wait并发执行

dart 复制代码
Future<void> _fetchWeatherData() async {
  try {
    // 同时发起两个请求
    final responses = await Future.wait([
      http.get(Uri.parse("$_baseUrl?key=$_apiKey&city=$_selectedCityCode&extensions=base")),
      http.get(Uri.parse("$_baseUrl?key=$_apiKey&city=$_selectedCityCode&extensions=all")),
    ]);

    final currentResponse = responses[0];
    final forecastResponse = responses[1];

    // 处理响应...
  } catch (e) {
    // 错误处理
  }
}

3.3 错误处理策略

dart 复制代码
Future<void> _fetchWeatherData() async {
  setState(() {
    _isLoading = true;
    _errorMessage = null;
  });

  try {
    final response = await http.get(url);

    // HTTP状态码检查
    if (response.statusCode == 200) {
      final data = json.decode(response.body);

      // 业务状态码检查
      if (data['status'] == "1") {
        setState(() {
          _weatherData = parseData(data);
          _isLoading = false;
        });
      } else {
        setState(() {
          _errorMessage = "API返回错误: ${data['info']}";
          _isLoading = false;
        });
      }
    } else {
      setState(() {
        _errorMessage = "HTTP错误: ${response.statusCode}";
        _isLoading = false;
      });
    }
  } catch (e) {
    // 捕获所有异常
    setState(() {
      _errorMessage = "网络请求失败: $e";
      _isLoading = false;
    });
  }
}

错误类型

  • SocketException:网络连接失败
  • HttpException:HTTP解析错误
  • FormatException:JSON格式错误
  • TimeoutException:请求超时

3.4 超时处理

dart 复制代码
import 'dart:async';

Future<void> fetchWeatherWithTimeout() async {
  try {
    final response = await http.get(url).timeout(
      const Duration(seconds: 10),
      onTimeout: () {
        // 超时后的处理
        throw Exception('请求超时,请检查网络连接');
      },
    );
  } catch (e) {
    print('错误: $e');
  }
}

四、JSON数据解析技术

4.1 JSON数据结构分析

高德天气API返回的JSON结构:

json 复制代码
{
  "status": "1",
  "info": "OK",
  "infocode": "10000",
  "lives": [
    {
      "province": "北京",
      "city": "北京市",
      "weather": "晴",
      "temperature": "15",
      "winddirection": "西",
      "windpower": "3级",
      "humidity": "25",
      "reporttime": "2024-01-15 14:00:00"
    }
  ]
}

数据特点

  • 最外层是状态信息
  • lives是一个数组(虽然通常只有一个元素)
  • 实际天气数据在lives[0]中

4.2 dart:convert基础使用

dart 复制代码
import 'dart:convert';

// JSON字符串解析为Dart对象
Map<String, dynamic> data = json.decode(jsonString);

// Dart对象编码为JSON字符串
String jsonString = json.encode(dartObject);

4.3 解析嵌套JSON

dart 复制代码
// 解析响应
final response = await http.get(url);
final data = json.decode(response.body);

// 检查状态
if (data['status'] == "1" && data['lives'] != null) {
  // 获取lives数组
  List<dynamic> lives = data['lives'];

  // 获取第一个元素
  Map<String, dynamic> liveData = lives[0];

  // 提取字段
  String city = liveData['city'] ?? '';
  String weather = liveData['weather'] ?? '';
  String temperature = liveData['temperature'] ?? '';
}

4.4 类型安全访问

dart 复制代码
// 使用null安全操作符
String city = liveData['city'] ?? '未知';
int temperature = int.tryParse(liveData['temperature'] ?? '0') ?? 0;

// 使用??提供默认值
List<dynamic> casts = forecastData['forecasts']?[0]['casts'] as List? ?? [];

五、数据模型类设计最佳实践

5.1 为什么需要数据模型类

原始解析方式的问题

dart 复制代码
// 不好的做法
Text(data['lives'][0]['temperature'])

问题

  • 类型不安全
  • 代码重复
  • 难以维护
  • 没有编译时检查

使用数据模型的优势

dart 复制代码
// 好的做法
Text(weatherData.temperature)

优势

  • 类型安全
  • 代码清晰
  • 便于复用
  • 支持IDE自动补全

5.2 数据模型类设计

dart 复制代码
class WeatherData {
  final String province;
  final String city;
  final String weather;
  final String temperature;
  final String windDirection;
  final String windPower;
  final String humidity;
  final String reportTime;

  WeatherData({
    required this.province,
    required this.city,
    required this.weather,
    required this.temperature,
    required this.windDirection,
    required this.windPower,
    required this.humidity,
    required this.reportTime,
  });

  // 工厂构造函数:从JSON创建实例
  factory WeatherData.fromJson(Map<String, dynamic> json) {
    return WeatherData(
      province: json['province'] ?? '',
      city: json['city'] ?? '',
      weather: json['weather'] ?? '',
      temperature: json['temperature'] ?? '',
      windDirection: json['winddirection'] ?? '',
      windPower: json['windpower'] ?? '',
      humidity: json['humidity'] ?? '',
      reportTime: _formatTime(json['reporttime'] ?? ''),
    );
  }

  // 私有辅助方法
  static String _formatTime(String reportTime) {
    try {
      final dateTime = DateTime.parse(reportTime);
      return '${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
    } catch (e) {
      return reportTime;
    }
  }
}

5.3 使用模型类解析数据

dart 复制代码
// 从API响应创建模型实例
final data = json.decode(response.body);
if (data['status'] == "1") {
  setState(() {
    _currentWeather = WeatherData.fromJson(data['lives'][0]);
  });
}

// 在UI中使用
Text(_currentWeather!.temperature)

5.4 列表数据解析

dart 复制代码
class ForecastData {
  final String date;
  final String dayWeather;
  final String nightTemp;
  final String dayTemp;

  ForecastData({
    required this.date,
    required this.dayWeather,
    required this.nightTemp,
    required this.dayTemp,
  });

  factory ForecastData.fromJson(Map<String, dynamic> json) {
    return ForecastData(
      date: _formatDate(json['date'] ?? ''),
      dayWeather: json['dayweather'] ?? '',
      nightTemp: json['nighttemp'] ?? '',
      dayTemp: json['daytemp'] ?? '',
    );
  }

  static String _formatDate(String dateStr) {
    try {
      final parts = dateStr.split('-');
      if (parts.length == 3) {
        return '${parts[1]}月${parts[2]}日';
      }
      return dateStr;
    } catch (e) {
      return dateStr;
    }
  }
}

// 解析列表数据
final casts = forecastData['forecasts'][0]['casts'] as List;
_forecast = casts.map((e) => ForecastData.fromJson(e)).toList();

六、API集成实战案例

6.1 完整的请求封装

dart 复制代码
class WeatherApiClient {
  static const String _baseUrl = "https://restapi.amap.com/v3/weather/weatherInfo";
  static const String _apiKey = "YOUR_API_KEY";

  // 获取实时天气
  static Future<WeatherData> getCurrentWeather(String cityCode) async {
    final url = Uri.parse("$_baseUrl?key=$_apiKey&city=$cityCode&extensions=base");

    try {
      final response = await http.get(url).timeout(
        const Duration(seconds: 10),
      );

      if (response.statusCode != 200) {
        throw Exception('HTTP错误: ${response.statusCode}');
      }

      final data = json.decode(response.body);

      if (data['status'] != "1") {
        throw Exception('API错误: ${data['info']}');
      }

      return WeatherData.fromJson(data['lives'][0]);
    } catch (e) {
      throw Exception('获取天气失败: $e');
    }
  }

  // 获取天气预报
  static Future<List<ForecastData>> getForecast(String cityCode) async {
    final url = Uri.parse("$_baseUrl?key=$_apiKey&city=$cityCode&extensions=all");

    try {
      final response = await http.get(url).timeout(
        const Duration(seconds: 10),
      );

      if (response.statusCode != 200) {
        throw Exception('HTTP错误: ${response.statusCode}');
      }

      final data = json.decode(response.body);

      if (data['status'] != "1") {
        throw Exception('API错误: ${data['info']}');
      }

      final casts = data['forecasts'][0]['casts'] as List;
      return casts.map((e) => ForecastData.fromJson(e)).toList();
    } catch (e) {
      throw Exception('获取预报失败: $e');
    }
  }
}

6.2 在State中使用API

dart 复制代码
class _WeatherHomePageState extends State<WeatherHomePage> {
  WeatherData? _currentWeather;
  List<ForecastData>? _forecast;
  bool _isLoading = false;
  String? _errorMessage;

  Future<void> _fetchWeatherData() async {
    setState(() {
      _isLoading = true;
      _errorMessage = null;
    });

    try {
      // 并发获取两种数据
      final results = await Future.wait([
        WeatherApiClient.getCurrentWeather(_selectedCityCode),
        WeatherApiClient.getForecast(_selectedCityCode),
      ]);

      setState(() {
        _currentWeather = results[0] as WeatherData;
        _forecast = results[1] as List<ForecastData>;
        _isLoading = false;
      });
    } catch (e) {
      setState(() {
        _errorMessage = e.toString();
        _isLoading = false;
      });
    }
  }
}

6.3 加载状态管理

dart 复制代码
Widget _buildBody() {
  // 加载中
  if (_isLoading) {
    return const Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          CircularProgressIndicator(),
          SizedBox(height: 16),
          Text('正在加载天气数据...'),
        ],
      ),
    );
  }

  // 错误状态
  if (_errorMessage != null) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const Icon(Icons.error_outline, size: 48, color: Colors.red),
          const SizedBox(height: 16),
          Text(_errorMessage!),
          const SizedBox(height: 16),
          ElevatedButton(
            onPressed: _fetchWeatherData,
            child: const Text('重试'),
          ),
        ],
      ),
    );
  }

  // 数据加载成功
  if (_currentWeather == null) {
    return const Center(child: Text('暂无数据'));
  }

  return SingleChildScrollView(
    child: Column(
      children: [
        _buildCurrentWeatherCard(),
        _buildForecastSection(),
      ],
    ),
  );
}

此处插入图片:加载中、错误、成功三种状态的截图


七、性能优化与缓存策略

7.1 减少不必要的请求

添加本地缓存

dart 复制代码
class WeatherCache {
  static final Map<String, CachedData> _cache = {};

  static bool isValid(String key) {
    final cached = _cache[key];
    if (cached == null) return false;

    // 缓存10分钟有效
    final age = DateTime.now().difference(cached.timestamp);
    return age.inMinutes < 10;
  }

  static WeatherData? getWeather(String city) {
    if (!isValid(city)) return null;
    return _cache[city]?.data;
  }

  static void setWeather(String city, WeatherData data) {
    _cache[city] = CachedData(
      data: data,
      timestamp: DateTime.now(),
    );
  }
}

class CachedData {
  final WeatherData data;
  final DateTime timestamp;

  CachedData({required this.data, required this.timestamp});
}

7.2 使用缓存

dart 复制代码
Future<void> _fetchWeatherData() async {
  // 检查缓存
  final cached = WeatherCache.getWeather(_selectedCityCode);
  if (cached != null) {
    setState(() {
      _currentWeather = cached;
      _isLoading = false;
    });
    return;
  }

  // 请求新数据
  try {
    final weather = await WeatherApiClient.getCurrentWeather(_selectedCityCode);
    WeatherCache.setWeather(_selectedCityCode, weather);

    setState(() {
      _currentWeather = weather;
      _isLoading = false;
    });
  } catch (e) {
    // 错误处理
  }
}

7.3 请求去重

防止用户快速切换城市时发起多个请求:

dart 复制代码
class _WeatherHomePageState extends State<WeatherHomePage> {
  String _pendingRequestCity = '';

  Future<void> _fetchWeatherData() async {
    final city = _selectedCityCode;

    // 如果有正在进行的请求,忽略
    if (_pendingRequestCity == city) return;

    _pendingRequestCity = city;

    try {
      final weather = await WeatherApiClient.getCurrentWeather(city);

      // 只处理最新的请求结果
      if (_selectedCityCode == city) {
        setState(() {
          _currentWeather = weather;
        });
      }
    } finally {
      if (_selectedCityCode == city) {
        _pendingRequestCity = '';
      }
    }
  }
}

八、总结

本文深入讲解了Flutter for OpenHarmony平台上的网络请求和JSON解析技术,主要内容包括:

  1. http包使用:掌握GET请求、参数传递、响应处理
  2. 异步编程:理解Future、async/await的工作原理
  3. 错误处理:学会捕获各种异常,提供友好的错误提示
  4. JSON解析:使用dart:convert处理API响应数据
  5. 数据模型:设计类型安全的数据模型类
  6. 性能优化:实现缓存和请求去重提升用户体验

这些技术是开发网络应用的基础,掌握后可以应对大多数API集成场景。通过不断实践,读者可以开发出更复杂、更稳定的网络应用。


欢迎加入开源鸿蒙跨平台社区 : 开源鸿蒙跨平台开发者社区

相关推荐
雪碧聊技术4 小时前
JSON数据格式
json·fastjson
摘星编程4 小时前
React Native鸿蒙:DeviceInfo应用版本读取
react native·react.js·harmonyos
爱吃大芒果4 小时前
Flutter for OpenHarmony实战 : mango_shop API 客户端的封装与鸿蒙网络权限适配
网络·flutter·harmonyos
吠品4 小时前
IP证书-SSL证书申请
网络·网络协议·tcp/ip
四月_h4 小时前
vue2项目集成websocket
网络·websocket·网络协议
想要一只奶牛猫4 小时前
Spring IOC&DI(上)
java·后端·spring
相思难忘成疾4 小时前
华为HCIP:MPLS实验
网络·华为·智能路由器·hcip
2601_949593654 小时前
高级进阶 React Native 鸿蒙跨平台开发:SVG 路径描边动画
react native·react.js·harmonyos
2501_920931704 小时前
React Native鸿蒙跨平台完成剧本杀组队消息与快捷入口组件技术解读,采用左侧图标+中间入口名称+右侧状态标签的设计实现快捷入口组件
javascript·react native·react.js·harmonyos