核心概念
无论使用哪种方法,基本流程都是一样的:

- 添加网络权限:在
AndroidManifest.xml中声明应用需要访问互联网。 - 发起网络请求:使用指定的 URL 向服务器发起一个 HTTP/HTTPS 请求。
- 获取响应:从服务器接收返回的数据,也就是网页的 HTML 源码。
- 处理数据:在主线程(UI 线程)之外处理网络数据,然后更新 UI。
使用 OkHttp (强烈推荐)
OkHttp 是目前 Android 开发中最流行、最高效的网络库,由 Square 公司开发,性能优异,支持异步请求,并且对现代网络协议(如 HTTP/2)有很好的支持。
步骤 1: 添加依赖
在项目的 app/build.gradle 文件中的 dependencies 代码块里添加 OkHttp 依赖:
dependencies {
// OkHttp
implementation("com.squareup.okhttp3:okhttp:4.12.0") // 请使用最新版本
}
步骤 2: 添加网络权限
在 注意:如果你的应用需要访问 在 Activity 或 ViewModel 中,使用 OkHttp 发起异步请求。 对应的布局文件 Retrofit 是一个类型安全的 HTTP 客户端,它基于 OkHttp,但提供了更高级的抽象,比如将 API 请求定义成接口,并支持自动解析 JSON、XML 等数据格式,虽然对于单纯获取 HTML 来说有点“杀鸡用牛刀”,但如果你需要和复杂的 API 交互,Retrofit 是不二之选。 在 创建一个接口来描述你的网络请求。 这是 Android 原生的网络 API,无需添加第三方库,但是它的 API 比较繁琐,不支持异步请求,需要自己开子线程,并且代码量更多,它通常只用于非常简单的场景或不想引入第三方库的项目。 不能在主线程进行网络操作
Android 的主线程(UI 线程)负责处理用户交互和绘制界面,如果在主线程进行网络请求,会导致应用无响应(ANR - Application Not Responding),所有网络请求都必须在子线程或使用异步框架(如 OkHttp 的 处理 数据量很大时
如果网页源码非常大(比如几 MB),直接用一个 解析源码 vs. 显示网页 使用 对于初学者和大多数应用,从 OkHttp 开始是最好的选择,它足够强大,学习曲线也不陡峭。app/src/main/AndroidManifest.xml 文件中添加 <uses-permission>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- 添加网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<application
...
android:usesCleartextTraffic="true" <!-- 如果请求HTTP网站,需要添加此行 -->
...>
...
</application>
</manifest>
http:// 协议的网站(不推荐,不安全),必须在 <application> 标签中添加 android:usesCleartextTraffic="true",从 Android 9 (API 28) 开始,默认禁止明文流量。步骤 3: 编写下载代码

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import okhttp3.*
import java.io.IOException
class MainActivity : AppCompatActivity() {
private val client = OkHttpClient()
private val sourceCodeTextView: TextView by lazy { findViewById(R.id.sourceCodeTextView) }
private val downloadButton: Button by lazy { findViewById(R.id.downloadButton) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
downloadButton.setOnClickListener {
// 示例:下载 "https://www.example.com" 的源码
downloadWebSourceCode("https://www.example.com")
}
}
private fun downloadWebSourceCode(url: String) {
// 创建一个请求对象
val request = Request.Builder()
.url(url)
.build()
// 异步执行请求
client.newCall(request).enqueue(object : Callback {
// 请求失败时调用
override fun onFailure(call: Call, e: IOException) {
// 网络错误或请求失败
runOnUiThread {
Toast.makeText(this@MainActivity, "下载失败: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
// 请求成功时调用
override fun onResponse(call: Call, response: Response) {
// response.body() 是响应体,即网页的源码
if (response.isSuccessful) {
val sourceCode = response.body?.string()
// 更新UI,必须在主线程执行
runOnUiThread {
sourceCodeTextView.text = sourceCode
}
} else {
// 服务器返回了错误状态码,如 404, 500 等
runOnUiThread {
Toast.makeText(this@MainActivity, "下载失败: HTTP ${response.code}", Toast.LENGTH_SHORT).show()
}
}
}
})
}
override fun onDestroy() {
super.onDestroy()
// 在Activity销毁时取消所有请求,防止内存泄漏
client.dispatcher.cancelAll()
}
}
activity_main.xml:<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<Button
android:id="@+id/downloadButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下载网页源码" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="16dp">
<TextView
android:id="@+id/sourceCodeTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点击按钮开始下载..."
android:textSize="12sp"
android:fontFamily="monospace" />
</ScrollView>
</LinearLayout>
使用 Retrofit (更高级的推荐)
步骤 1: 添加依赖
app/build.gradle 中添加 Retrofit 和 OkHttp 的依赖:dependencies {
// Retrofit
implementation("com.squareup.retrofit2:retrofit:2.9.0") // 请使用最新版本
// 用于接收原始字符串响应
implementation("com.squareup.retrofit2:converter-scalars:2.9.0")
}
步骤 2: 定义 API 接口

import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Url
interface WebService {
// @GET 注解表示这是一个 GET 请求
// @Url 表示 URL 是动态传入的
// Call<String> 表示我们期望将响应体解析成 String
@GET
fun fetchWebSource(@Url url: String): Call<String>
}
步骤 3: 创建 Retrofit 实例并发起请求
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.converter.scalars.ScalarsConverterFactory
class RetrofitActivity : AppCompatActivity() {
private lateinit var retrofit: Retrofit
private lateinit webService: WebService
private val sourceCodeTextView: TextView by lazy { findViewById(R.id.sourceCodeTextView) }
private val downloadButton: Button by lazy { findViewById(R.id.downloadButton) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) // 复用上面的布局
// 初始化 Retrofit
retrofit = Retrofit.Builder()
.baseUrl("https://www.example.com/") // baseUrl 可以是任意域名,因为我们用 @Url 覆盖了
.addConverterFactory(ScalarsConverterFactory.create()) // 添加字符串转换器
.build()
webService = retrofit.create(WebService::class.java)
downloadButton.setOnClickListener {
downloadWithRetrofit("https://www.example.com")
}
}
private fun downloadWithRetrofit(url: String) {
val call = webService.fetchWebSource(url)
call.enqueue(object : Callback<String> {
override fun onResponse(call: Call<String>, response: Response<String>) {
if (response.isSuccessful) {
val sourceCode = response.body()
runOnUiThread {
sourceCodeTextView.text = sourceCode
}
} else {
runOnUiThread {
Toast.makeText(this@RetrofitActivity, "下载失败: HTTP ${response.code()}", Toast.LENGTH_SHORT).show()
}
}
}
override fun onFailure(call: Call<String>, t: Throwable) {
runOnUiThread {
Toast.makeText(this@RetrofitActivity, "下载失败: ${t.message}", Toast.LENGTH_SHORT).show()
}
}
})
}
}
方法三: 使用
HttpURLConnection (原生 API,不推荐用于新项目)import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
class HttpURLConnectionActivity : AppCompatActivity() {
private val sourceCodeTextView: TextView by lazy { findViewById(R.id.sourceCodeTextView) }
private val downloadButton: Button by lazy { findViewById(R.id.downloadButton) }
private val handler = Handler(Looper.getMainLooper())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
downloadButton.setOnClickListener {
// 在子线程中执行网络请求
Thread {
downloadWithHttpURLConnection("https://www.example.com")
}.start()
}
}
private fun downloadWithHttpURLConnection(urlString: String) {
var connection: HttpURLConnection? = null
var reader: BufferedReader? = null
try {
val url = URL(urlString)
connection = url.openConnection() as HttpURLConnection
connection.requestMethod = "GET"
connection.connectTimeout = 15000
connection.readTimeout = 15000
val responseCode = connection.responseCode
if (responseCode == HttpURLConnection.HTTP_OK) {
val inputStream = connection.inputStream
reader = BufferedReader(InputStreamReader(inputStream))
val stringBuilder = StringBuilder()
var line: String?
while (reader.readLine().also { line = it } != null) {
stringBuilder.append(line).append("\n")
}
// 通过 Handler 在主线程更新 UI
handler.post {
sourceCodeTextView.text = stringBuilder.toString()
}
} else {
// 通过 Handler 在主线程显示错误信息
handler.post {
Toast.makeText(this, "下载失败: HTTP $responseCode", Toast.LENGTH_SHORT).show()
}
}
} catch (e: IOException) {
e.printStackTrace()
handler.post {
Toast.makeText(this, "下载失败: ${e.message}", Toast.LENGTH_SHORT).show()
}
} finally {
reader?.close()
connection?.disconnect()
}
}
}
重要注意事项和最佳实践
enqueue)。android:networkSecurityConfig
从 Android 7.0 (API 24) 开始,默认禁止使用不安全的 HTTPS 证书(用户自签名的证书),如果你的服务器使用的是自签名证书,需要在 AndroidManifest.xml 中配置 networkSecurityConfig。TextView 显示可能会导致性能问题,你可能需要只显示一部分内容,或者使用 RecyclerView 来展示。
WebView 组件,而不是下载源码。WebView 显示网页的示例:val webView: WebView = findViewById(R.id.webView)
webView.webViewClient = WebViewClient() // 防止使用系统浏览器打开
webView.loadUrl("https://www.example.com")
方法
优点
缺点
推荐场景
OkHttp
简单、高效、功能强大、社区活跃
需要添加依赖
绝大多数情况下的首选,尤其是简单的网络请求。
Retrofit
类型安全、代码优雅、易于维护、支持多种数据解析
比较重量级,对简单请求来说略显复杂
需要与复杂 API 交互的项目,或者团队已经在使用 Retrofit。
HttpURLConnection
无需第三方库、系统自带
API 繁琐、需要手动管理线程、功能有限
学习目的或极度轻量级的应用,不想引入任何第三方库。
