安卓应用开发学习:聚合数据API获取天气预报

安卓应用开发学习:聚合数据API获取天气预报最近在看软件书籍时 又看到了聚合数据 API 方面的内容

大家好,欢迎来到IT知识分享网。

一、引言

上个月我通过腾讯位置服务,实现了手机定位应用的开发学习。最近在看软件书籍时,又看到了聚合数据API方面的内容。 书上介绍了聚合数据天气预报API的应用,不过书上的代码看得有些难受,我到聚合数据官网,对天气预报API的接口文档进行了研究,感觉比书上的要简单。于是,我参照官网的接口文档设计查询部分的代码,UI等设计则借鉴了书上的内容,完成了这个应用的开发。实现的效果如下图:

安卓应用开发学习:聚合数据API获取天气预报

二、聚合数据平台选择API

聚合数据平台提供了很多的API,其中免费的API也不少。要使用该平台的API自然需要先注册用户了。

安卓应用开发学习:聚合数据API获取天气预报

注册号用户,登录后,先完成实名认证,否则无法使用API。我是后知后觉,申请API的时候出现了要求实名认证的提示。

安卓应用开发学习:聚合数据API获取天气预报

大体的流程在网站上有说明。

安卓应用开发学习:聚合数据API获取天气预报

完成注册和认证后,进入API页面,选择免费接口 (免费API接口工具大全 – 聚合数据)。我们要用到的天气预报接口就在第一行。

安卓应用开发学习:聚合数据API获取天气预报

点击天气预报,进入天气预报接口页面。点击“立即申请”。这个页面里还可以获得接口的相关介绍和示例代码,方便我们进行应用开发。

安卓应用开发学习:聚合数据API获取天气预报

完成申请后就可以在“个人中心 – 数据中心 – 我的API”中看到申请到的API了。聚合数据对免费接口有限制,普通会员只能申请3个。大多数免费接口每天的请求次数为50次,进行开发学习还是够用了。调用API需要的Key也在这个页面里。

安卓应用开发学习:聚合数据API获取天气预报

完成了API的申请,就可以着手进行软件的设计开发了。

三、软件设计

我的天气预报应用的界面设计参考了书上的样式,但书上的代码将城市名写死了,我希望支持输入城市名进行查询。因此设计UI时,在页面顶部添加了SearchView组件,用于输入城市名称,输入后按下软键盘中的搜索按钮,执行获取天气预报操作。界面的中间部分放置了几个TextView组件用于显示实时天气信息,界面下部使用纵向的LinearLayout布局设置,该布局做了圆角处理,其下放置了多个TextView组件用于显示未来5天的预报信息。有参照做起来就很快了。

安卓应用开发学习:聚合数据API获取天气预报

逻辑代码的设计则花了一些时间,主要是书上的代码看着不够简洁,比官网提供的示例要复杂不少。我就没有照搬书上的内容做,而是综合了书上的代码与官网示例进行的设计。其中GET请求部分的代码基本照搬了“接口文档”中提供的Java示例。有所区别的是,需要添加try语句,否则会报错。

安卓应用开发学习:聚合数据API获取天气预报

官网提供了接口测试功能,可以先在接口测试页面查看获取天气预报的请求详情和返回结果。方便后续的代码设计。

安卓应用开发学习:聚合数据API获取天气预报

这个API的调用接口URI格式如下:

http://apis.juhe.cn/simpleWeather/query?key=key&city=%E8%8B%8F%E5%B7%9E

其中key在“个人中心 – 数据中心 – 我的API”中获取。city则是从SearchView组件中获取。我将GET请求放在了SearchView组件的监听器中执行。监听器响应搜索按钮触发,获取组件中的文本内容,并将其传递给获取天气预报的方法。

获取天气预报的方法,我是参考了官网的java示例代码(如下):

package cn.juhe.test; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; public class JavaGet { public static void main(String[] args) throws Exception { String apiKey = "你申请的key"; String apiUrl = "http://apis.juhe.cn/simpleWeather/query"; HashMap<String, String> map = new HashMap<>(); map.put("key", apiKey); map.put("city", "苏州"); URL url = new URL(String.format(apiUrl + "?" + params(map))); BufferedReader in = new BufferedReader(new InputStreamReader((url.openConnection()).getInputStream())); String inputLine; StringBuffer response = new StringBuffer(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); System.out.println(response); } public static String params(Map<String, String> map) { return map.entrySet().stream() .map(entry -> entry.getKey() + "=" + entry.getValue()) .collect(Collectors.joining("&")); } }

