You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android应用连接云端MySQL配置类实现及连接异常解决求助

云端MySQL连接问题解决与Android正确实现方案

一、先明确:Android直接用JDBC连接MySQL的风险

  • 数据库用户名、密码会直接暴露在APK中,反编译即可获取,存在极高安全风险
  • 移动端网络不稳定,JDBC长连接易断开,性能表现差
  • 不符合Android开发最佳实践,官方不推荐客户端直接操作数据库

二、解决当前的连接异常

你遇到的SQLNonTransientConnectionException是连接失败导致的,按以下步骤逐一排查:

  1. 云端MySQL远程访问配置

    • 确认云服务商(如阿里云、腾讯云)的数据库实例已开启外网访问
    • 将测试设备IP或临时设置0.0.0.0/0(仅限测试,生产环境禁止)添加到数据库IP白名单
    • 给数据库用户授予远程访问权限:执行SQL GRANT ALL ON 数据库名.* TO '用户名'@'%' IDENTIFIED BY '密码'; FLUSH PRIVILEGES;
  2. 修正JDBC URL参数
    MySQL 8.0+必须指定时区,否则会触发连接错误,URL修改为:

private String host = "jdbc:mysql://你的外网IP:3306/数据库名?autoReconnect=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true";
  • 替换你的外网IP为云端数据库的实际外网IP
  • 确认端口为3306(若数据库修改过端口需对应调整)
  • 添加allowPublicKeyRetrieval=true解决部分权限验证问题
  1. 更新依赖版本
    你的mysql-connector-java版本过旧,Android项目中在build.gradle(Module)中替换为:
implementation 'mysql:mysql-connector-java:8.0.33'
  1. 添加Android网络权限
    在AndroidManifest.xml中添加网络访问权限:
<uses-permission android:name="android.permission.INTERNET" />
  1. 避免主线程执行网络操作
    Android禁止在主线程处理网络请求,必须用子线程实现,示例用Coroutine:
// 在Activity中调用数据库方法时
CoroutineScope(Dispatchers.IO).launch {
    try {
        DatabaseController controller = new DatabaseController();
        ArrayList<String> tasks = controller.getTasks();
        // 切换回主线程更新UI
        withContext(Dispatchers.Main) {
            // 此处更新列表UI
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

三、修正你的DatabaseController代码(修复资源泄漏)

原代码未关闭Connection、Statement、ResultSet,会导致资源泄漏,修正后:

import android.util.Log;
import java.sql.*;
import java.util.ArrayList;

public class DatabaseController {
    private static final String TAG = "DatabaseController";
    private String host = "jdbc:mysql://你的外网IP:3306/数据库名?autoReconnect=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true";
    private String username = "你的数据库用户名";
    private String password = "你的数据库密码";

    private Connection getDbConn() throws SQLException, ClassNotFoundException {
        // MySQL 8.0+ 无需显式加载Driver,DriverManager会自动扫描
        // Class.forName("com.mysql.cj.jdbc.Driver"); 该行可删除
        return DriverManager.getConnection(host, username, password);
    }

    public ArrayList<String> getTasks() throws SQLException, ClassNotFoundException {
        ArrayList<String> tasks = new ArrayList<>();
        Connection conn = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            conn = getDbConn();
            String sql = "SELECT * FROM User";
            statement = conn.createStatement();
            resultSet = statement.executeQuery(sql);
            while (resultSet.next()) {
                tasks.add(resultSet.getString("task"));
            }
        } finally {
            // 按顺序关闭资源:ResultSet -> Statement -> Connection
            try {
                if (resultSet != null) resultSet.close();
                if (statement != null) statement.close();
                if (conn != null) conn.close();
            } catch (SQLException e) {
                Log.e(TAG, "关闭资源失败", e);
            }
        }
        return tasks;
    }
}

四、推荐的正确实现方案:使用中间层API

直接JDBC存在安全隐患,建议后端搭建REST API服务(如Spring Boot、Node.js),Android通过HTTP请求获取数据:

1. 后端示例(Spring Boot接口)

@RestController
@RequestMapping("/api")
public class DataController {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @GetMapping("/building-materials")
    public List<Map<String, Object>> getBuildingMaterials() {
        return jdbcTemplate.queryForList("SELECT * FROM building_materials");
    }

    @GetMapping("/apps")
    public List<Map<String, Object>> getApps() {
        return jdbcTemplate.queryForList("SELECT * FROM apps");
    }
}

2. Android端用Retrofit请求

  • 添加Retrofit依赖到build.gradle:
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
  • 创建API接口:
public interface ApiService {
    @GET("/api/building-materials")
    Call<List<Map<String, Object>>> getBuildingMaterials();

    @GET("/api/apps")
    Call<List<Map<String, Object>>> getApps();
}
  • 在Activity中调用(按钮点击逻辑):
Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://你的后端IP:端口/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();
ApiService apiService = retrofit.create(ApiService.class);

// 建材列表按钮点击事件
materialBtn.setOnClickListener(v -> {
    apiService.getBuildingMaterials().enqueue(new Callback<List<Map<String, Object>>>() {
        @Override
        public void onResponse(Call<List<Map<String, Object>>> call, Response<List<Map<String, Object>>> response) {
            if (response.isSuccessful()) {
                List<Map<String, Object>> materials = response.body();
                // 更新UI展示建材列表
            }
        }

        @Override
        public void onFailure(Call<List<Map<String, Object>>> call, Throwable t) {
            t.printStackTrace();
        }
    });
});

// 应用列表按钮点击事件
appBtn.setOnClickListener(v -> {
    apiService.getApps().enqueue(new Callback<List<Map<String, Object>>>() {
        @Override
        public void onResponse(Call<List<Map<String, Object>>> call, Response<List<Map<String, Object>>> response) {
            if (response.isSuccessful()) {
                List<Map<String, Object>> apps = response.body();
                // 更新UI展示应用列表
            }
        }

        @Override
        public void onFailure(Call<List<Map<String, Object>>> call, Throwable t) {
            t.printStackTrace();
        }
    });
});

内容的提问来源于stack exchange,提问作者VadiMayer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 01:05:22