Android Studio实战:集成百度地图与和风天气API开发智能天气应用
最近在开发一款结合实时定位与天气数据的应用时,发现很多初学者在第三方API集成环节容易踩坑。本文将分享如何用Android Studio串联百度地图定位、和风天气数据获取以及MPAndroidChart图表展示这三个核心模块,打造一款功能完整的天气应用。不同于简单的代码堆砌,我会重点讲解模块间的数据流转和工程化实践。
1. 开发环境与项目初始化
在Android Studio中新建项目时,建议选择"Empty Activity"模板,将最低API级别设置为21(Android 5.0)。这个版本已经支持我们需要的所有关键特性,同时能覆盖约95%的Android设备。
关键依赖配置(build.gradle):
dependencies { // 百度地图SDK implementation 'com.baidu.lbsyun:BaiduMapSDK_Map:7.4.0' implementation 'com.baidu.lbsyun:BaiduMapSDK_Location:9.1.8' // 网络请求 implementation 'com.squareup.okhttp3:okhttp:4.9.3' // JSON解析 implementation 'com.google.code.gson:gson:2.8.9' // 图表库 implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' // 圆形进度条 implementation 'com.mikhaellopez:circularprogressbar:3.1.0' }别忘了在AndroidManifest.xml中添加必要权限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.READ_PHONE_STATE"/>2. 百度地图集成与精确定位
在百度地图开放平台创建应用时,需要注意SHA1值的配置。开发调试时使用Android Studio生成的debug证书,发布时则需要换成正式签名证书。这个步骤经常被开发者忽略,导致地图无法正常显示。
定位服务封装示例:
class BaiduLocationService(context: Context) : BDAbstractLocationListener() { private val mLocationClient = LocationClient(context.applicationContext) init { val option = LocationClientOption().apply { setIsNeedAddress(true) // 需要地址信息 isOpenGps = true // 打开GPS locationMode = LocationClientOption.LocationMode.Hight_Accuracy setCoorType("bd09ll") // 设置坐标类型 scanSpan = 1000 // 定位间隔 } mLocationClient.locOption = option mLocationClient.registerLocationListener(this) } override fun onReceiveLocation(location: BDLocation?) { location?.let { when (it.locType) { BDLocation.TypeGpsLocation -> { // GPS定位结果处理 val city = it.city?.replace("市", "") ?: "" EventBus.getDefault().post(LocationEvent(it.latitude, it.longitude, city)) } BDLocation.TypeNetWorkLocation -> { // 网络定位结果处理 } else -> { // 错误处理 } } } } fun start() { if (mLocationClient.isStarted.not()) { mLocationClient.start() } } fun stop() { mLocationClient.stop() } }定位优化技巧:
- 在应用进入后台时适当降低定位频率
- 对连续定位结果进行去重处理
- 使用缓存机制保存最后一次有效位置
3. 和风天气API对接与数据解析
和风天气提供了丰富的免费API,但需要注意免费版有每日调用次数限制。建议在应用中加入本地缓存机制,避免频繁请求相同数据。
天气数据模型设计:
public class WeatherData { @SerializedName("now") private NowBean now; @SerializedName("daily_forecast") private List<DailyForecastBean> forecast; public static class NowBean { @SerializedName("tmp") private String temperature; @SerializedName("cond_txt") private String condition; // 其他字段... } public static class DailyForecastBean { @SerializedName("tmp_max") private String maxTemp; @SerializedName("tmp_min") private String minTemp; // 其他字段... } }网络请求封装示例:
object WeatherApi { private const val BASE_URL = "https://devapi.qweather.com/v7/" private val client = OkHttpClient() private val gson = Gson() suspend fun fetchWeather(location: String, key: String): WeatherData { val request = Request.Builder() .url("${BASE_URL}weather/now?location=$location&key=$key") .build() return withContext(Dispatchers.IO) { val response = client.newCall(request).execute() if (!response.isSuccessful) throw IOException("Unexpected code $response") val json = response.body?.string() ?: throw IOException("Empty response") gson.fromJson(json, WeatherResponse::class.java).toWeatherData() } } suspend fun fetchAirQuality(location: String, key: String): AirQualityData { // 类似实现... } }性能优化建议:
- 使用Kotlin协程简化异步调用
- 对API响应进行压缩处理
- 实现请求失败自动重试机制
- 添加网络状态检测
4. 数据可视化与UI优化
MPAndroidChart虽然功能强大,但配置选项繁多。这里分享几个实用配置:
温度曲线图配置:
LineChart chart = findViewById(R.id.tempChart); LineData data = new LineData(); LineDataSet set = new LineDataSet(entries, "温度变化"); set.setColor(Color.parseColor("#FF5722")); set.setLineWidth(2f); set.setCircleColor(Color.parseColor("#FF5722")); set.setCircleRadius(3f); set.setDrawCircleHole(false); set.setValueTextSize(10f); set.setMode(LineDataSet.Mode.CUBIC_BEZIER); data.addDataSet(set); chart.setData(data); // 交互配置 chart.setTouchEnabled(true); chart.setDragEnabled(true); chart.setScaleEnabled(true); chart.setPinchZoom(true); chart.setDrawGridBackground(false); // 动画效果 chart.animateX(1500);空气质量圆形进度条实现:
<com.mikhaellopez.circularprogressbar.CircularProgressBar android:id="@+id/aqiProgress" android:layout_width="120dp" android:layout_height="120dp" app:cpb_progress="75" app:cpb_progressMax="500" app:cpb_backgroundProgressBarColor="#E0E0E0" app:cpb_progressBarColor="#4CAF50" app:cpb_progressBarWidth="8dp" app:cpb_backgroundProgressBarWidth="8dp" app:cpb_roundBorder="true"/>UI设计要点:
- 使用ConstraintLayout构建响应式布局
- 为不同天气状况设计专属图标和配色方案
- 添加适当的过渡动画提升用户体验
- 实现下拉刷新功能
5. 工程架构与代码组织
随着功能增加,合理的项目结构变得尤为重要。推荐采用以下包结构:
com.example.weather ├── api // 网络请求相关 ├── model // 数据模型 ├── service // 后台服务 ├── ui // 界面相关 │ ├── main // 主界面 │ └── splash // 启动页 ├── util // 工具类 └── view // 自定义视图使用ViewModel管理界面数据:
class WeatherViewModel(application: Application) : AndroidViewModel(application) { private val _weatherData = MutableLiveData<WeatherData>() val weatherData: LiveData<WeatherData> = _weatherData private val _loadingState = MutableLiveData<Boolean>() val loadingState: LiveData<Boolean> = _loadingState fun fetchData(location: String) { viewModelScope.launch { _loadingState.value = true try { val result = WeatherApi.fetchWeather(location, API_KEY) _weatherData.value = result } catch (e: Exception) { // 错误处理 } finally { _loadingState.value = false } } } }架构建议:
- 遵循单一职责原则拆分组件
- 使用依赖注入管理服务实例
- 实现适当的接口抽象便于测试
- 添加日志记录帮助调试
6. 调试技巧与常见问题解决
在开发过程中,我遇到了几个典型问题及解决方案:
定位失败问题排查:
- 检查SHA1和包名配置是否正确
- 确认设备已开启位置服务
- 测试不同定位模式(GPS/网络)
- 查看百度地图后台的调用统计
天气数据异常处理:
fun parseTemperature(tempStr: String?): Int { return try { tempStr?.toInt() ?: 0 } catch (e: NumberFormatException) { Log.w("Weather", "Invalid temperature format: $tempStr") 0 } }性能优化工具:
- 使用Android Profiler监控CPU和内存使用
- 通过Layout Inspector检查视图层次
- 启用StrictMode检测主线程操作
- 使用LeakCanary检测内存泄漏
适配不同屏幕尺寸的技巧:
- 为不同dpi提供多套图片资源
- 使用dp单位而非px
- 创建不同的布局文件(layout-sw600dp等)
- 测试各种宽高比例的设备
7. 扩展功能与进阶优化
基础功能完成后,可以考虑添加以下增强特性:
多城市管理:
public class CityManager { private SharedPreferences prefs; public void addCity(String city) { Set<String> cities = getSavedCities(); cities.add(city); prefs.edit().putStringSet("saved_cities", cities).apply(); } public Set<String> getSavedCities() { return prefs.getStringSet("saved_cities", new HashSet<>()); } }天气预警通知:
fun showWeatherAlert(context: Context, warning: WeatherWarning) { val channelId = "weather_alerts" val notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_warning) .setContentTitle(warning.title) .setContentText(warning.description) .setPriority(NotificationCompat.PRIORITY_HIGH) .setAutoCancel(true) .build() val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager manager.notify(warning.id.hashCode(), notification) }数据缓存策略:
public class WeatherCache { private static final long MAX_AGE = TimeUnit.MINUTES.toMillis(30); public void saveData(String location, WeatherData data) { CacheEntry entry = new CacheEntry(System.currentTimeMillis(), data); // 存储到SharedPreferences或Room数据库 } public WeatherData getData(String location) { CacheEntry entry = // 从存储中读取 if (entry != null && System.currentTimeMillis() - entry.timestamp < MAX_AGE) { return entry.data; } return null; } }国际化支持:
- 创建不同语言的strings.xml文件
- 处理日期和数字的本地化格式
- 考虑RTL(从右到左)布局支持
- 测试不同语言下的UI适配
在项目开发过程中,最耗时的部分往往是不同API之间的数据对接和错误处理。建议在初期就建立完善的日志系统,记录关键操作和数据流转,这能极大提升调试效率。