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

news2024/12/23 20:54:15

一、引言

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

二、聚合数据平台选择API

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

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

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

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

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

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

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

三、软件设计

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

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

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

这个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”找到天气预报,点击“统计”按钮,可以查看调用情况。

四、代码展示

最终的代码如下:

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 = "59e7634610d0c07c8e4aa4a5d1dee643";

    @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("&"));
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1988283.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用Streamlit构建一个web模拟HTTP请求工具

目录 前言 HTTP工具功能点&#xff1a; 1. 导入库: 2.设置页面配置: 3. Markdown格式的说明文本: 4.用户输入界面: 5. 发送请求按钮和逻辑: 6.发送HTTP请求并计算请求细节: 7.总结 前言 最初就是因为在微信看到一篇文章中,看到此http工具的制作因为觉得Streamlit有无限…

第四周:网络应用(下)

一、P2P应用_原理与文件分发 1、纯P2P架构 2、文件分发&#xff1a;客户机/服务器架构 3、文件分发&#xff1a;P2P架构 P2P和CS架构对比&#xff1a; 对于文件分发这类应用来说&#xff0c;P2P架构有着非常好的可扩展性&#xff0c;即当节点数目增大的时候&#xff0c;它所…

Ajax-02

一.form-serialize插件 作用&#xff1a;快速收集表单元素的值 const form document.querySelector(.example-form) const data serialize(form,{hash:true,empty:true}) *参数1&#xff1a;要获取哪个表单的数据 表单元素设置name属性&#xff0c;值会作为对象的属性名 建议…

【初阶数据结构题目】13.环形链表II

环形链表II 点击链接做题 思路&#xff1a;快慢指针 快慢指针&#xff0c;即慢指针一次走一步&#xff0c;快指针一次走两步&#xff0c;两个指针从链表起始位置开始运行&#xff0c;如果链表带环则一定会在环中相遇&#xff0c;否则快指针率先走到链表的未尾 让一个指针从链表…

visual studio跳转到上一个/下一个光标处的快捷键设置

vscode能通过Alt左右箭头跳转到上/下一个光标处&#xff0c;这对于“点进函数看源码&#xff0c;看完后跳转到原来位置”是非常方便的。 在Visual Studio中&#xff0c;有2种方法实现这样的功能。 第一种&#xff0c;直接点击这两个按钮&#xff1a;&#xff08;缺点是每次要用…

阻抗?关于双绞线阻抗的那些事...

双绞线的阻抗不是电阻&#xff0c;而是描述传输高频信号时电磁感应分布特性的一个特性参数。 电缆中的每个微分段的电阻、电容、电感的分布感应值都是不一样。 分析方法&#xff1a;计算每个微分电缆段的感应值分布函数&#xff0c;换算成四端网络参数等效值&#xff0c;单位…

【C语言】预处理详解(下)

文章目录 前言6. 宏和函数的对比7. #和##7.1 #运算符7.2 ##运算符&#xff08;运用较少&#xff0c;了解即可&#xff09; 8. 命名的约定9. #undef &#xff08;了解即可&#xff09;10. 条件编译&#xff08;重点&#xff09;11. 头文件的包含11.1 头文件被包含的方式&#xf…

<数据集>柑橘缺陷识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;1290张 标注数量(xml文件个数)&#xff1a;1290 标注数量(txt文件个数)&#xff1a;1290 标注类别数&#xff1a;4 标注类别名称&#xff1a;[Orange-Green-Black-Spot, Orange-Black-Spot, Orange-Canker, Orange…

CTFHUB | web进阶 | PHP | Bypass disable_function | Apache Mod CGI

开启题目 点击重置 backdoor 目录&#xff0c;再点击 GetFlag&#xff0c;然后发现了可以蚁剑连接&#xff0c; 连接成功发现无任何发现&#xff0c;所以我们使用 apache_mode_cgi插件 发现直接进入终端了&#xff0c;最后发现了 flag

深入解析Python内省之dir、getattr、hasattr和setattr使用详解

