1、概述
在现代 Android 开发中,网络请求和数据处理是构建应用程序的重要组成部分。本文详细介绍了多线程在 Android 中实现网络请求的重要性和必要性,以及如何在 Android 中实现网络请求,通过 HttpURLConnection 获取数据,并使用 Gson 库解析 JSON 数据,最后还完成了一项天气预报实战项目。
2、多线程
在学习 Android 在 Web 中的应用之前,我们首先需要了解 Android 中的多线程。因为 Android 应用的用户界面在主线程(UI 线程)中运行。长时间的操作(如网络请求、数据库访问等)会阻塞主线程,导致应用无响应。这时候我们就要借助多线程将耗时操作放在后台线程中,确保 UI 线程保持响应,使用户体验更加流畅。所以,在实现网络请求时,离不开多线程操作。
2.1、创建一个新线程
创建一个新线程很简单,只需创建一个 Thread 对象并重写其run()方法即可。
new Thread(new Runnable() {
@Override
public void run() {
// 执行耗时操作
}
}).start();
2.2、Handler实现多线程通信
为了确保应用的稳定性和线程安全,只有主线程可以安全地更新UI,我们不能在子线程中执行UI界面更新操作。当子线程里需要执行UI界面更新操作时,正确的做法是借助Handler机制将UI 更新请求发送回主线程。
Handler实现原理
Handler
:
是用于处理消息和执行任务的组件它允许你在特定线程(通常是主线程)中执行代码,并将消息或 Runnable 添加到消息队列中。
功能:
(1)通过方法sentMessage()
,子线程发送消息(Message)到消息队列(MessageQuene)中。
(2)通过重写handleMessage()
方法,接收Looper传来的消息(Message),并进行相应的处理。Message:是代表要执行的操作或任务的对象。它可以携带数据,并指示Handler执行的具体操作。
功能:
(1)包含标识符(what字段),用于区分不同类型的消息。
(2)可以携带任意数据(通过 obj、arg1、arg2 等字段)。Looper:负责循环处理消息队列(MessageQuene)中的消息。每个线程可以有一个 Looper,但通常只有主线程会有一个。
功能:从 MessageQueue 中取出消息并交给相应的 Handler 进行处理。MessageQuene:是一个队列,存储所有待处理的消息和任务。
功能:持有多个 Message 对象,按顺序排队等待Looper 从中获取消息并进行处理。
代码实现
private Handler myhandler = new Handler(/*主线程自带的Looper*/Looper.myLooper()){
@Override//
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
//处理消息,可执行更新UI请求
}
};
new Thread(new Runnable() {
@Override
public void run() {
String string = "要传递的参数";
Message message = new Message();
message.what = 0;//区分不同类型的消息
message.obj = string;
myhandler.sendMessage(message);//发送消息到MessageQueue
}
}).start();
3、Android中的网络请求
3.1、HttpURLConnection和URL
HttpURLConnection 类是 Java 中用于发送 HTTP 请求和接收 HTTP 响应的一个类,广泛应用于 Android 开发中。它提供了一个简单且灵活的接口,使得与服务器进行通信变得相对容易。
主要方法及功能:
connect():建立到目标 URL 的连接。
setRequestMethod(String method):设置请求方法(如 GET、POST、PUT、DELETE 等)。
setRequestProperty(String key, String value):设置请求头信息。可以用来设置如 Content-Type、User-Agent、Authorization 等。
getInputStream():获取响应体的输入流,用于读取服务器返回的数据。
setConnectTimeout(int timeout):设置连接超时的时间,单位为毫秒。
URL 类是 Java 中用于表示和操作统一资源定位符的一个重要类。它提供了一种方便的方式来处理和解析 URL,包括获取 URL 的各个组成部分。
主要构造方法及功能:
URL(String spec):根据给定的字符串创建一个新的 URL 对象。
主要方法及功能:
openConnection():打开与该 URL 的连接,返回 URLConnection 对象。
3.2、使用HttpURLConnection获取网站数据的过程
流程解读:
打开指定 URL 的连接:调用 openConnection() 方法创建一个HttpURLConnection 对象,准备与远程服务器进行通信。
调用 connect() 方法:该步骤会尝试连接到指定的 URL,准备发送请求并获取响应。
InputStreamReader:用于将机器能识别的二进制流转换为人易读懂的字符串。
BufferedReader:BufferedReader 提供了方便的方法(如 readLine())来逐行读取数据,减少读取时的 I/O 操作。
代码实现如下
//提供URL
String url = "你的url";
URL requestUrl = new URL(url);
//打开指定 URL 的连接,获取连接
httpURLConnection = (HttpURLConnection) requestUrl.openConnection();
//调用 connect() 方法以建立与服务器的连接
httpURLConnection.connect();
//读取并处理响应数据
//InputStream获取二进制输入流
InputStream inputStream = httpURLConnection.getInputStream();
//用InputStreamReader将字节流转换为字符流后存入BufferedReader
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
//从BufferedReader中获取字符串
String line;
StringBuilder stringBuilder = new StringBuilder();
//检测每一行,不为空就将字符串打印出来
while ((line = bufferedReader.readLine())!=null){
stringBuilder.append(line);
stringBuilder.append("\n");
}
//将处理数据返回
String data;
if(stringBuilder.length()==0){
data = null;
}else{
data = stringBuilder.toString();
}
注意:当应用需要进行网络操作时,必须声明访问互联网的权限,否则任何网络请求都会抛出SecurityException异常。所以必须得在 AndroidManifest.xml 文件中声明
<uses-permission android:name="android.permission.INTERNET"/>
3.3、JSON 格式与 Gson 类
由于网络获取的数据一般为 JSON 格式的,所以我们有必要了解 JSON 及其数据转换方法。
JSON 是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。它基于 JavaScript 语言的一个子集,但独立于语言,广泛用于数据传输和存储。数据以键值对的方式保存,示例如下
{
"name": "Alice",
"age": 30,
"isStudent": false
}
Gson 是一个由 Google 提供的 Java 库,用于将 Java 对象转换为 JSON 格式,或将 JSON 字符串解析为 Java 对象。
在使用 Gson 类之前,要先在build.gradle文件加入依赖:
implementation("com.google.code.gson:gson:2.8.5")
将 JSON 转换为 Java 对象:
MyClass myObject = gson.fromJson(jsonString, MyClass.class);
将 JSON 转换为 Java 对象后,我们就可以通过对象的属性直接访问数据,而不需要解析复杂的字符串,处理数据就方便很多了
4、实战应用:通过 HttpURLConnection 实现天气预报功能
4.1、基本原理
通过天气预报网站的一些免费实时预报接口,获取对应地区的当前天气数据,显示在我们自己制作的UI界面上。
4.2、效果展示
在输入框中输入你想查询的城市,点击查询天气按钮,显示相应的信息。app进入默认显示城市“福州”的实时天气。
4.3、实现代码
4.3.1、准备免费实况天气接口
我用的是网站易客云天气API免费天气API接口|天气预报接口|全球天气API接口|气象预警|空气质量的免费接口“http://v1.yiketianqi.com/free/day”
4.3.2、写好UI界面 activity_main.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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:onClick="start"
android:text="@string/查询天气"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
<EditText
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="请输入城市"
android:gravity="center"
android:textSize="20dp"
android:text="福州"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/linear1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button">
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="更新时间:"
android:textSize="20sp" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="TextView"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/linear2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linear1">
<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="天气情况:"
android:textSize="20sp" />
<TextView
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="TextView"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/linear3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linear2">
<TextView
android:id="@+id/textView6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="实时温度:"
android:textSize="20sp" />
<TextView
android:id="@+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="TextView"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/linear4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linear3">
<TextView
android:id="@+id/textView8"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="最高温:"
android:textSize="20sp" />
<TextView
android:id="@+id/textView9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="TextView"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/linear5"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linear4">
<TextView
android:id="@+id/textView10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="最低温:"
android:textSize="20sp" />
<TextView
android:id="@+id/textView11"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="TextView"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/linear6"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linear5">
<TextView
android:id="@+id/textView12"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="风向:"
android:textSize="20sp" />
<TextView
android:id="@+id/textView13"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="TextView"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/linear7"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linear6">
<TextView
android:id="@+id/textView14"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="风力等级:"
android:textSize="20sp" />
<TextView
android:id="@+id/textView15"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="TextView"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/linear8"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linear7">
<TextView
android:id="@+id/textView16"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="风速:"
android:textSize="20sp" />
<TextView
android:id="@+id/textView17"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="TextView"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/linear9"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="1dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linear8">
<TextView
android:id="@+id/textView18"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="空气质量:"
android:textSize="20sp" />
<TextView
android:id="@+id/textView19"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="TextView"
android:textSize="20sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
4.3.3、WeatherBean 实体类
用于接收 JSON 数据转换成的对象
package com.example.web;
import com.google.gson.annotations.SerializedName;
public class WeatherBean {
@SerializedName("cityid")
private String cityId;
private String city;
@SerializedName("update_time")
private String updateTime;
private String wea;
private String tem;
@SerializedName("tem_day")
private String temDay;
@SerializedName("tem_night")
private String temNight;
private String win;
@SerializedName("win_speed")
private String winSpeed;
@SerializedName("win_meter")
private String winMeter;
private String air;
public String getCityId() {
return cityId;
}
public String getCity() {
return city;
}
public String getUpdateTime() {
return updateTime;
}
public String getWea() {
return wea;
}
public String getTem() {
return tem;
}
public String getTemDay() {
return temDay;
}
public String getTemNight() {
return temNight;
}
public String getWin() {
return win;
}
public String getWinSpeed() {
return winSpeed;
}
public String getWinMeter() {
return winMeter;
}
public String getAir() {
return air;
}
@Override
public String toString() {
return "WeatherBean{" +
"cityId='" + cityId + '\'' +
", city='" + city + '\'' +
", updateTime='" + updateTime + '\'' +
", wea='" + wea + '\'' +
", tem='" + tem + '\'' +
", temDay='" + temDay + '\'' +
", temNight='" + temNight + '\'' +
", win='" + win + '\'' +
", winSpeed='" + winSpeed + '\'' +
", winMeter='" + winMeter + '\'' +
", air='" + air + '\'' +
'}';
}
}
4.3.4、NetUtil 工具类
NetUtil 类是一个用于网络请求的工具类,主要用于获取天气数据。
getWeatherOfCity(String city) 方法根据指定的城市名称构建完整的天气 API 请求 URL,并调用doGet 方法发送请求并获取天气数据,返回获取的天气结果字符串。
doGet(String url) 方法建立与指定 URL 的 HTTP 连接,返回从服务器获取的响应内容。
package com.example.web;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class NetUtil {
public static String BASE_URL = "http://v1.yiketianqi.com/free/day";
public static String APP_ID = "36646882";
public static String APP_SECRET = "Rx83gNDE";
public static String doGet(String url){
try {
//1、建立连接
HttpURLConnection httpURLConnection = null;
URL requestUrl = new URL(url);
httpURLConnection = (HttpURLConnection) requestUrl.openConnection();
httpURLConnection.setRequestMethod("GET");
httpURLConnection.setConnectTimeout(5000);
httpURLConnection.setRequestProperty("Charset","utf-8");
httpURLConnection.connect();
//2、获取的二进制流
InputStream inputStream = httpURLConnection.getInputStream();
//3、将二进制流包装
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
//4、从BufferedReader中获取字符串
String line;
StringBuilder stringBuilder = new StringBuilder();
//检测每一行,不为空就将字符串打印出来
while ((line = bufferedReader.readLine())!=null){
stringBuilder.append(line);
stringBuilder.append("\n");
}
if(stringBuilder.length()==0){
return null;
}
return stringBuilder.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static String getWeatherOfCity(String city){
//拼接出get请求的url
String weatherUrl = BASE_URL+"?"+"appid="+APP_ID+"&appsecret="+APP_SECRET+"&unescape=1"+"&city="+city;
String weatherResult = doGet(weatherUrl);
return weatherResult;
}
}
4.3.5、MainActivity 类
在 MainActivity 类里面实现开辟新线程执行网络请求,并将获取的响应信息由JSON格式转换为WeatherBean对象,用对象属性更改UI界面的过程。
package com.example.web;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.google.gson.Gson;
public class MainActivity extends AppCompatActivity {
private TextView content;
private Handler myhandler = new Handler(/*主线程自带的Looper*/Looper.myLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what==0){
String strData = (String) msg.obj;
parseJsonDataAndShow(strData);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start(findViewById(R.id.button));
}
public void start(View view){
new Thread(new Runnable() {
@Override
public void run() {
TextView city = findViewById(R.id.textView);
String string = getString(city.getText().toString());//发送网络请求
Message message = new Message();
message.what = 0;
message.obj = string;
myhandler.sendMessage(message);
}
}).start();
}
//网络请求方法
public String getString(String city) {
return NetUtil.getWeatherOfCity(city);
}
//转换JSON数据并更改UI界面
public void parseJsonDataAndShow(String jsonStr){
Log.d("fan",jsonStr);
Gson gson = new Gson();
WeatherBean weatherBean = gson.fromJson(jsonStr,WeatherBean.class);
String updateTime = weatherBean.getUpdateTime();
String wea = weatherBean.getWea();
String tem = weatherBean.getTem();
String temDay = weatherBean.getTemDay();
String temNight = weatherBean.getTemNight();
String win = weatherBean.getWin();
String winSpeed = weatherBean.getWinSpeed();
String winMeter = weatherBean.getWinMeter();
String air = weatherBean.getAir();
TextView tvUpdateTime = findViewById(R.id.textView3);
TextView tvWea = findViewById(R.id.textView5);
TextView tvTem = findViewById(R.id.textView7);
TextView tvTemDay = findViewById(R.id.textView9);
TextView tvTemNight = findViewById(R.id.textView11);
TextView tvWin = findViewById(R.id.textView13);
TextView tvWinSpeed = findViewById(R.id.textView15);
TextView tvWinMeter = findViewById(R.id.textView17);
TextView tvAir = findViewById(R.id.textView19);
tvUpdateTime.setText(updateTime);
tvWea.setText(wea);
tvTem.setText(tem);
tvTemDay.setText(temDay);
tvTemNight.setText(temNight);
tvWin.setText(win);
tvWinSpeed.setText(winSpeed);
tvWinMeter.setText(winMeter);
tvAir.setText(air);
}
}
5、总结与延伸
本文展示了在 Android 中实现网络请求和数据解析的基本流程。需要注意的是,在实际开发中,考虑到易用性和功能,我们一般倾向于使用更高级的库,如 Retrofit 或 OkHttp,这些库在 HttpURLConnection 的基础上提供了更丰富的功能和更简洁的接口。有了HttpURLConnection的基础,接下来学习这些这些库便不是难事了。
标签:return,String,网络应用,URL,实践,import,Andorid,new,public From: https://blog.csdn.net/2301_76183769/article/details/144432860