核心问题:为什么不能直接在 Android 上连接 MySQL?
在开始之前,最重要的一点是理解:Android App(客户端)不应该也不应该直接连接到公网上的 MySQL 数据库。

原因如下:
-
安全风险(最主要的原因):
- 数据库凭据暴露:如果将数据库的用户名、密码、IP地址、端口等直接写在 App 的代码里(即使是加密的),一旦 App 被反编译,这些信息就完全暴露了,攻击者可以轻易地获取你的数据库凭据,从而窃取、篡改甚至删除你的所有数据。
- SQL 注入:客户端直接连接数据库,很难完全防止 SQL 注入攻击,这会严重威胁数据安全。
-
网络架构问题:
- 公网 IP 和端口:大多数数据库服务器没有公网 IP,或者防火墙不允许 3306 (MySQL 默认端口) 这样的端口对外访问。
- NAT 和防火墙:手机和服务器通常都位于 NAT(网络地址转换)之后,直接建立连接非常困难且不稳定。
-
性能和可维护性:
(图片来源网络,侵删)- 连接管理:每个 App 连接都是一个数据库连接,如果用户量巨大,数据库服务器会因连接数耗尽而崩溃。
- 业务逻辑耦合:如果数据库表结构需要修改,你就必须强制所有用户更新 App,这在实际开发中是不可行的。
- 不灵活:数据库迁移、扩容、读写分离等后端操作会直接影响 App 的稳定性。
推荐架构:客户端 -> 服务器 -> 数据库
正确的做法是引入一个中间层,也就是后端服务器,架构如下:
[Android App] <--(HTTPS/JSON/XML)--> [Your Backend Server] <--(Internal Network)--> [MySQL Database]
工作流程:
- Android App 作为客户端,负责用户界面和交互。
- App 不直接连接 MySQL,而是通过 HTTP/HTTPS 协议向后端服务器发送请求(
GET /api/users/123)。 - 后端服务器(例如用 Java Spring Boot, Node.js, Python Flask/Django 等编写)接收这个请求。
- 后端服务器验证请求的合法性(例如检查用户 Token 是否有效)。
- 服务器使用 JDBC (Java) 或其他数据库驱动,安全地连接到本地或内网的 MySQL 数据库。
- 服务器执行数据库操作(查询、增、删、改),然后将结果打包成 JSON 或 XML 格式。
- 服务器将 JSON/XML 数据通过 HTTP 响应返回给 Android App。
- Android App 解析收到的 JSON/XML 数据,并更新 UI。
这种架构的优势:
- 安全:数据库凭据只存在于服务器上,App 完全接触不到,服务器可以处理所有认证和授权。
- 灵活:后端 API 可以独立于 App 进行迭代,你可以修改数据库逻辑或增减接口,而无需更新 App(只要 API 格式不变)。
- 可扩展:当用户量增长时,你可以轻松地扩展后端服务器集群,而数据库只需连接到这些服务器即可。
- 解耦:客户端和服务器是分离的,可以由不同的团队使用不同的技术栈进行开发。
如何实现:后端服务器部分(以 Java Spring Boot 为例)
这是连接 MySQL 的核心部分。

