Flutter安卓天气APP加载天气数据失败报错求助
Flutter天气预报APP安卓运行异常排查
问题现象
- 安卓端运行时屏幕显示错误:
"Exception: Failed to load weather data occurred";
- 终端输出系统级日志:
E/ion(18018): ioctl c0044901 failed with code -1: Invalid argument D/hw-ProcessState(18018): Binder ioctl to enable oneway spam detection failed:
Invalid argument
相关代码
weather_model.dart
class WeatherModel { final String temp; final String city; final String desc; WeatherModel.fromMap(Map<String, dynamic> json) : temp = json['main']['temp'].toString(), city = json['name'], desc = json['weather'][0]['description']; }
call_to_api.dart
import 'dart:convert'; import 'dart:developer'; import 'package:geocoding/geocoding.dart'; import 'package:geolocator/geolocator.dart'; import "package:http/http.dart" as http; import 'package:easyweather/constants/api_key.dart'; import 'package:easyweather/logic/models/weather_model.dart'; class CallToApi { Future<WeatherModel> callWeatherAPi(bool current, String cityName) async { try { Position currentPosition = await getCurrentPosition(); if (current) { List<Placemark> placemarks = await placemarkFromCoordinates( currentPosition.latitude, currentPosition.longitude); Placemark place = placemarks[0]; cityName = place.locality!; } var url = Uri.https('api.openweathermap.org', '/data/2.5/weather', {'q': cityName, "units": "metric", "appid": apiKey}); final http.Response response = await http.get(url); log(response.body.toString()); if (response.statusCode == 200) { final Map<String, dynamic> decodedJson = json.decode(response.body); return WeatherModel.fromMap(decodedJson); } else { throw Exception('Failed to load weather data'); } } catch (e) { throw Exception('Failed to load weather data'); } } Future<Position> getCurrentPosition() async { bool serviceEnabled; LocationPermission permission; serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) { return Future.error('Location services are disabled.'); } permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { return Future.error('Location permissions are denied'); } } if (permission == LocationPermission.deniedForever) { return Future.error( 'Location permissions are permanently denied, we cannot request permissions.'); } return await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.best, ); } }
weather_page.dart
import 'dart:developer'; import 'package:anim_search_bar/anim_search_bar.dart'; import 'package:flutter/material.dart'; import 'package:easyweather/constants/constants.dart'; import 'package:easyweather/logic/models/weather_model.dart'; import 'package:easyweather/logic/services/call_to_api.dart'; class WeatherPage extends StatefulWidget { const WeatherPage({Key? key}) : super(key: key); @override State<WeatherPage> createState() => _WeatherPageState(); } class _WeatherPageState extends State<WeatherPage> { Future<WeatherModel> getData(bool isCurrentCity, String cityName) async { return await CallToApi().callWeatherAPi(isCurrentCity, cityName); } TextEditingController textController = TextEditingController(text: ""); Future<WeatherModel>? _myData; @override void initState() { setState(() { _myData = getData(true, ""); }); super.initState(); } @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: false, backgroundColor: Colors.white, body: FutureBuilder( builder: (ctx, snapshot) { if (snapshot.connectionState == ConnectionState.done) { // If error occured if (snapshot.hasError) { return Center( child: Text( '${snapshot.error.toString()} occurred', style: const TextStyle(fontSize: 18), ), ); // if data has no errors } else if (snapshot.hasData) { // Extracting data from snapshot object final data = snapshot.data as WeatherModel; return Container( padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment(0.8, 1), colors: <Color>[ Color.fromARGB(255, 120, 60, 240), // Rosa Color.fromARGB(255, 30, 144, 255), // Azul Elétrico Color.fromARGB(255, 0, 255, 255), // Ciano Color.fromARGB(255, 128, 0, 128), // Roxo Color.fromARGB(255, 255, 0, 128), // Rosa Choque ], tileMode: TileMode.mirror, ), ), width: double.infinity, height: double.infinity, child: SafeArea( child: Column( children: [ AnimSearchBar( rtl: true, width: 400, color: const Color(0xffffb56b), textController: textController, suffixIcon: const Icon( Icons.search, color: Colors.black, size: 26, ), onSuffixTap: () async { textController.text == "" ? log("No city entered") : setState(() { _myData = getData(false, textController.text); }); FocusScope.of(context).unfocus(); textController.clear(); }, style: f14RblackLetterSpacing2, onSubmitted: (String) {}, ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( data.city, style: f24Rwhitebold, ), height25, Text( data.desc, style: f16PW, ), height25, Text( "${data.temp}°C", style: f42Rwhitebold, ), ], ), ), ], ), ), ); } } else if (snapshot.connectionState == ConnectionState.waiting) { return const Center( child: CircularProgressIndicator(), ); } else { return Center( child: Text("${snapshot.connectionState} occured"), ); } return const Center( child: Text("Server timed out!"), ); }, future: _myData!, ), ); } }
main.dart
import 'package:flutter/material.dart'; import 'package:easyweather/view/weather_page.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Easy Weather', debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.green, ), home: const WeatherPage(), ); } }
排查与修复方案
1. 暴露真实错误信息
当前所有异常被统一包装,无法定位具体问题。修改callWeatherAPi的catch块,打印原始错误:
catch (e) { log('原始错误详情: $e'); throw Exception('Failed to load weather data: ${e.toString()}'); }
运行后查看终端日志,就能知道是定位服务失败、城市名称为空还是API请求报错。
2. 处理位置信息为空的场景
部分地区通过坐标逆地理编码无法获取locality,直接使用!强制解包会触发空指针。修改位置解析逻辑:
if (current) { List<Placemark> placemarks = await placemarkFromCoordinates( currentPosition.latitude, currentPosition.longitude); if (placemarks.isEmpty) { throw Exception('无法获取当前位置的城市信息'); } Placemark place = placemarks[0]; cityName = place.locality ?? place.administrativeArea ?? "Unknown City"; }
3. 增加API返回字段的空安全检查
当API返回异常(比如城市不存在)时,直接访问嵌套字段会崩溃。修改WeatherModel的构造函数:
WeatherModel.fromMap(Map<String, dynamic> json) : temp = (json['main']?['temp'] ?? 0.0).toString(), city = json['name'] ?? "Unknown City", desc = json['weather']?.isNotEmpty == true ? json['weather'][0]['description'] ?? "No description" : "No description";
4. 忽略系统级日志
终端出现的E/ion和Binder错误属于安卓底层系统日志,和业务逻辑无关,不会导致数据加载失败,可以直接忽略。
5. 验证API可用性
手动拼接请求URL(替换你的apiKey)在浏览器中访问,确认API是否正常返回数据:
https://api.openweathermap.org/data/2.5/weather?q=Beijing&units=metric&appid=YOUR_API_KEY
如果浏览器返回正常,说明API Key和请求格式没问题;如果报错,检查Key是否激活、是否超出调用额度。
内容的提问来源于stack exchange,提问作者Jeferson Olivaras
相关产品推荐
相关产品推荐

