核心概念:为什么需要特殊处理?
在 Android 10 (API 29) 及更高版本中,Google 出于安全考虑,引入了分区存储和网络限制策略,默认情况下:

- 非 HTTP 网络限制:应用无法访问
http://协议的本地网络地址(如http://10.0.2.2:8080),只能访问https://,这是为了防止恶意应用在用户不知情的情况下扫描和访问本地网络。 - 分区存储:应用对文件系统的访问受到了更严格的限制,但这主要影响文件读写,对网络访问影响不大。
要让你的 Android App 成功访问本地服务器,你需要根据你的目标 Android 版本和服务器类型,采取不同的策略。
场景一:访问 PC 或其他设备上的服务器(最常见)
这是最常见的情况,比如你的本地服务器运行在你的开发机器(PC)上,你想用手机上的 App 去访问它。
步骤 1:确定 PC 的本地 IP 地址
你的手机和 PC 必须在同一个 Wi-Fi 网络下。
- 在 Windows PC 上,打开命令提示符或 PowerShell,输入
ipconfig,找到你正在使用的网卡的 "IPv4 地址",168.1.105。 - 在 macOS 上,打开终端,输入
ifconfig,找到 "en0" 或 "en1" 下的 "inet" 地址,168.1.105。
步骤 2:在 Android 代码中使用 PC 的 IP 地址
在你的 Android App 的网络请求代码中,不要使用 localhost 或 0.0.1,因为这些指向的是手机本身,你应该使用上一步中找到的 PC 的 IP 地址。