上述代码也是将城市名写死的,且获取到的结果直接打印输出,不符合我们的实际应用需求,得修改,但关键的代码基本是可以照搬的。

在做逻辑代码设计时,我遇到了两个问题:

1.一开始,我的GET请求代码是放在主线程中执行的,结果测试时出现了报错。网上搜索后,才了解到从Android 4.0 之后不能在主线程中请求HTTP,需要将GET请求放在分线程中执行。

2.在分线程中执行GET请求获取到天气预报的信息后,我在分线程里更新UI的代码,结果测试时又报错了。一搜,Android不允许在分线程中直接修改UI界面,可以使用runOnUiThread方法更新UI界面。

解决了这两个问题,其它方面就很顺利了,完成后测试了几次,还可以。

安卓应用开发学习:聚合数据API获取天气预报安卓应用开发学习:聚合数据API获取天气预报

请求次数可以在“个人中心-数据中心-我的API”找到天气预报,点击“统计”按钮,可以查看调用情况。

安卓应用开发学习:聚合数据API获取天气预报

四、代码展示

最终的代码如下:

1. 界面设计文件  activity_weather.xml

<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".WeatherActivity"> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:text="天气预报" android:textSize="24sp" android:textStyle="bold" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <LinearLayout android:id="@+id/linearLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:orientation="horizontal" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_title"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginEnd="10dp" android:text="城市" android:textSize="17sp" /> <android.widget.SearchView android:id="@+id/sv_cityName" android:layout_width="match_parent" android:layout_height="40dp" android:background="@drawable/shape_round_bg_gray" android:iconifiedByDefault="true" android:imeOptions="actionSearch" android:queryHint="请输入关键字" android:textColor="@color/gray_78" android:textSize="15sp" /> </LinearLayout> <ScrollView android:layout_width="match_parent" android:layout_height="0dp" android:layout_marginTop="10dp" android:background="@color/blue_415" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/linearLayout"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:orientation="vertical" > <TextView android:id="@+id/tv_result" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_cityName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="城市名" android:textColor="@color/white" android:textSize="24sp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:orientation="horizontal"> <TextView android:id="@+id/tv_realTime_temp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="~℃" android:textColor="@color/white" android:textSize="32sp" android:textStyle="bold" /> <TextView android:id="@+id/tv_realTime_weather" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="天气" android:textColor="@color/white" android:textSize="32sp" android:textStyle="bold" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:orientation="horizontal"> <TextView android:id="@+id/tv_realTime_dir" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="风向" android:textColor="@color/white" android:textSize="22sp" android:textStyle="bold" /> <TextView android:id="@+id/tv_realTime_pow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="风级" android:textColor="@color/white" android:textSize="22sp" android:textStyle="bold" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:orientation="horizontal"> <TextView android:id="@+id/tv_realTime_hum" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="湿度~" android:textColor="@color/white" android:textSize="22sp" android:textStyle="bold" /> <TextView android:id="@+id/tv_realTime_aqi" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="空气质量指数~" android:textColor="@color/white" android:textSize="22sp" android:textStyle="bold" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:background="@drawable/radius_border_15" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:layout_marginTop="10dp" android:text="5天预报" android:textSize="20sp" /> <TextView android:id="@+id/tv_date1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:layout_marginTop="10dp" android:text="日期1" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/tv_temp1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="~℃" /> <TextView android:id="@+id/tv_weather1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="天气" /> <TextView android:id="@+id/tv_dir1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="风向" /> </LinearLayout> <TextView android:id="@+id/tv_date2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:layout_marginTop="10dp" android:text="日期2" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/tv_temp2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="~℃" /> <TextView android:id="@+id/tv_weather2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="天气" /> <TextView android:id="@+id/tv_dir2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="风向" /> </LinearLayout> <TextView android:id="@+id/tv_date3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:layout_marginTop="10dp" android:text="日期3" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/tv_temp3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="~℃" /> <TextView android:id="@+id/tv_weather3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="天气" /> <TextView android:id="@+id/tv_dir3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="风向" /> </LinearLayout> <TextView android:id="@+id/tv_date4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:layout_marginTop="10dp" android:text="日期4" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/tv_temp4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="~℃" /> <TextView android:id="@+id/tv_weather4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="天气" /> <TextView android:id="@+id/tv_dir4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="风向" /> </LinearLayout> <TextView android:id="@+id/tv_date5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:layout_marginTop="10dp" android:text="日期5" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:orientation="horizontal"> <TextView android:id="@+id/tv_temp5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="~℃" /> <TextView android:id="@+id/tv_weather5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="天气" /> <TextView android:id="@+id/tv_dir5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="10dp" android:text="风向" /> </LinearLayout> </LinearLayout> </LinearLayout> </ScrollView> </androidx.constraintlayout.widget.ConstraintLayout>

