这篇指南将从零开始,涵盖从环境搭建、核心 API 设计,到 Android 客户端调用的完整流程。

目录
-
第一部分:PHP 服务器端
- 1 环境搭建 (推荐 XAMPP)
- 2 数据库设计 (MySQL)
- 3 核心概念:RESTful API
- 4 实践:编写 PHP API
- 连接数据库
- 实现用户注册接口 (
/register.php) - 实现用户登录接口 (
/login.php) - 实现获取数据接口 (
/get_data.php)
- 5 安全性考虑 (非常重要!)
-
第二部分:Android 客户端
- 1 网络请求库 (推荐 Retrofit + OkHttp)
- 2 实践:调用 PHP API
- 添加依赖
- 创建数据模型 (POJO/Model)
- 定义 Retrofit 接口
- 实现网络请求
-
第三部分:进阶与最佳实践
第一部分:PHP 服务器端
1 环境搭建
对于初学者,最简单的方式是使用集成环境包,如 XAMPP 或 WAMP。

- 下载并安装 XAMPP:从 Apache Friends 官网 下载适合你操作系统的版本。
- 启动 XAMPP Control Panel。
- 启动 Apache (Web 服务器) 和 MySQL (数据库服务器)。
- 创建项目目录:
- 找到 XAMPP 的安装目录,进入
htdocs文件夹。 - 在
htdocs中创建一个新文件夹,my_android_app。 - 你的 PHP 项目根目录就是
http://localhost/my_android_app/。
- 找到 XAMPP 的安装目录,进入
2 数据库设计
我们需要一个简单的用户表来存储数据。
- 打开浏览器,访问
http://localhost/phpmyadmin/。 - 点击“新建”,创建一个数据库,命名为
app_db。 - 在
app_db中,执行以下 SQL 语句创建users表:
CREATE TABLE `users` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `username` VARCHAR(50) NOT NULL UNIQUE, `email` VARCHAR(100) NOT NULL UNIQUE, `password` VARCHAR(255) NOT NULL, -- 存储哈希后的密码 `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
注意:password 字段我们使用 VARCHAR(255),因为后面我们会存储哈希值,它比原始密码长得多。
3 核心概念:RESTful API
你的 Android 应用和 PHP 服务器之间通过 API (应用程序编程接口) 进行通信,现代 Web API 遵循 RESTful 风格,其核心是:
- 使用 HTTP 动词:
GET: 获取数据 (获取用户信息)。POST: 创建新数据 (注册新用户)。PUT: 更新数据 (修改用户资料)。DELETE: 删除数据 (删除用户)。
- 使用 URL 表示资源:
http://yourdomain.com/api/users-> 代表所有用户资源。http://yourdomain.com/api/users/123-> 代表 ID 为 123 的用户资源。
- 数据格式:通常使用 JSON,因为它轻量且易于被 Android 解析。
4 实践:编写 PHP API
我们将创建几个 PHP 文件来处理不同的请求,所有文件都放在 htdocs/my_android_app/api/ 目录下。

api/config.php - 数据库配置文件
<?php
// 数据库连接配置
define('DB_SERVER', 'localhost');
define('DB_USERNAME', 'root'); // XAMPP 默认用户名
define('DB_PASSWORD', ''); // XAMPP 默认密码为空
define('DB_NAME', 'app_db');
// 创建连接
$conn = new mysqli(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
// 检查连接
if ($conn->connect_error) {
// 在生产环境中,不要直接输出错误信息,而是记录到日志
die("Connection failed: " . $conn->connect_error);
}
// 设置字符集,防止中文乱码
$conn->set_charset("utf8mb4");
?>
api/register.php - 处理用户注册
这个脚本接收 POST 请求,包含 username, email, password。
<?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");
// 1. 引入配置和必要的库
require_once 'config.php';
require_once 'vendor/autoload.php'; // 如果使用 Composer 的 password_hash
// 2. 获取 POST 数据
$data = json_decode(file_get_contents("php://input"));
// 3. 验证数据
if (!empty($data->username) && !empty($data->email) && !empty($data->password)) {
$username = $data->username;
$email = $data->email;
// 4. 密码哈希 (非常重要!)
$hashed_password = password_hash($data->password, PASSWORD_DEFAULT);
// 5. 准备 SQL 语句防止 SQL 注入
$sql = "INSERT INTO users (username, email, password) VALUES (?, ?, ?)";
if ($stmt = $conn->prepare($sql)) {
// 绑定参数
$stmt->bind_param("sss", $username, $email, $hashed_password);
// 执行
if ($stmt->execute()) {
// 注册成功
http_response_code(201); // Created
echo json_encode(array("message" => "User was created."));
} else {
// 可能是用户名或邮箱已存在
http_response_code(503); // Service unavailable
echo json_encode(array("message" => "Unable to create user. User or email may already exist."));
}
$stmt->close();
}
} else {
// 数据为空
http_response_code(400); // Bad request
echo json_encode(array("message" => "Data is incomplete."));
}
$conn->close();
?>
api/login.php - 处理用户登录
<?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");
require_once 'config.php';
$data = json_decode(file_get_contents("php://input"));
if (!empty($data->email) && !empty($data->password)) {
$email = $data->email;
$sql = "SELECT id, username, password FROM users WHERE email = ?";
if ($stmt = $conn->prepare($sql)) {
$stmt->bind_param("s", $email);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
$user = $result->fetch_assoc();
// 验证密码
if (password_verify($data->password, $user['password'])) {
// 登录成功
http_response_code(200); // OK
echo json_encode(array(
"message" => "Login successful.",
"user" => array(
"id" => $user['id'],
"username" => $user['username']
)
));
} else {
// 密码错误
http_response_code(401); // Unauthorized
echo json_encode(array("message" => "Invalid password."));
}
} else {
// 用户不存在
http_response_code(401); // Unauthorized
echo json_encode(array("message" => "User not found."));
}
$stmt->close();
}
} else {
http_response_code(400);
echo json_encode(array("message" => "Data is incomplete."));
}
$conn->close();
?>
api/get_data.php - 获取需要展示的数据 (示例)
<?php
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Origin: *");
require_once 'config.php';
$sql = "SELECT id, title, content FROM posts ORDER BY created_at DESC";
if ($result = $conn->query($sql)) {
$items = array();
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$items[] = $row;
}
echo json_encode($items);
} else {
echo json_encode(array("message" => "No posts found."));
}
} else {
http_response_code(500);
echo json_encode(array("message" => "Unable to fetch data."));
}
$conn->close();
?>
5 安全性考虑
- 密码哈希:永远不要存储明文密码!使用
password_hash()和password_verify()是 PHP 的标准做法。 - 防止 SQL 注入:始终使用 预处理语句 (
prepare,bind_param,execute),而不是直接将变量拼接到 SQL 字符串中。 - 输入验证:在处理任何用户输入前,都要验证其格式和合法性(如邮箱格式、用户名长度等)。
- HTTPS:在生产环境中,必须使用 HTTPS 协议来加密客户端和服务器之间的通信,防止数据被窃听。
- CORS (
Access-Control-Allow-Origin): 允许任何域名访问你的 API,这在开发时很方便,但在生产环境中应该只允许你的 Android 应用的特定域名。
第二部分:Android 客户端
我们将使用 Retrofit,一个类型安全的 HTTP 客户端,它能极大地简化网络请求。
1 添加依赖
在你的 app/build.gradle 文件的 dependencies 代码块中添加:
// Retrofit & GSON (用于 JSON 解析) implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // OkHttp (Retrofit 的默认网络客户端) implementation 'com.squareup.okhttp3:okhttp:4.9.3' implementation 'com.squareup.okhttp3:logging-interceptor:4.9.3' // 用于打印网络日志 // Coroutines (用于处理异步任务) implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
2 实践:调用 PHP API
创建数据模型 (POJO/Model)
这些类必须与 PHP 返回的 JSON 结构完全匹配。
User.kt
data class User(
val id: Int,
val username: String
)
data class LoginResponse(
val message: String,
val user: User? // 登录成功时才有 user
)
Post.kt
data class Post(
val id: Int,
val title: String,
val content: String
)
定义 Retrofit 接口
这个接口声明了所有可用的 API 端点。
ApiService.kt
import retrofit2.Call
import retrofit2.http.*
interface ApiService {
// 注册
@POST("api/register.php")
fun registerUser(
@Body user: RegisterRequest // 请求体
): Call<ApiResponse>
// 登录
@POST("api/login.php")
fun loginUser(
@Body loginRequest: LoginRequest
): Call<LoginResponse>
// 获取数据
@GET("api/get_data.php")
fun getPosts(): Call<List<Post>>
}
// 用于注册的请求体
data class RegisterRequest(
val username: String,
val email: String,
val password: String
)
// 用于登录的请求体
data class LoginRequest(
val email: String,
val password: String
)
// 通用的 API 响应
data class ApiResponse(val message: String)
创建 Retrofit 实例并实现请求
ApiClient.kt (单例模式)
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object ApiClient {
private const val BASE_URL = "http://10.0.2.2/my_android_app/" // Android 模拟器访问 localhost 的地址
// 如果是真实设备,请使用你的电脑局域网 IP,如 "http://192.168.1.100/my_android_app/"
val apiService: ApiService by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
}
在 Activity 或 ViewModel 中调用
LoginActivity.kt (使用 Kotlin Coroutines)
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import retrofit2.HttpException
class LoginActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
val loginButton: Button = findViewById(R.id.login_button)
loginButton.setOnClickListener {
val email = "test@example.com" // 从输入框获取
val password = "password123" // 从输入框获取
// 在 IO 线程执行网络请求
CoroutineScope(Dispatchers.IO).launch {
try {
val response = ApiClient.apiService.loginUser(LoginRequest(email, password))
// 请求成功,在主线程更新 UI
if (response.isSuccessful) {
val loginResponse = response.body()
runOnUiThread {
Toast.makeText(this@LoginActivity, loginResponse?.message, Toast.LENGTH_SHORT).show()
// 可以保存用户信息,并跳转到主界面
}
} else {
// 服务器返回错误 (如 401, 400)
runOnUiThread {
Toast.makeText(this@LoginActivity, "Login failed: ${response.code()}", Toast.LENGTH_SHORT).show()
}
}
} catch (e: HttpException) {
// 处理 HTTP 错误
runOnUiThread {
Toast.makeText(this@LoginActivity, "Network error: ${e.message}", Toast.LENGTH_SHORT).show()
}
} catch (e: Exception) {
// 处理其他异常 (如无网络)
runOnUiThread {
Toast.makeText(this@LoginActivity, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}
}
}
}
第三部分:进阶与最佳实践
-
身份验证与授权:
- 登录成功后,服务器会返回一个用户信息,你应该生成一个 Token (如 JWT - JSON Web Token) 返回给客户端。
- 客户端在后续需要登录才能访问的 API 请求中,需要在请求头中带上这个 Token。
- PHP 服务器端需要创建一个中间件来验证每个请求的 Token 是否有效。
-
使用 Composer:
- 对于更复杂的项目,强烈建议使用 Composer 来管理 PHP 依赖,你可以用它来安装
firebase/php-jwt来生成和验证 JWT。
- 对于更复杂的项目,强烈建议使用 Composer 来管理 PHP 依赖,你可以用它来安装
-
项目结构:
- 将业务逻辑从 PHP 文件中分离出来,创建一个
UserRepository.php类来处理所有与用户相关的数据库操作,register.php和login.php只负责接收请求和返回响应。
- 将业务逻辑从 PHP 文件中分离出来,创建一个
-
错误处理和日志:
- 在 PHP 中,不要将
mysqli或error_log的直接输出返回给客户端,应该记录详细的错误日志,并向客户端返回一个通用的、友好的错误信息。
- 在 PHP 中,不要将
-
分页:
- 当数据量很大时(如
get_data.php),实现分页功能至关重要,可以通过在 URL 中传递page和limit参数来实现。
- 当数据量很大时(如
这个指南为你提供了一个从零开始的完整框架,你可以基于这个结构,不断扩展功能,构建出健壮、安全的 Android 应用后端。