概要 内省(Introspection)是编程语言的一种能力,使程序能够在运行时检查对象的类型、属性和方法。Python提供了强大的内省工具,允许开发者动态地检查和操作对象的属性和方法。本文将详细介绍Python中的内省工具:dir、getattr、hasattr和setattr,并通过具体的示例代码展示…

springboot 股票资产管理系统-计算机毕业设计源码96208

摘要 随着全球金融市场的快速发展&#xff0c;股票交易和投资已经成为重要的经济活动之一。在此背景下&#xff0c;股票资产管理系统的设计与实现显得尤为重要。Spring Boot框架&#xff0c;以其快速、简洁和高效的特性&#xff0c;在股票资产管理系统的开发中得到了广泛应用。…

C++第三十二弹---从概念到实践:全面解析C++多态性

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1. 多态的概念 1.1 概念 2. 多态的定义及实现 2.1 多态的构成条件 2.2 虚函数 2.3 虚函数的重写 2.4 C11 override 和 final 2.5 重载、覆…

【Linux】安装部署docker及docker-compose

环境说明 操作系统&#xff1a;Ubuntu 22.04 架构&#xff1a;x86_64 一、docker安装 1.下载docker源码包 下载地址&#xff1a;https://download.docker.com/linux/static/stable/x86_64/ 注意&#xff1a;如果这个地址互联网打不开&#xff0c;那就开代理访问&#xff…

基于STM32的智能家居安全系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 初始化代码安全传感器读取代码应用场景 家居安全监控办公环境安全监控常见问题及解决方案 常见问题解决方案结论 1. 引言 在智能家居和办公环境中&#xff0c;安全系统是一个至关重要的组成…

这个低代码开发丝滑小连招,竟还能本地部署?

在这个飞速发展的数字化时代&#xff0c;我们深知每一位开发者对于高效、灵活、安全的开发环境有着不懈的追求。为此&#xff0c;JNPF低代码开发平台最新推出的V5.0版本现已全面支持用户免费本地部署体验&#xff01;这一重大福利&#xff0c;将为您带来前所未有的开发体验&…

leetcode数论(2280. 表示一个折线图的最少线段数)-几何

前言 经过前期的基础训练以及部分实战练习&#xff0c;粗略掌握了各种题型的解题思路。现阶段开始专项练习。 数论包含最大公约数(>2个数)、最大公约数性质、最小公倍数、区间范围质因素计数(最下间隔)、质因素分解、判断质数、平方根、立方根、互质、同余等等。 描述 给…

SwiftUI 如何定制 Picker 视图当前选中行的背景颜色?

功能需求 有时我们希望可以定制 SwiftUI 中 Picker 视图当前选中行的背景色,这可以做到吗? 在上面的演示图中,我们随心所欲地变换着 SwiftUI 中 Picker 视图当前选中行的背景色。这是怎么做到的呢? 在本篇博文中,您将学到以下内容 功能需求1. 钩深极奥:修改 SwiftUI 原…

为什么总是很难很好的理解电工电子知识?终于有了解决的办法!

理解电工电子知识可能具有挑战性&#xff0c;这可以由多种因素造成。以下是一些可能的原因&#xff1a; 抽象性高&#xff1a;电工电子学涉及到电流、电压、电阻、电容、电感等抽象概念&#xff0c;这些概念在日常生活中不易直接观察和体验&#xff0c;因此需要较高的抽象思维…

【nginx】centos7安装并配置开机自启

【nginx】配置开机自启 1.nginx配置开机自启 安装完成nginx之后 vim /lib/systemd/system/nginx.service[Unit] Descriptionnginx Afternetwork.target[Service] Typeforking ExecStart/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf ExecReload/usr/loc…

使用开源 LLM 充当 LangChain 智能体

太长不看版 开源 LLM 现已达到一定的性能水平&#xff0c;可堪作为智能体工作流的推理引擎。在我们的测试基准上&#xff0c;Mixtral 甚至已超越 GPT-3.5&#xff0c;而且我们还可以通过微调轻松地进一步提高其性能。 引言 经由因果语言建模任务训练出的大语言模型&#xff…