Android连接远程数据库的避坑指南
阅读原文时间:2022年04月19日阅读:1

今天用Android Studio连接数据库时候,写了个测试连接的按钮,然后连接的时候报错了,报错信息:

2021-09-07 22:45:20.433 7054-7083/cn.xuziao.faceprocess E/AndroidRuntime: FATAL EXCEPTION: Thread-113
    Process: cn.xuziao.faceprocess, PID: 7054
    java.lang.NoSuchMethodError: No virtual method group(Ljava/lang/String;)Ljava/lang/String; in class Ljava/util/regex/Matcher; or its super classes (declaration of 'java.util.regex.Matcher' appears in /system/framework/core-libart.jar)
        at com.mysql.cj.conf.ConnectionUrlParser.isConnectionStringSupported(ConnectionUrlParser.java:152)
        at com.mysql.cj.conf.ConnectionUrl.acceptsUrl(ConnectionUrl.java:317)
        at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:186)
        at java.sql.DriverManager.getConnection(DriverManager.java:179)
        at java.sql.DriverManager.getConnection(DriverManager.java:213)
        at cn.xuziao.faceprocess.LoginActivity$1$1.run(LoginActivity.java:38)
        at java.lang.Thread.run(Thread.java:818)

然后我当时写的按钮点击执行方法片段

textView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            String url = "jdbc:mysql://172.17.9.241:3306/user_info";
            String login_username = "login";
            String login_password = "root";
            Connection connection = DriverManager.getConnection(url, login_username, login_password);
            String sql = "select username from base_info";
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery(sql);
            while (resultSet.next()){
                Log.d(TAG, resultSet.getString(1));
            }
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        Toast.makeText(LoginActivity.this, "注册中...", Toast.LENGTH_SHORT).show();
    }
});

附上当时的build.gradle文件里面我导入的依赖

dependencies {

    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    implementation 'mysql:mysql-connector-java:8.0.26'
}

最后一行是我引入的依赖

然后我就查到,安卓的主线程不能访问网络,具体博客:https://www.cnblogs.com/lyroge/p/3837902.html

引入后的代码:

textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Class.forName("com.mysql.jdbc.Driver");
                            String url = "jdbc:mysql://172.17.9.241:3306/user_info";
                            String login_username = "login";
                            String login_password = "root";
                            Connection connection = DriverManager.getConnection(url, login_username, login_password);
                            String sql = "select username from base_info";
                            Statement statement = connection.createStatement();
                            ResultSet resultSet = statement.executeQuery(sql);
                            while (resultSet.next()){
                                Log.d(TAG, resultSet.getString(1));
                            }
                        } catch (ClassNotFoundException | SQLException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
                Toast.makeText(LoginActivity.this, "注册中...", Toast.LENGTH_SHORT).show();
            }
        });

然后我引入了多线程,还是报错,报错信息跟上面是一样的,那就说明问题不只主线程不能访问网络的问题,然后我就改了好久奇奇怪怪的东西,我就看到我的安卓自带的jdk版本是1.8但是我的MySQL驱动到了8,然后我就去把MySQL驱动换成了5,build.gradle依赖部分改为:

dependencies {

    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    implementation 'mysql:mysql-connector-java:5.1.6'

}

然后我发现上面那个报错的问题解决了,然后报了新的错:

W/System.err: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
    Last packet sent to the server was 0 ms ago.
        at java.lang.reflect.Constructor.newInstance(Native Method)
        at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
        at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1074)
        at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2103)
        at com.mysql.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:718)
        at com.mysql.jdbc.JDBC4Connection.<init>(JDBC4Connection.java:46)
        at java.lang.reflect.Constructor.newInstance(Native Method)
        at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
        at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:302)
        at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:282)
        at java.sql.DriverManager.getConnection(DriverManager.java:179)
        at java.sql.DriverManager.getConnection(DriverManager.java:213)
        at cn.xuziao.faceprocess.LoginActivity$1$1.run(LoginActivity.java:37)
        at java.lang.Thread.run(Thread.java:818)
    Caused by: java.net.UnknownHostException: Unable to resolve host "172.17.9.241": No address associated with hostname
        at java.net.InetAddress.lookupHostByName(InetAddress.java:470)
        at java.net.InetAddress.getAllByNameImpl(InetAddress.java:252)
        at java.net.InetAddress.getAllByName(InetAddress.java:215)
        at com.mysql.jdbc.StandardSocketFactory.connect(StandardSocketFactory.java:243)
        at com.mysql.jdbc.MysqlIO.<init>(MysqlIO.java:280)
        at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2026)
        ... 10 more
W/System.err: Caused by: android.system.GaiException: android_getaddrinfo failed: EAI_NODATA (No address associated with hostname)
        at libcore.io.Posix.android_getaddrinfo(Native Method)
        at libcore.io.ForwardingOs.android_getaddrinfo(ForwardingOs.java:55)
        at java.net.InetAddress.lookupHostByName(InetAddress.java:451)
        ... 15 more
    Caused by: android.system.ErrnoException: android_getaddrinfo failed: ECONNREFUSED (Connection refused)
        ... 18 more

这个错是被异常捕获机制捕获到的一个错误,说明MySQL驱动是成功导入而且能够使用了,然后通过查资料了解到,要解决这个错误只需要在AndroidManifest.xml文件里面加一行<uses-permission android:name="android.permission.INTERNET"/>就行了

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="cn.xuziao.faceprocess">
    <!--要加在application标签的上面-->
    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="false"
        android:icon="@mipmap/real_launcher_logo"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.FaceProcess"
        >
        <activity
            android:name=".LoginActivity"
            android:exported="true"
            android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
            >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".MainActivity"
            android:exported="true"/>
    </application>
</manifest>

然后问题成功解决

总结

1. 安卓连接网络要使用多线程,不能直接用主线程

2. MySQL驱动版本不宜过高,我这边试的5是可以的,8不行,至于6、7暂时还不清楚

之后我又新建了一个Java工程,用的jdk8但是使用MySQL8的驱动是可以正确连接的,不知道Android为什么不行,如果有知道的希望能在评论区一起分享一下

3. 要记得在AndroidManifest.xml文件里面加上<uses-permission android:name="android.permission.INTERNET"/>这一行