项目准备
- 使用 Spring Initializr (https://start.spring.io/) 创建一个新项目。
- 添加依赖:
Spring Web:用于创建 RESTful API。Spring Data JPA:简化数据库操作。MySQL Driver:Java 连接 MySQL 的驱动。Lombok(可选):简化代码。
配置 application.properties
在 src/main/resources/application.properties 文件中配置数据库连接信息。
# 服务器端口 server.port=8080 # MySQL 数据库配置 # 注意:这里的 username 和 password 是你数据库的用户名和密码 spring.datasource.url=jdbc:mysql://localhost:3306/your_database_name?useSSL=false&serverTimezone=UTC spring.datasource.username=your_db_user spring.datasource.password=your_db_password spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # JPA (Hibernate) 配置 # 自动更新数据库表结构(开发环境可用,生产环境建议手动管理) spring.jpa.hibernate.ddl-auto=update # 显示 SQL 语句 spring.jpa.show-sql=true # 格式化 SQL 语句 spring.jpa.properties.hibernate.format_sql=true
创建实体类
import jakarta.persistence.*;
@Entity // 声明这是一个与数据库表对应的实体
@Table(name = "users") // 对应数据库中的 users 表
public class User {
@Id // 声明为主键
@GeneratedValue(strategy = GenerationType.IDENTITY) // 自增主键
private Long id;
private String name;
private String email;
// Getters and Setters (可以用 Lombok 的 @Data 注解自动生成)
// ...
}
创建数据访问层
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Spring Data JPA 会自动实现基本的 CRUD 操作
// 你也可以在这里定义自定义查询方法,
// User findByName(String name);
}
创建服务层
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public List<User> getAllUsers() {
return userRepository.findAll();
}
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
public User createUser(User user) {
return userRepository.save(user);
}
// ... 其他增删改查方法
}
创建控制器
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/users") // 定义 API 的基础路径
public class UserController {
@Autowired
private UserService userService;
// GET /api/users -> 获取所有用户
@GetMapping
public List<User> getAllUsers() {
return userService.getAllUsers();
}
// GET /api/users/1 -> 根据 ID 获取单个用户
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
// POST /api/users -> 创建一个新用户
@PostMapping
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}
// ... 其他接口 (PUT, DELETE)
}
你的后端服务器就准备好了,启动它,访问 http://localhost:8080/api/users 就能测试你的 API。
如何实现:Android 客户端部分
Android 客户端的工作就是通过网络调用上面创建的 API。
添加网络权限
在 app/src/main/AndroidManifest.xml 中添加:
<uses-permission android:name="android.permission.INTERNET" />
添加网络依赖
在 app/build.gradle.kts (或 app/build.gradle) 文件中添加 Retrofit 和 Gson 依赖。
// build.gradle.kts
dependencies {
// ... 其他依赖
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0") // 用于 JSON 转换
implementation("com.squareup.okhttp3:logging-interceptor:4.11.0") // 用于日志
}
创建数据模型 (与服务器 JSON 对应)
// User.kt
data class User(
val id: Long,
val name: String,
val email: String
)
创建 Retrofit API 接口
// ApiService.kt
import retrofit2.Call
import retrofit2.http.*
interface ApiService {
// 注意:这里使用服务器的公网 IP 或域名
@GET("api/users")
fun getAllUsers(): Call<List<User>>
@GET("api/users/{id}")
fun getUserById(@Path("id") id: Long): Call<User>
@POST("api/users")
fun createUser(@Body user: User): Call<User>
}
创建 Retrofit 实例
// RetrofitClient.kt
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitClient {
private const val BASE_URL = "http://YOUR_SERVER_IP:8080/" // 替换成你的服务器 IP
val instance: ApiService by lazy {
// 创建一个 OkHttp 客户端用于打印日志
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(interceptor)
.build()
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()
retrofit.create(ApiService::class.java)
}
}
重要提示:将 YOUR_SERVER_IP 替换为你的服务器的公网 IP 地址,如果你的服务器和你的电脑在同一个局域网内,你可以用电脑的局域网 IP(如 http://192.168.1.10:8080/)进行测试,但要发布到公网,服务器必须有公网 IP。
在 Activity 或 ViewModel 中调用 API
// MainActivity.kt
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
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 tvResult: TextView = findViewById(R.id.tv_result)
val btnFetch: Button = findViewById(R.id.btn_fetch)
btnFetch.setOnClickListener {
fetchUsers()
}
}
private fun fetchUsers() {
val call = RetrofitClient.instance.getAllUsers()
call.enqueue(object : Callback<List<User>> {
override fun onResponse(call: Call<List<User>>, response: Response<List<User>>) {
if (response.isSuccessful) {
val userList = response.body()
val result = userList?.joinToString(separator = "\n") { "ID: ${it.id}, Name: ${it.name}" }
Log.d("API_SUCCESS", "Users: $userList")
// 在 UI 线程更新视图
runOnUiThread {
findViewById<TextView>(R.id.tv_result).text = result ?: "No users found"
}
} else {
Log.e("API_ERROR", "Response not successful: ${response.code()}")
}
}
override fun onFailure(call: Call<List<User>>, t: Throwable) {
Log.e("API_FAILURE", "Failed to fetch users: ${t.message}")
}
})
}
}
部署你的后端服务器
要让 Android App 能在任意地方访问,你需要将后端服务器部署到公网上。
- 购买云服务器:例如阿里云、腾讯云、AWS、DigitalOcean、Vultr 等,选择一个配置较低的服务器即可开始。
- 安装环境:在服务器上安装 Java (JDK) 和 MySQL。
- 配置安全组:在云服务器的管理控制台,找到“安全组”或“防火墙”规则,开放 8080 端口(或你设置的 HTTP 端口),并限制访问来源(建议只允许你的 IP 访问,或者全部开放
0.0.0/0)。 - 上传并运行:将你的 Spring Boot 项目打包成
.jar文件,上传到服务器,然后用java -jar your-app-name.jar命令运行。 - 使用反向代理(推荐):生产环境中,建议使用 Nginx 作为反向代理,它可以将 80 (HTTP) 或 443 (HTTPS) 端口的请求转发到你应用的 8080 端口,并可以处理 SSL 证书,实现 HTTPS 访问,这是更安全、更专业的做法。
总结与最佳实践
- 永远不要让 App 直连 MySQL,这是架构设计的铁律。
- 采用 RESTful API 架构,使用 JSON 作为数据交换格式。
- 后端首选 Java Spring Boot,它与 Android 开发语言一致,生态成熟,上手快。
- Android 客户端使用 Retrofit 进行网络请求,它是目前最流行、最强大的网络库之一。
- 服务器必须部署在公网,并配置好防火墙规则。
- 生产环境必须使用 HTTPS,以保护数据在传输过程中的安全。
- 处理异步操作:网络请求是耗时操作,必须在 Android 的后台线程(如 Coroutine, RxJava, AsyncTask)中执行,并在主线程更新 UI。
- 错误处理:妥善处理网络错误、服务器错误和数据解析错误,并向用户友好的提示。
遵循以上步骤和原则,你就可以构建一个安全、可扩展且易于维护的 Android 应用后端系统。
