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

Android应用连接Google Cloud SQL时的认证错误求助

问题:Android应用连接Google Cloud MySQL数据库时的认证错误

我正在开发Android应用以连接Google Cloud MySQL数据库,遵循官方指南。已将IP地址添加到数据库实例的允许网络列表,并启用了Cloud SQL API。以示例代码为基础开发,在模拟器运行应用时出现如下错误:

2023-03-25 15:58:37.727 13755-13755/com.example.connectiontest E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.connectiontest, PID: 13755
    com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to initialize pool: Could not create connection to database server.
        at com.zaxxer.hikari.pool.HikariPool.throwPoolInitializationException(HikariPool.java:595)
        at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:581)
        at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:115)
        at com.zaxxer.hikari.HikariDataSource.<init>(HikariDataSource.java:81)
        at com.example.connectiontest.MainActivity$1.onClick(MainActivity.java:53)
        at android.view.View.performClick(View.java:7506)
        at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1202)
        at android.view.View.performClickInternal(View.java:7483)
        at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
        at android.view.View$PerformClick.run(View.java:29334)
        at android.os.Handler.handleCallback(Handler.java:942)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7872)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
     Caused by: java.sql.SQLNonTransientConnectionException: Could not create connection to database server.
        at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:110)
        at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
        at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89)
        at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:63)
        at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:997)
        at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:814)
        at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:444)
        at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:237)
        at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:198)
        at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:121)
        at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:358)
        at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:206)
        at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:477)
        at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:560)
        at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:115) 
        at com.zaxxer.hikari.HikariDataSource.<init>(HikariDataSource.java:81) 
        at com.example.connectiontest.MainActivity$1.onClick(MainActivity.java:53) 
        at android.view.View.performClick(View.java:7506) 
        at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1202) 
        at android.view.View.performClickInternal(View.java:7483) 
        at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0) 
        at android.view.View$PerformClick.run(View.java:29334) 
        at android.os.Handler.handleCallback(Handler.java:942) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loopOnce(Looper.java:201) 
        at android.os.Looper.loop(Looper.java:288) 
        at android.app.ActivityThread.main(ActivityThread.java:7872) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) 
     Caused by: java.lang.RuntimeException: Unable to obtain credentials to communicate with the Cloud SQL API
        at com.google.cloud.sql.core.CoreSocketFactory$ApplicationDefaultCredentialFactory.create(CoreSocketFactory.java:392)
        at com.google.cloud.sql.core.CoreSocketFactory.getInstance(CoreSocketFactory.java:140)
        at com.google.cloud.sql.core.CoreSocketFactory.connect(CoreSocketFactory.java:256)
        at com.google.cloud.sql.core.CoreSocketFactory.connect(CoreSocketFactory.java:215)
        at com.google.cloud.sql.mysql.SocketFactory.connect(SocketFactory.java:57)
        at com.google.cloud.sql.mysql.SocketFactory.connect(SocketFactory.java:43)
        at com.mysql.cj.protocol.a.NativeSocketConnection.connect(NativeSocketConnection.java:63)
        at com.mysql.cj.NativeSession.connect(NativeSession.java:120)
        at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:944)
        at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:814) 
        at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:444) 
        at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:237) 
        at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:198) 
        at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:121) 
        at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:358) 
        at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:206) 
        at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:477) 
        at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:560) 
        at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:115) 
        at com.zaxxer.hikari.HikariDataSource.<init>(HikariDataSource.java:81) 
        at com.example.connectiontest.MainActivity$1.onClick(MainActivity.java:53) 
        at android.view.View.performClick(View.java:7506) 
        at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1202) 
        at android.view.View.performClickInternal(View.java:7483) 
        at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0) 
        at android.view.View$PerformClick.run(View.java:29334) 
        at android.os.Handler.handleCallback(Handler.java:942) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loopOnce(Looper.java:201) 
        at android.os.Looper.loop(Looper.java:288) 
        at android.app.ActivityThread.main(ActivityThread.java:7872) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) 
     Caused by: java.io.IOException: The Application Default Credentials are not available. They are available if running in Google Compute Engine. Otherwise, the environment variable GOOGLE_APPLICATION_CREDENTIALS must be defined pointing to a file defining the credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.
        at com.google.auth.oauth2.DefaultCredentialsProvider.getDefaultCredentials(DefaultCredentialsProvider.java:125)