2. 逻辑代码 WeatherActivity.java

import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.widget.SearchView; import android.widget.TextView; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; // 与官网示例不同,官网是net.sf.json.JSONObject import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.stream.Collectors; public class WeatherActivity extends AppCompatActivity { private final static String TAG = "WeatherActivity"; private SearchView sv_cityName; // 搜索框 private TextView tv_cityName; // 显示城市名 private TextView tv_realTime_temp; // 显示实时温度 private TextView tv_realTime_weather; // 显示实时天气 private TextView tv_realTime_dir; // 显示实时风向 private TextView tv_realTime_pow; // 显示实时风级 private TextView tv_realTime_hum; // 显示实时湿度 private TextView tv_realTime_aqi; // 显示空气质量指数 private TextView tv_date1, tv_date2, tv_date3, tv_date4, tv_date5; // 日期 private TextView tv_temp1, tv_temp2, tv_temp3, tv_temp4, tv_temp5; // 温度 private TextView tv_weather1, tv_weather2, tv_weather3, tv_weather4, tv_weather5; // 天气 private TextView tv_dir1, tv_dir2, tv_dir3, tv_dir4, tv_dir5; // 风向 private String mCityName; // 保存用户在搜索框中输入的城市名 // 天气情况查询接口地址 private static final String API_URL = "http://apis.juhe.cn/simpleWeather/query"; // 接口请求Key(在聚合数据网站申请天气预报API后生成的AppKey) private static final String API_KEY = "*"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_weather); sv_cityName = findViewById(R.id.sv_cityName); // 城市名搜索框 // 实时天气 tv_cityName = findViewById(R.id.tv_cityName); tv_realTime_temp = findViewById(R.id.tv_realTime_temp); tv_realTime_weather = findViewById(R.id.tv_realTime_weather); tv_realTime_dir = findViewById(R.id.tv_realTime_dir); tv_realTime_pow = findViewById(R.id.tv_realTime_pow); tv_realTime_hum = findViewById(R.id.tv_realTime_hum); tv_realTime_aqi = findViewById(R.id.tv_realTime_aqi); // 未来5天预报天气 tv_date1 = findViewById(R.id.tv_date1); tv_date2 = findViewById(R.id.tv_date2); tv_date3 = findViewById(R.id.tv_date3); tv_date4 = findViewById(R.id.tv_date4); tv_date5 = findViewById(R.id.tv_date5); tv_temp1 = findViewById(R.id.tv_temp1); tv_temp2 = findViewById(R.id.tv_temp2); tv_temp3 = findViewById(R.id.tv_temp3); tv_temp4 = findViewById(R.id.tv_temp4); tv_temp5 = findViewById(R.id.tv_temp5); tv_weather1 = findViewById(R.id.tv_weather1); tv_weather2 = findViewById(R.id.tv_weather2); tv_weather3 = findViewById(R.id.tv_weather3); tv_weather4 = findViewById(R.id.tv_weather4); tv_weather5 = findViewById(R.id.tv_weather5); tv_dir1 = findViewById(R.id.tv_dir1); tv_dir2 = findViewById(R.id.tv_dir2); tv_dir3 = findViewById(R.id.tv_dir3); tv_dir4 = findViewById(R.id.tv_dir4); tv_dir5 = findViewById(R.id.tv_dir5); // 设置搜索框监听器 sv_cityName.setOnQueryTextListener(new SearchView.OnQueryTextListener() { // 当点击搜索按钮时触发该方法 @Override public boolean onQueryTextSubmit(String s) { sv_cityName.clearFocus(); // 移除焦点 mCityName = s; // Android 4.0 之后不能在主线程中请求HTTP new Thread(() -> queryWeather (mCityName)).start(); // 分线程中获取天气信息 return false; } // 当搜索内容改变时触发该方法 @Override public boolean onQueryTextChange(String s) { return false; } }); } / * 根据城市名查询天气情况 * * @param cityName 城市名称 */ private void queryWeather(String cityName) { HashMap<String, String> map = new HashMap<>(); //组合参数 map.put("city", cityName); map.put("key", API_KEY); String queryParams = params(map); try { URL url = new URL(API_URL + "?" + queryParams); Log.d(TAG, "URL=" + url); BufferedReader in = new BufferedReader(new InputStreamReader((url.openConnection()).getInputStream())); String inputLine; StringBuffer response = new StringBuffer(); // StringBuffer是线程安全的,StringBuilder效率更高,但不是线程安全的 while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); // Log.d(TAG, "查询天气返回的结果:"); // Log.d(TAG, response.toString()); // 将获取到的结果转换为JSONObject,从中获取天气信息 try { JSONObject jsonObject = new JSONObject(response.toString()); int error_code = jsonObject.getInt("error_code"); if (error_code == 0) { JSONObject result = jsonObject.getJSONObject("result"); String city = result.getString("city"); // 获取实时天气数据 JSONObject realtime = result.getJSONObject("realtime"); String temp = realtime.getString("temperature"); // 温度 String hum = realtime.getString("humidity"); // 湿度 String info = realtime.getString("info"); // 天气 String dir = realtime.getString("direct"); // 风向 String pow = realtime.getString("power"); // 风级 String aqi = realtime.getString("power"); // 空气质量指数 // 获取未来5天的天气数据 JSONArray futureArray = result.getJSONArray("future"); JSONObject f1 = futureArray.getJSONObject(0); String date1 = f1.getString("date"); String temp1 = f1.getString("temperature"); String weather1 = f1.getString("weather"); String dir1 = f1.getString("direct"); JSONObject f2 = futureArray.getJSONObject(1); String date2 = f2.getString("date"); String temp2 = f2.getString("temperature"); String weather2 = f2.getString("weather"); String dir2 = f2.getString("direct"); JSONObject f3 = futureArray.getJSONObject(2); String date3 = f3.getString("date"); String temp3 = f3.getString("temperature"); String weather3 = f3.getString("weather"); String dir3 = f3.getString("direct"); JSONObject f4 = futureArray.getJSONObject(3); String date4 = f4.getString("date"); String temp4 = f4.getString("temperature"); String weather4 = f4.getString("weather"); String dir4 = f4.getString("direct"); JSONObject f5 = futureArray.getJSONObject(4); String date5 = f5.getString("date"); String temp5 = f5.getString("temperature"); String weather5 = f5.getString("weather"); String dir5 = f5.getString("direct"); // 分线程不能直接修改UI界面,可以使用runOnUiThread方法更新UI界面 runOnUiThread(() -> { tv_cityName.setText(city); // 更新实时天气 tv_realTime_temp.setText(String.format(Locale.CHINESE, "%s%s", temp, "℃")); tv_realTime_weather.setText(info); tv_realTime_dir.setText(dir); tv_realTime_pow.setText(pow); tv_realTime_hum.setText(String.format(Locale.CHINESE, "%s%s", "湿度:", hum)); tv_realTime_aqi.setText(String.format(Locale.CHINESE, "%s%s", "空气质量指数:", aqi)); // 更新未来5天预报天气 tv_date1.setText(date1); tv_temp1.setText(temp1); tv_weather1.setText(weather1); tv_dir1.setText(dir1); tv_date2.setText(date2); tv_temp2.setText(temp2); tv_weather2.setText(weather2); tv_dir2.setText(dir2); tv_date3.setText(date3); tv_temp3.setText(temp3); tv_weather3.setText(weather3); tv_dir3.setText(dir3); tv_date4.setText(date4); tv_temp4.setText(temp4); tv_weather4.setText(weather4); tv_dir4.setText(dir4); tv_date5.setText(date5); tv_temp5.setText(temp5); tv_weather5.setText(weather5); tv_dir5.setText(dir5); }); // 使用runOnUiThread更新界面 } else { Log.d(TAG, "调用接口失败:" + jsonObject.getString("reason")); } } catch (JSONException e) { e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } } / * 将map型转为请求参数型 * * @param map map型保存的参数 */ private static String params(Map<String, String> map) { return map.entrySet().stream() .map(entry -> entry.getKey() + "=" + entry.getValue()) .collect(Collectors.joining("&")); } }

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/120093.html

(0)
上一篇 2025-10-31 22:15
下一篇 2025-10-31 22:20

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信