目录
- 核心概念:为什么需要服务器?
- 通信协议:HTTP/HTTPS
- 数据交换格式:JSON vs. XML
- Android 客户端实现
- 原生
HttpURLConnection(Java) - 第三方库:Volley, Retrofit, OkHttp
- 原生
- PHP 服务器端实现
- 环境准备
- 连接数据库
- 编写 API 接口
- 安全性考虑
- 完整示例:用户登录
- 高级主题与最佳实践
核心概念:为什么需要服务器?
Android 应用(客户端)通常不能直接操作数据库,服务器扮演了中间人的角色,主要职责包括:

- 数据持久化:将数据存储在服务器端的数据库中。
- 业务逻辑处理:执行复杂的计算、验证规则等,而不是放在客户端。
- 安全性:保护敏感数据(如数据库密码、API 密钥),防止客户端直接访问数据库。
- 跨平台:同一个 API 可以被 Android、iOS、Web 等多个客户端使用。
架构流程:
Android App <--(HTTP请求/JSON数据)--> PHP Server <--(SQL查询)--> MySQL Database <--(SQL结果)--> PHP Server <--(HTTP响应/JSON数据)--> Android App
通信协议:HTTP/HTTPS
客户端和服务器之间通过 HTTP (或更安全的 HTTPS) 协议进行通信。
-
HTTP 请求方法:
GET:请求数据,获取用户列表、获取文章详情,数据通常在 URL 的 Query String 中。POST:提交数据,用户注册、发布新文章,数据通常放在请求体中,更安全,且能传输更大量的数据。PUT/PATCH:更新数据。DELETE:删除数据。
-
HTTPS:强烈建议在生产环境中使用 HTTPS,它通过 SSL/TLS 加密了通信内容,可以防止数据在传输过程中被窃听或篡改。
(图片来源网络,侵删)
数据交换格式:JSON vs. XML
客户端和服务器交换数据时,需要一种通用的格式。JSON 是绝对的主流。
-
JSON (JavaScript Object Notation):
- 优点:轻量级、易于人阅读和编写、易于机器解析和生成,与 JavaScript 语法天然兼容,在 Android 中可以轻松转换为
JSONObject和JSONArray。 - 示例:
{ "status": "success", "user_id": 123, "message": "Login successful" }
- 优点:轻量级、易于人阅读和编写、易于机器解析和生成,与 JavaScript 语法天然兼容,在 Android 中可以轻松转换为
-
XML (eXtensible Markup Language):
- 优点:结构严谨,有规范的 DTD 或 Schema。
- 缺点:冗长、解析比 JSON 复杂,现在已较少用于移动端 API。
除非有特殊遗留系统要求,否则请毫不犹豫地选择 JSON。

