从零构建天气空气质量AppAndroid Studio实战指南在移动应用开发领域天气类应用始终占据着重要地位。这类应用不仅实用性强而且涵盖了Android开发的多个核心技术点是初学者进阶的理想项目。本文将带你完整实现一个整合百度地图定位与和风天气数据的空气质量监测应用从环境配置到最终发布每个步骤都配有详细说明和实用技巧。1. 开发环境与项目初始化1.1 Android Studio配置优化在开始项目前确保你的开发环境已经准备就绪。推荐使用最新稳定版的Android Studio并安装以下必备组件Android SDK Platform 31支持大多数现代Android设备Android Emulator用于快速测试应用Kotlin插件虽然本项目使用Java但Kotlin插件有助于代码分析Git Integration方便版本控制提示在Android Studio的SDK Manager中勾选Show Package Details可以查看更详细的SDK版本信息。配置gradle.properties文件添加以下优化参数org.gradle.daemontrue org.gradle.paralleltrue org.gradle.cachingtrue android.enableJetifiertrue1.2 创建新项目选择Empty Activity模板设置项目名称WeatherAirQualityApp包名com.yourdomain.weatherair根据你的域名修改语言选择Java最低API级别设为21覆盖约95%的设备项目创建完成后先进行基础配置// 在AndroidManifest.xml中添加基础权限 uses-permission android:nameandroid.permission.INTERNET / uses-permission android:nameandroid.permission.ACCESS_COARSE_LOCATION / uses-permission android:nameandroid.permission.ACCESS_FINE_LOCATION /2. 百度地图SDK集成与定位实现2.1 百度地图开发者账号申请访问百度地图开放平台(https://lbsyun.baidu.com/)注册开发者账号个人开发者选择个人认证进入控制台创建新应用获取API Key注意保存SHA1和包名信息2.2 SDK集成步骤在项目的build.gradle中添加百度地图仓库allprojects { repositories { google() jcenter() maven { url https://mapapi.bdimg.com/repository/ } } }在app模块的build.gradle中添加依赖dependencies { implementation com.baidu.lbsyun:BaiduMapSDK_Map:7.4.0 implementation com.baidu.lbsyun:BaiduMapSDK_Location:9.1.8 }配置AndroidManifest.xmlapplication meta-data android:namecom.baidu.lbsapi.API_KEY android:value你的API_KEY / /application2.3 定位功能核心实现创建LocationService类处理定位逻辑public class LocationService { private LocationClient mLocationClient; private BDAbstractLocationListener mListener; public interface LocationCallback { void onReceiveLocation(BDLocation location); void onError(int locType, String message); } public void init(Context context, LocationCallback callback) { mLocationClient new LocationClient(context.getApplicationContext()); mListener new BDAbstractLocationListener() { Override public void onReceiveLocation(BDLocation location) { if (location null || callback null) return; if (location.getLocType() BDLocation.TypeGpsLocation || location.getLocType() BDLocation.TypeNetWorkLocation) { callback.onReceiveLocation(location); } else { callback.onError(location.getLocType(), location.getLocDescribe()); } } }; LocationClientOption option new LocationClientOption(); option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy); option.setCoorType(bd09ll); option.setScanSpan(0); option.setOpenGps(true); option.setNeedDeviceDirect(true); option.setIsNeedAddress(true); mLocationClient.setLocOption(option); mLocationClient.registerLocationListener(mListener); } public void start() { if (mLocationClient ! null !mLocationClient.isStarted()) { mLocationClient.start(); } } public void stop() { if (mLocationClient ! null mLocationClient.isStarted()) { mLocationClient.stop(); } } }3. 和风天气API集成与数据解析3.1 获取和风天气API Key访问和风天气开发者平台(https://dev.heweather.com/)注册账号并登录进入控制台创建项目获取免费版的Key每日1000次调用3.2 网络请求框架配置使用Retrofit Gson进行网络请求和数据解析在build.gradle中添加dependencies { implementation com.squareup.retrofit2:retrofit:2.9.0 implementation com.squareup.retrofit2:converter-gson:2.9.0 implementation com.google.code.gson:gson:2.8.8 }创建API服务接口public interface HeWeatherService { GET(weather/now) CallWeatherResponse getCurrentWeather( Query(location) String location, Query(key) String key ); GET(air/now) CallAirQualityResponse getAirQuality( Query(location) String location, Query(key) String key ); }配置Retrofit实例public class ApiClient { private static final String BASE_URL https://free-api.heweather.net/s6/; private static Retrofit retrofit; public static Retrofit getClient() { if (retrofit null) { retrofit new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); } return retrofit; } }3.3 数据模型设计根据和风天气API返回的JSON结构创建对应的Java类public class WeatherResponse { SerializedName(HeWeather6) private ListWeather weatherList; // getters and setters } public class Weather { SerializedName(basic) private BasicInfo basic; SerializedName(now) private NowInfo now; SerializedName(update) private UpdateInfo update; // getters and setters } public class NowInfo { SerializedName(tmp) private String temperature; SerializedName(cond_txt) private String condition; SerializedName(wind_dir) private String windDirection; // 其他字段... }4. 应用界面设计与实现4.1 主界面布局设计使用ConstraintLayout构建响应式界面androidx.constraintlayout.widget.ConstraintLayout xmlns:androidhttp://schemas.android.com/apk/res/android xmlns:apphttp://schemas.android.com/apk/res-auto android:layout_widthmatch_parent android:layout_heightmatch_parent ImageView android:idid/iv_background android:layout_width0dp android:layout_height0dp android:scaleTypecenterCrop app:layout_constraintBottom_toBottomOfparent app:layout_constraintEnd_toEndOfparent app:layout_constraintStart_toStartOfparent app:layout_constraintTop_toTopOfparent / TextView android:idid/tv_city android:layout_widthwrap_content android:layout_heightwrap_content android:textSize24sp android:textColorandroid:color/white app:layout_constraintLeft_toLeftOfparent app:layout_constraintRight_toRightOfparent app:layout_constraintTop_toTopOfparent android:layout_marginTop16dp / !-- 其他UI元素 -- /androidx.constraintlayout.widget.ConstraintLayout4.2 天气信息展示组件创建自定义WeatherView显示天气信息public class WeatherView extends LinearLayout { private TextView tvTemperature; private TextView tvCondition; private ImageView ivWeatherIcon; public WeatherView(Context context) { super(context); init(); } private void init() { setOrientation(VERTICAL); LayoutInflater.from(getContext()).inflate(R.layout.view_weather, this, true); tvTemperature findViewById(R.id.tv_temperature); tvCondition findViewById(R.id.tv_condition); ivWeatherIcon findViewById(R.id.iv_weather_icon); } public void setWeatherData(NowInfo nowInfo) { tvTemperature.setText(nowInfo.getTemperature() °C); tvCondition.setText(nowInfo.getCondition()); // 根据天气状况设置图标 int resId getWeatherIconRes(nowInfo.getCondition()); ivWeatherIcon.setImageResource(resId); } private int getWeatherIconRes(String condition) { switch (condition) { case 晴: return R.drawable.ic_sunny; case 多云: return R.drawable.ic_cloudy; // 其他天气状况... default: return R.drawable.ic_unknown; } } }4.3 空气质量指数可视化使用MPAndroidChart库展示空气质量数据添加依赖implementation com.github.PhilJay:MPAndroidChart:v3.1.0创建AirQualityChartViewpublic class AirQualityChartView extends BarChart { public AirQualityChartView(Context context) { super(context); initChart(); } private void initChart() { getDescription().setEnabled(false); setDrawGridBackground(false); setDrawBarShadow(false); setHighlightFullBarEnabled(false); XAxis xAxis getXAxis(); xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); xAxis.setDrawGridLines(false); YAxis leftAxis getAxisLeft(); leftAxis.setAxisMinimum(0f); leftAxis.setEnabled(false); getAxisRight().setEnabled(false); getLegend().setEnabled(false); } public void setData(AirQualityData data) { ListBarEntry entries new ArrayList(); entries.add(new BarEntry(0, data.getPm25())); entries.add(new BarEntry(1, data.getPm10())); entries.add(new BarEntry(2, data.getSo2())); entries.add(new BarEntry(3, data.getNo2())); entries.add(new BarEntry(4, data.getO3())); entries.add(new BarEntry(5, data.getCo())); BarDataSet dataSet new BarDataSet(entries, Air Quality); dataSet.setColors(getColorsForAQI(data.getAqi())); dataSet.setDrawValues(true); BarData barData new BarData(dataSet); barData.setBarWidth(0.5f); setData(barData); animateY(1000); invalidate(); } private int[] getColorsForAQI(int aqi) { // 根据AQI值返回不同颜色 if (aqi 50) return new int[]{Color.GREEN}; else if (aqi 100) return new int[]{Color.YELLOW}; // 其他范围... } }5. 应用优化与发布准备5.1 权限管理最佳实践实现动态权限请求public class PermissionHelper { private static final int REQUEST_CODE 1001; private static final String[] PERMISSIONS { Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION }; public static boolean checkPermissions(Activity activity) { if (Build.VERSION.SDK_INT Build.VERSION_CODES.M) { return true; } for (String permission : PERMISSIONS) { if (activity.checkSelfPermission(permission) ! PackageManager.PERMISSION_GRANTED) { return false; } } return true; } public static void requestPermissions(Activity activity) { if (Build.VERSION.SDK_INT Build.VERSION_CODES.M) { activity.requestPermissions(PERMISSIONS, REQUEST_CODE); } } public static boolean handleResult(int requestCode, int[] grantResults) { if (requestCode ! REQUEST_CODE) return false; for (int result : grantResults) { if (result ! PackageManager.PERMISSION_GRANTED) { return false; } } return true; } }5.2 应用性能优化内存优化使用LeakCanary检测内存泄漏优化图片资源大小及时释放不再使用的资源网络请求优化添加请求缓存合并API请求使用Gzip压缩启动优化减少Application初始化工作使用SplashScreen API延迟加载非必要组件5.3 发布前检查清单检查项说明是否完成应用图标准备各种分辨率的图标☐应用名称确认最终名称无侵权☐权限说明在隐私政策中说明权限用途☐API密钥保护检查是否硬编码了敏感信息☐混淆配置启用ProGuard/R8混淆☐多设备测试在不同尺寸设备上测试☐崩溃监控集成Firebase Crashlytics☐6. 常见问题解决方案6.1 百度地图不显示可能原因及解决方案SHA1配置错误确认debug和release的SHA1都已配置使用keytool重新生成SHA1包名不匹配检查AndroidManifest中的package属性确认与百度控制台配置一致网络权限问题确认已添加INTERNET权限检查是否被安全软件拦截6.2 天气数据获取失败排查步骤检查和风天气API Key是否有效确认网络请求是否成功使用Charles或Fiddler抓包验证返回的数据格式是否与模型匹配检查是否超过API调用限制6.3 定位不准确优化建议确保设备已开启高精度定位模式在室外开阔地带测试检查百度地图定位参数配置考虑使用混合定位策略GPS网络7. 扩展功能建议7.1 天气预警通知实现步骤注册广播接收器监听系统时间变化定时检查天气预警信息使用NotificationManager发送通知添加通知渠道适配Android 8.0核心代码片段public class WeatherNotification { private static final String CHANNEL_ID weather_alerts; public static void showAlert(Context context, String title, String message) { createNotificationChannel(context); NotificationCompat.Builder builder new NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_alert) .setContentTitle(title) .setContentText(message) .setPriority(NotificationCompat.PRIORITY_HIGH) .setAutoCancel(true); NotificationManager manager (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); manager.notify(createID(), builder.build()); } private static void createNotificationChannel(Context context) { if (Build.VERSION.SDK_INT Build.VERSION_CODES.O) { NotificationChannel channel new NotificationChannel( CHANNEL_ID, Weather Alerts, NotificationManager.IMPORTANCE_HIGH ); channel.setDescription(Weather warning notifications); NotificationManager manager context.getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } } private static int createID() { return (int) System.currentTimeMillis(); } }7.2 多城市天气管理实现方案创建城市数据库表实现城市添加/删除功能使用ViewPager2展示多个城市天气添加城市搜索自动完成数据库表结构建议CREATE TABLE cities ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, latitude REAL NOT NULL, longitude REAL NOT NULL, is_current INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );7.3 天气数据本地缓存缓存策略实现使用Room数据库存储历史数据设置合理的缓存过期时间网络不可用时自动使用缓存添加数据同步状态提示缓存模型设计Entity(tableName weather_cache) public class CachedWeather { PrimaryKey public String locationId; public String weatherJson; public String airQualityJson; public long timestamp; public boolean isExpired() { return System.currentTimeMillis() - timestamp 30 * 60 * 1000; // 30分钟过期 } }在开发过程中我发现百度地图定位在室内环境下有时会出现较大偏差这种情况下可以结合IP定位进行补偿。另外和风天气的免费API有调用频率限制在实际项目中需要考虑缓存策略或升级到付费版本。