在 Android 4.0 时代,有两个主流的网络访问方式:

- HttpURLConnection:这是 Google 推荐的标准方式,功能强大,相对简单。
- Apache HttpClient:在早期版本中非常流行,但在 Android 6.0 (API 23) 中已被彻底移除。
重要提示:虽然 Android 4.0 仍然支持 HttpClient,但为了代码的长期可维护性和未来的兼容性,强烈建议你优先使用 HttpURLConnection。
下面我将围绕 HttpURLConnection 展开讲解,因为它更符合现代 Android 开发的最佳实践。
核心步骤
在 Android 中进行网络操作,通常遵循以下步骤:
- 添加网络权限:在
AndroidManifest.xml中声明应用需要访问网络的权限。 - 选择执行线程:网络操作绝对不能在主线程(UI线程)中执行,否则会抛出
NetworkOnMainThreadException异常并可能导致应用无响应,必须在后台线程(如AsyncTask,Thread,Thread + Handler,ExecutorService或现代的Coroutine/RxJava)中执行。 - 创建和配置 HttpURLConnection:指定 URL,设置请求方法(GET/POST等),添加请求头等。
- 发送请求并获取响应码:对于 GET 请求,此步骤会直接获取服务器响应。
- 读取服务器返回的数据流:将输入流转换为字符串(如 JSON 或 XML)。
- 解析数据并更新 UI:将解析后的数据通过
Handler或runOnUiThread等方式传递回主线程,以更新 UI。
详细代码示例 (使用 HttpURLConnection 和 AsyncTask)
这是一个完整的、在 Android 4.0+ 环境下工作的示例,使用 AsyncTask 在后台线程执行网络请求,并在主线程更新 UI。

添加网络权限
打开你的 在 在 Activity 中,我们将创建一个继承自 为什么用 AsyncTask?
在 Android 4.0 时代, 注意:app/src/main/AndroidManifest.xml 文件,在 <manifest> 标签内添加 <uses-permission>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.networkdemo">
<!-- 1. 添加网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<application
... >
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
创建布局文件
res/layout/activity_main.xml 中添加一个用于显示结果的 TextView。<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="点击按钮获取网络数据"
android:textSize="18sp" />
<Button
android:id="@+id/btn_fetch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_result"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:text="获取数据" />
</RelativeLayout>
编写网络请求逻辑 (AsyncTask)
AsyncTask 的内部类来处理网络请求。AsyncTask 是处理后台任务并更新 UI 的标准方式,它封装了线程和 Handler 的复杂性,非常适合这种“一次性”的网络请求。AsyncTask 在 Android 3.0 (Honeycomb) 后的默认行为是串行执行,所有任务会按顺序排队执行,对于简单的网络请求,这通常不是问题。
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class MainActivity extends Activity {
private TextView tvResult;
private Button btnFetch;
// 一个用于测试的公开API
private static final String API_URL = "https://jsonplaceholder.typicode.com/todos/1";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvResult = findViewById(R.id.tv_result);
btnFetch = findViewById(R.id.btn_fetch);
btnFetch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 当点击按钮时,启动AsyncTask
new NetworkTask().execute(API_URL);
}
});
}
/**
* 定义一个AsyncTask来执行网络请求
* Params: 传入的参数类型,这里是String类型的URL
* Progress: 后台任务进度的类型,这里不需要
* Result: 后台任务返回结果的类型,这里是String类型(JSON数据)
*/
private class NetworkTask extends AsyncTask<String, Void, String> {
// 4.0+ 可以显示一个ProgressDialog来提示用户等待
// ProgressDialog progressDialog;
@Override
protected void onPreExecute() {
super.onPreExecute();
// 在任务开始前执行,通常在UI线程上显示一个加载对话框
// progressDialog = ProgressDialog.show(MainActivity.this, "请稍候", "正在加载数据...");
tvResult.setText("正在加载...");
}
@Override
protected String doInBackground(String... urls) {
// 3. 在后台线程中执行网络请求
if (urls == null || urls.length == 0) {
return null;
}
String urlString = urls[0];
HttpURLConnection urlConnection = null;
BufferedReader reader = null;
StringBuilder response = new StringBuilder();
try {
URL url = new URL(urlString);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET"); // 设置请求方法为GET
urlConnection.setConnectTimeout(15000); // 设置连接超时时间(毫秒)
urlConnection.setReadTimeout(15000); // 设置读取超时时间(毫秒)
urlConnection.connect(); // 发起请求
// 4. 获取响应码
int responseCode = urlConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) { // 200 OK
// 5. 读取输入流
InputStream inputStream = urlConnection.getInputStream();
if (inputStream == null) {
return null;
}
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
response.append(line).append("\n");
}
} else {
// 服务器返回错误
return "Error: Server returned HTTP " + responseCode;
}
} catch (IOException e) {
e.printStackTrace();
return "Error: " + e.getMessage();
} finally {
// 6. 关闭连接和流
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return response.toString();
}
@Override
protected void onPostExecute(String result) {
// 7. 任务完成后,在UI线程上更新UI
super.onPostExecute(result);
// if (progressDialog != null && progressDialog.isShowing()) {
// progressDialog.dismiss();
// }
if (result != null) {
tvResult.setText(result);
} else {
tvResult.setText("获取数据失败");
Toast.makeText(MainActivity.this, "网络请求失败", Toast.LENGTH_SHORT).show();
}
}
}
}
关键点解析
AsyncTask 的三个泛型参数:
Params: doInBackground 方法的输入参数类型,我们传入一个 String (URL)。Progress: onProgressUpdate 方法的参数类型,我们不需要进度更新,所以用 Void。Result: doInBackground 的返回值和 onPostExecute 的输入参数类型,我们返回一个 String (JSON 数据)。doInBackground(String... urls):
try-catch-finally 块来处理异常