Android 客户端实现
在 Android 中进行网络请求,不能在主线程(UI线程)中执行,否则会抛出 NetworkOnMainThreadException 异常,必须使用异步任务(如 AsyncTask - 已废弃)、线程 + Handler,或者现代的协程/ExecutorService。
原生 HttpURLConnection (不推荐用于新项目,但有助于理解原理)
// 在 Kotlin 中使用协程是现代 Android 开发的最佳实践
// 假设在 ViewModel 或使用 lifecycleScope.launch
fun loginUser(username: String, password: String) {
// 创建一个线程或使用协程来执行网络请求
CoroutineScope(Dispatchers.IO).launch {
val url = URL("https://yourdomain.com/api/login.php")
val connection = url.openConnection() as HttpURLConnection
connection.requestMethod = "POST"
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded")
connection.doOutput = true
// 构建请求体
val postData = "username=$username&password=$password"
// 发送请求
val outputStream = connection.outputStream
outputStream.write(postData.toByteArray())
outputStream.flush()
// 获取响应
if (connection.responseCode == HttpURLConnection.HTTP_OK) {
val inputStream = connection.inputStream
val response = inputStream.bufferedReader().use { it.readText() }
// 在主线程更新UI
withContext(Dispatchers.Main) {
Log.d("API Response", response)
// 解析 JSON 并更新UI
}
} else {
// 处理错误
Log.e("API Error", "HTTP Error: ${connection.responseCode}")
}
}
}
第三方库 (强烈推荐)
Retrofit (最流行)
Retrofit 是一个类型安全的 HTTP 客户端,它将 REST API 的调用转换为简单的 Java/Kotlin 接口。
第一步:添加依赖
// build.gradle (Module :app)
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // 用于JSON转换
}
第二步:定义 API 接口
import retrofit2.Call
import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded
import retrofit2.http.POST
interface ApiService {
@FormUrlEncoded
@POST("api/login.php")
fun loginUser(
@Field("username") username: String,
@Field("password") password: String
): Call<LoginResponse> // Call 是 Retrofit 的包装类
}
// 数据类 (data class) 用于解析 JSON 响应
data class LoginResponse(
val status: String,
val user_id: Int,
val message: String
)
第三步:创建 Retrofit 实例并调用
object RetrofitClient {
private const val BASE_URL = "https://yourdomain.com/"
val instance: ApiService by lazy {
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create()) // 添加Gson转换器
.build()
retrofit.create(ApiService::class.java)
}
}
// 在 Activity/ViewModel 中使用
fun performLogin() {
val apiService = RetrofitClient.instance
val call = apiService.loginUser("testuser", "password123")
call.enqueue(object : Callback<LoginResponse> {
override fun onResponse(call: Call<LoginResponse>, response: Response<LoginResponse>) {
if (response.isSuccessful) {
val loginResponse = response.body()
if (loginResponse?.status == "success") {
// 登录成功,更新UI
Log.d("Login", "User ID: ${loginResponse.user_id}")
}
} else {
// 服务器返回了错误码,如 404, 500
Log.e("Login Error", "Code: ${response.code()}")
}
}
override fun onFailure(call: Call<LoginResponse>, t: Throwable) {
// 网络请求失败,如无网络、服务器宕机
Log.e("Network Failure", t.message.toString())
}
})
}
优点:代码简洁、类型安全、易于维护,支持 RxJava 和协程。
Volley (适合小型、简单的请求)
Volley 是 Google 推荐的异步网络请求库,适合处理小量的、频繁的网络请求。
PHP 服务器端实现
环境准备
你需要一个支持 PHP 和 MySQL 的 Web 服务器环境,最简单的方式是安装 XAMPP (Windows, Linux, macOS) 或 MAMP (macOS),它们包含了 Apache、MySQL 和 PHP。
连接数据库
创建一个 db_config.php 文件来管理数据库连接信息,不要把密码直接写在代码里。
<?php
// db_config.php
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'root');
define('DB_PASSWORD', ''); // XAMPP 默认为空
define('DB_NAME', 'my_app_db');
// 创建连接
$conn = new mysqli(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
// 检查连接
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
?>
编写 API 接口
创建一个 api/login.php 文件,它将处理来自 Android 的请求。
<?php
// api/login.php
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Origin: *"); // 允许所有来源的请求,生产环境应指定域名
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
// 包含数据库配置
include_once 'db_config.php';
// 获取 POST 数据
$data = json_decode(file_get_contents("php://input"));
// 检查数据是否存在
if (!empty($data->username) && !empty($data->password)) {
$username = $data->username;
$password = $data->password;
// **重要:使用预处理语句防止 SQL 注入**
$stmt = $conn->prepare("SELECT id FROM users WHERE username = ? AND password = ?");
// 在实际应用中,密码应该被哈希存储,例如使用 password_hash()
// 这里为了简单,直接比较,请勿在生产环境这样做!
$hashed_password = md5($password); // 注意:md5 不安全,仅作示例
$stmt->bind_param("ss", $username, $hashed_password);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows > 0) {
// 登录成功
$stmt->bind_result($user_id);
$stmt->fetch();
$response = array(
"status" => "success",
"user_id" => $user_id,
"message" => "Login successful"
);
} else {
// 登录失败
$response = array(
"status" => "error",
"message" => "Invalid username or password"
);
}
$stmt->close();
} else {
// 数据为空
$response = array(
"status" => "error",
"message" => "Data is incomplete."
);
}
// 返回 JSON 响应
echo json_encode($response);
$conn->close();
?>
安全性考虑
- SQL 注入:永远不要将用户输入直接拼接到 SQL 查询中。必须使用预处理语句(如上例所示)。
- 密码安全:永远不要明文存储密码,使用
password_hash()和password_verify()函数来处理密码。 - HTTPS:在生产环境,强制使用 HTTPS。
- 输入验证:在 PHP 和 Android 端都要对用户输入进行验证和清理。
- CORS:
header("Access-Control-Allow-Origin: *");方便开发,但生产环境应将其替换为你的 App 域名,如header("Access-Control-Allow-Origin: https://your-android-app.com");。
完整示例:用户登录
流程总结:
-
Android (Retrofit):
- 用户在输入框输入用户名和密码,点击登录按钮。
performLogin()方法被调用,Retrofit 将username和password作为@Field发送到https://yourdomain.com/api/login.php。enqueue方法异步发送请求,并在回调中处理响应。status是"success",就保存user_id并跳转到主页,如果是"error",就显示错误提示。
-
PHP (
api/login.php):- 接收到 POST 请求,读取
username和password。 - 使用预处理语句查询数据库。
- 如果找到匹配的用户,返回
{"status": "success", "user_id": 123, ...}。 - 如果没找到,返回
{"status": "error", "message": "..."}。
- 接收到 POST 请求,读取
高级主题与最佳实践
- 身份验证与授权:登录成功后,服务器应返回一个 Token (如 JWT - JSON Web Token),后续的 API 请求都需要在 Header 中携带这个 Token,以证明用户身份。
- 分页:当数据量很大时(如新闻列表),使用
LIMIT和OFFSET或基于游标的分页来避免一次性加载过多数据。 - 文件上传:Android 使用
Multipart请求,PHP 使用$_FILES超全局变量来接收上传的文件。 - 错误处理:定义统一的错误码和错误信息格式,方便客户端统一处理。
- 日志记录:在服务器端记录 API 请求和错误日志,便于排查问题。
| 组件 | 关键技术/概念 | 推荐工具/实践 |
|---|---|---|
| Android 客户端 | 网络请求(不能在主线程)、JSON 解析 | Retrofit (首选), OkHttp, Volley |
| 通信协议 | HTTP/HTTPS | HTTPS (必须) |
| 数据格式 | JSON (绝对主流) | JSON |
| PHP 服务器端 | 处理请求、连接数据库、返回 JSON | 预处理语句 (防SQL注入), 密码哈希 |
| 数据库 | MySQL | 使用预处理语句与 PHP 交互 |
从 Android 调用 PHP API 是一个标准化的过程,掌握好 Retrofit 和 PHP + MySQL + 预处理语句 这套组合,你就能构建出功能强大且安全的移动应用后端服务。