错误的地址:
http://localhost:8080/apihttp://127.0.0.1:8080/apihttp://10.0.2.2:8080/api(这个地址是 Android 模拟器访问宿主 PC 的专用地址,真机不适用)
正确的地址:
http://192.168.1.105:8080/api
步骤 3:处理 Android 网络限制(关键!)
如果你的应用 targetSdkVersion 是 28 或更高(强烈建议),并且运行在 Android 9 (Pie) 或更高版本的设备上,你必须在 AndroidManifest.xml 中声明 usesCleartextTraffic。
<!-- AndroidManifest.xml -->
<manifest ...>
<!-- 允许应用在非加密的网络上传输数据 -->
<uses-permission android:name="android.permission.INTERNET" />
<!--
android:usesCleartextTraffic="true"
这行是关键!它允许应用使用 HTTP 协议。
如果你的服务器是 HTTPS,可以不设置或设为 false。
-->
<application
android:usesCleartextTraffic="true"
...>
...
</application>
</manifest>
对于 Android 10 (API 29) 及更高版本:
如果你只希望应用在调试时能访问本地 HTTP 服务器,正式发布时要求更安全,可以使用 networkSecurityConfig。
-
在
res/xml/目录下创建一个network_security_config.xml文件:<!-- res/xml/network_security_config.xml --> <network-security-config> <domain-config cleartextTrafficPermitted="true"> <!-- 将 your-local-server-ip �换成你的PC IP地址 --> <domain includeSubdomains="true">192.168.1.105</domain> </domain-config> </network-security-config> -
在
AndroidManifest.xml的<application>标签中引用它:<!-- AndroidManifest.xml --> <application ... android:networkSecurityConfig="@xml/network_security_config" ...> ... </application>
这种方式更安全,因为它只允许访问你指定的 IP 地址,而不是所有 HTTP 流量。
场景二:访问手机本机上的服务器
如果你的应用需要在手机上启动一个服务器(例如一个 HTTP 服务器来提供文件),然后让 App 的其他部分访问它。
- 地址:在这种情况下,你应该使用
0.2.2这个特殊地址,这个地址是 Android 模拟器用来访问宿主(开发机器)的代理地址。在真机上,0.2.2是无效的。 - 真机方案:在真机上,如果你想访问本机服务器,最可靠的方法是使用
localhost或0.0.1。- Android 8.0 (Oreo) 及以下:直接使用
http://localhost:8080通常可以工作。 - Android 9 (Pie) 及以上:由于网络限制,直接使用
localhost可能会失败,你需要结合usesCleartextTraffic="true"和networkSecurityConfig来允许访问localhost。
- Android 8.0 (Oreo) 及以下:直接使用
network_security_config.xml 示例(允许访问 localhost):
<!-- res/xml/network_security_config.xml -->
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">localhost</domain>
</domain-config>
</network-security-config>
代码实现示例(使用 Retrofit + OkHttp)
现代 Android 开发中,使用 Retrofit 和 OkHttp 是进行网络请求的最佳实践。
添加依赖 (build.gradle.kts / build.gradle)
// build.gradle (Module :app)
dependencies {
// Retrofit for networking
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // For JSON parsing
// OkHttp for logging (optional but highly recommended)
implementation "com.squareup.okhttp3:logging-interceptor:4.9.3"
}
创建 API 接口
// ApiService.kt
import retrofit2.Call
import retrofit2.http.GET
interface ApiService {
@GET("api/data") // 假设你的本地服务器有这个接口
fun getData(): Call<List<String>>
}
创建 Retrofit 实例
// RetrofitClient.kt
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
object RetrofitClient {
// !!! 替换成你的 PC IP 地址 !!!
private const val BASE_URL = "http://192.168.1.105:8080/"
val instance: ApiService by lazy {
// 创建一个 OkHttp 客户端,可以添加拦截器(如日志)
val okHttpClient = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.build()
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
}
在 Activity 或 ViewModel 中调用
// MainActivity.kt
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
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val textView: TextView = findViewById(R.id.textView)
val button: Button = findViewById(R.id.button)
button.setOnClickListener {
fetchData(textView)
}
}
private fun fetchData(textView: TextView) {
RetrofitClient.instance.getData().enqueue(object : Callback<List<String>> {
override fun onResponse(call: Call<List<String>>, response: Response<List<String>>) {
if (response.isSuccessful) {
val data = response.body()
textView.text = "Data from server: $data"
} else {
textView.text = "Error: ${response.code()}"
Toast.makeText(this@MainActivity, "Request failed: ${response.message()}", Toast.LENGTH_SHORT).show()
}
}
override fun onFailure(call: Call<List<String>>, t: Throwable) {
textView.text = "Failure: ${t.message}"
Toast.makeText(this@MainActivity, "Network error: ${t.message}", Toast.LENGTH_SHORT).show()
}
})
}
}
常见问题排查清单
如果连接失败,请按以下顺序检查:
-
IP 地址是否正确?
- 确保手机和 PC 在同一个 Wi-Fi 下。
- 确保你使用的是 PC 的 局域网 IP(如
168.x.x),而不是公网 IP。 - 在手机浏览器中输入
http://你的PCIP:端口,看是否能正常访问服务器,如果浏览器也无法访问,说明问题出在服务器端或网络连接上。
-
端口是否开放且正确?
- 确保服务器监听的端口是你代码中使用的端口。
- 检查 PC 防火墙是否阻止了该端口的入站连接,你可以尝试暂时关闭防火墙进行测试。
-
targetSdkVersion和usesCleartextTraffic?- 检查你的
build.gradle文件中的targetSdkVersion。 targetSdkVersion >= 28,请务必在AndroidManifest.xml中添加android:usesCleartextTraffic="true"或使用networkSecurityConfig。
- 检查你的
-
服务器是否正在运行?
这是最基本但最容易忽略的一点,确保你的本地服务器应用(如 Node.js, Python Flask, Java Spring Boot)已经成功启动。
-
是否需要模拟器?
- 如果你用模拟器测试,记得使用
0.2.2来访问宿主 PC 的服务器,而不是localhost。
- 如果你用模拟器测试,记得使用
-
检查 OkHttp 日志
- 如代码示例所示,添加
HttpLoggingInterceptor,它会打印出详细的请求和响应信息,是调试网络问题的利器。
- 如代码示例所示,添加
| 场景 | 目标地址 | 关键配置 |
|---|---|---|
| 访问 PC 上的服务器 | http://PC的局域网IP:端口 |
android:usesCleartextTraffic="true" 或 networkSecurityConfig |
| 访问手机本机服务器 | http://localhost:端口 (真机) |
android:usesCleartextTraffic="true" + networkSecurityConfig (针对 Android 9+) |
| 访问手机本机服务器 | http://10.0.2.2:端口 (仅限模拟器) |
android:usesCleartextTraffic="true" |
遵循以上步骤和注意事项,你就可以顺利地在 Android 应用中访问本地服务器了。