2023-03-25 15:58:37.728 13755-13755/com.example.connectiontest E/AndroidRuntime:     at com.google.auth.oauth2.GoogleCredentials.getApplicationDefault(GoogleCredentials.java:125)
        at com.google.auth.oauth2.GoogleCredentials.getApplicationDefault(GoogleCredentials.java:97)
        at com.google.cloud.sql.core.CoreSocketFactory$ApplicationDefaultCredentialFactory.create(CoreSocketFactory.java:390)
            ... 33 more

我了解了Application Default Credentials相关内容,但移动设备无法设置环境变量,且未在Google文档中找到替代认证方式。以下是简化后的应用代码:

MainActivity代码

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

public class MainActivity extends AppCompatActivity {

    private static final String CONNECTION_NAME = "***";
    private static final String DB_NAME = "***";
    private static final String DB_USER = "***";
    private static final String DB_PASSWORD = "***";

    Button testConn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        testConn = (Button) findViewById(R.id.button);
        testConn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String jdbcURL = String.format("jdbc:mysql:///%s", DB_NAME);
                Properties connProps = new Properties();
                connProps.setProperty("user", DB_USER);
                connProps.setProperty("password", DB_PASSWORD);
                connProps.setProperty("socketFactory", "com.google.cloud.sql.mysql.SocketFactory");
                connProps.setProperty("cloudSqlInstance", CONNECTION_NAME);

 
                HikariConfig config = new HikariConfig();
                config.setJdbcUrl(jdbcURL);
                config.setDataSourceProperties(connProps);
                config.setConnectionTimeout(10000); 

                HikariDataSource connectionPool = new HikariDataSource(config);//代码在此处崩溃

                try {
                    Connection conn = connectionPool.getConnection();
                    String stmt = "SELECT test_field FROM test_table limit 1;";
                    PreparedStatement selectStmt = conn.prepareStatement(stmt);
                    selectStmt.setQueryTimeout(10); 
                    ResultSet rs = selectStmt.executeQuery();
                    while (rs.next()) {
                        Toast.makeText(getApplicationContext(), rs.getString("test_field"), Toast.LENGTH_LONG).show();
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

build.gradle代码

plugins {
    id 'com.android.application'
}

android {
    namespace 'com.example.connectiontest'
    compileSdk 33

    defaultConfig {
        applicationId "com.example.connectiontest"
        minSdk 26
        targetSdk 33
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    packagingOptions {
        resources.excludes.add("META-INF/*")
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.8.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation files('libs/mysql-connector-j-8.0.32.jar')
    implementation 'com.zaxxer:HikariCP:3.4.5'
    implementation 'com.google.cloud.sql:mysql-socket-factory-connector-j-8:1.11.0'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

解决方案

核心问题

你使用的Cloud SQL Socket Factory默认依赖Application Default Credentials(ADC),但Android设备无法设置环境变量,因此需要更换认证方式。

方案1:使用服务账号密钥文件

  1. 在Google Cloud控制台创建服务账号,授予Cloud SQL Client角色,下载JSON格式的密钥文件。
  2. 将密钥文件放入Android项目的assets目录。
  3. 修改连接属性,添加credentialsFile指定密钥文件路径:
connProps.setProperty("credentialsFile", "file:///android_asset/your-service-account-key.json");

注意:发布版本中请勿直接暴露密钥文件,建议通过加密存储等安全方式管理。

方案2:直接使用公网IP连接(跳过Socket Factory)

既然你已经将模拟器/设备IP加入Cloud SQL白名单,可以直接用标准JDBC URL连接,无需Socket Factory:

// 替换为你的Cloud SQL实例公网IP
String jdbcURL = String.format("jdbc:mysql://<INSTANCE_PUBLIC_IP>/%s?useSSL=false", DB_NAME);
// 移除以下两行Socket Factory相关配置
// connProps.setProperty("socketFactory", "com.google.cloud.sql.mysql.SocketFactory");
// connProps.setProperty("cloudSqlInstance", CONNECTION_NAME);

这种方式更适配移动设备,避免复杂认证流程,但需确保IP白名单配置正确,生产环境建议启用SSL加密。

方案3:通过中间层API连接(生产环境推荐)

直接在Android应用中连接数据库存在凭证泄露风险,生产环境建议搭建后端API(如Cloud Functions、App Engine),Android应用通过调用API操作数据库,既能避免暴露数据库凭证,也便于管控访问权限。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 18:27:00