注意
很多朋友用IDEA通过JDBC的方式去连接MYSQL数据库,JDBC之获取数据库连接方法详解,然后看到这里用Androidstudio通过JDBC的方式去连接MYSQL数据库是不是也是像我前两天一样,不以为然呢?是不是以为贴过java代码就行了呢?还请听我细细道来
基础
需要会使用IDEA编译器用java代码通过JDBC的方式,来连接MySQL数据库,最好还是先看完这篇博客:JDBC之获取数据库连接方法详解,然后再看Androidstudio
这篇
Androidstudio通过JDBC连接数据库巨坑介绍(这里呢,我使用我所做项目的修改密码界面来做介绍)
1.网络权限问题(打不开apk)
Android Studio默认是不允许访问Internet的。因此需要在app/src/main/AndroidManifest.xml
中的<manifest>...</manifest>
区域内添加一行在 ,AndroidManifest.xml
中(别去加在<application></application>
里面)添加:
<uses-permission android:name="android.permission.INTERNET" />
2.jar包问题(找不到driver)
我的jar包用的是5.0.8
版本的,jar包的话,去官网下载,建议版本不要太高,版本太高问题超级多
3.连接方法问题(返回连接对象为null)
Connection conn=DriverManager.getConnection(url,user,password);
Driver driver=new com.mysql.jdbc.Driver();
Properties info =new Properties();
info.setProperty("user","root0");
info.setProperty("password","abc123");
Connection conn=driver.connect(url,info);
连接之前记得加载一下驱动类就行,刚开始我的Connection返回对象一直为null,我一直以为是连接方式有问题,我试完这两种连接方式,于是Connection返回对象一直为null,至于为什么返回null的话,请看下一个巨坑。
4.异步启动问题(返回连接对象为null)
同步启动
刚开始我直接把连接过程写在主线程里面,所以造成了上面那种尴尬的情况,Connection返回对象一直是null,查了一下原因如下:
一个APP如果在主线程中请求网络操作,将会抛出NetworkOnMainThreadException
异常。Android这个设计是为了防止网络请求时间过长而导致界面假死的情况发生,我试了直接不是假死,而是直接返回null值,然后就gg
了。
static void connect(){
//1.读取配置文件中的四个基本信息
String url = "jdbc:mysql://"+ip+"/"+数据库名;
String user_server = 用户名;
String password_server = 密码;
String driverclass = "com.mysql.jdbc.Driver";
//2.加载驱动
try {
Class.forName(driverclass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
conn = null;
//3.获取连接
try {
conn = DriverManager.getConnection(url, user_server, password_server);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
//4.预编译sql语句,返回PreparedStatement的实例
sql="select * from login ";
try {
ps=conn.prepareStatement(sql);
//6.执行并返回结果集
ResultSet resultSet =ps.executeQuery();
while(resultSet.next()){
String usertest=resultSet.getString(1);
String passwordtest=resultSet.getString(2);
if(usertest.equals(user)){
if(password_before.equals(passwordtest)) {
String sql2 = "update login set password=? where user=?";
ps = conn.prepareStatement(sql2);
//5.填充占位符
ps.setString(1, password_after);
ps.setString(2, user);
//6.执行操作
ps.execute();
flag_test=1;
return;
}
flag_test=2;
return;
}
}
flag_test=0;
} catch (SQLException e) {
e.printStackTrace();
}
flag_test=0;
return ;
}
当你执行到下面这行的时候,然后就会返回null值,紧接着下面也就没有任何意义了
conn = DriverManager.getConnection(url, user_server, password_server);
可以断点调试看看
异步启动
Android在子线程中是不可以进行ui操作的。Toast也是不可以在子线程中使用的。
Thread threads=new Thread(new Runnable() {
public void run() {
//1.读取配置文件中的四个基本信息
String url = "jdbc:mysql://"+ip+"/"+数据库名;
String user_server = 用户名;
String password_server = 密码;
String driverclass = "com.mysql.jdbc.Driver";
//2.加载驱动
try {
Class.forName(driverclass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
conn = null;
//3.获取连接
try {
conn = DriverManager.getConnection(url, user_server, password_server);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
//4.预编译sql语句,返回PreparedStatement的实例
sql="select * from login ";
try {
ps=conn.prepareStatement(sql);
//6.执行并返回结果集
ResultSet resultSet =ps.executeQuery();
while(resultSet.next()){
String usertest=resultSet.getString(1);
String passwordtest=resultSet.getString(2);
if(usertest.equals(user)){
if(password_before.equals(passwordtest)) {
String sql2 = "update login set password=? where user=?";
ps = conn.prepareStatement(sql2);
//5.填充占位符
ps.setString(1, password_after);
ps.setString(2, user);
//6.执行操作
ps.execute();
flag_test=1;
return;
}
flag_test=2;
return;
}
}
flag_test=0;
} catch (SQLException e) {
e.printStackTrace();
}
flag_test=0;
return ;
}
});
threads.start();
这样的话,就能启动成功喽,但是注意一点,这种方式,只能运行一次,如果点击了两次的话,那么也就相当于,这个线程对象调用了两次start函数,这能行吗?????。但是我们进行修改密码然后点击按钮判断的时候,就只是点击一次,然后手动返回上个页面了吗?,答案肯定不是的。
5.同一个线程不能多次调用异步启动函数(threads.start()------>new Thread(threads).start();)(java.lang.IllegalThreadStateException报错,闪退问题)
这里也就是解决4的问题,threads.start();
,你点击几次按钮,那么它就会调用几次start函数,你能把一个thread对象连续start吗?肯定不能!所以等你点击第二次的时候,那么就报错了,IllegalThreadStateException
紧接着闪退了,
这算是一次点击,对吧?也就是相当于调用了一次threads.start()
;,如果我们继续在这页面输入内容加密码这些东西,再次进行判断,是不是相当于第二次调用threads.start()
,然后就闪退,变成如下界面,也就是上一个activity了,并且IllegalThreadStateException
报错
那么我们该如何解决呢?出发点肯定是不能让一个线程调用两次start函数,对吧?那么每次都new一个thread不就得了嘛,对吧,或者搞一个thread数组,换着使用
new Thread(threads).start();
6.线程函数中弹出Toast问题
如果在连接上的时候需要一个toast
提示一下,连接成功(修改密码前提肯定是连接数据库嘛)或者修改密码成功的话,那么是不是会在线程函数中写上:
Toast.makeText(getApplicationContext(), "修改密码成功", Toast.LENGTH_SHORT).show();
线程函数中能不能直接toast呢?(答案肯定是不能呀 ,如果在线程函数中,直接写的话,毫无疑问报错java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
)Android在子线程中是不可以进行ui操作的。Toast也是不可以在子线程中使用的。
在调用Toast之前,需要先创建一个Looper
对象,这样做,我们所调用的Toast才能加入到一个消息队列中,并向屏幕进行输出显示。而我们之所以在MainActivity
类中可以直接调用Toast而无需在其前面创建一个Looper
对象,是因为当我们进行程序编译时,Android框架会默认为我们创建出一个Looper
对象。
所以在线程函数中调用 Toast的话,需要如下操作:
Looper.prepare();
Toast.makeText(getApplicationContext(), "修改密码成功", Toast.LENGTH_SHORT).show();
Looper.loop();
那么我们可不可以设置一个全局变量flag,然后在线程函数里面设置flag的值,然后出来之后,利用一个judge_after
函数来进行toast操作呢?在线程函数里面保证只进行连接和设置flag 的作用,其它的在主线程里面进行会比较方便,因为异步线程,没法进行断点调试,那样做起来调试时候比较让人难以接受,
7.修改密码框架模式问题(judge_before + judge +judge_after)
Thread threads=new Thread(new Runnable() {
public void run() {
//1.读取配置文件中的四个基本信息
String url = "jdbc:mysql://"+ip+"/"+数据库名;
String user_server = 用户名;
String password_server = 密码;
String driverclass = "com.mysql.jdbc.Driver";
//2.加载驱动
try {
Class.forName(driverclass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
conn = null;
//3.获取连接
try {
conn = DriverManager.getConnection(url, user_server, password_server);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
//4.预编译sql语句,返回PreparedStatement的实例
sql="select * from login ";
try {
ps=conn.prepareStatement(sql);
//6.执行并返回结果集
ResultSet resultSet =ps.executeQuery();
while(resultSet.next()){
String usertest=resultSet.getString(1);
String passwordtest=resultSet.getString(2);
if(usertest.equals(user)){
if(password_before.equals(passwordtest)) {
String sql2 = "update login set password=? where user=?";
ps = conn.prepareStatement(sql2);
//5.填充占位符
ps.setString(1, password_after);
ps.setString(2, user);
//6.执行操作
ps.execute();
flag_test=1;
return;
}
flag_test=2;
return;
}
}
flag_test=0;
} catch (SQLException e) {
e.printStackTrace();
}
flag_test=0;
return ;
}
});
public boolean thread_before(){
EditText user_text = findViewById(R.id.username);
user = user_text.getText().toString();
EditText password_text = findViewById(R.id.password_before);
password_before = password_text.getText().toString();
EditText password_text_ = findViewById(R.id.password_after);
password_after = password_text_.getText().toString();
if(user.equals("")&&password_before.equals("")&&password_after.equals("")){
Toast.makeText(getApplicationContext(), "请输入用户名,原密码和修改密码", Toast.LENGTH_SHORT).show();
return false;
}else if(user.equals("")&&password_before.equals("")&&(!password_after.equals(""))){
Toast.makeText(getApplicationContext(), "请输入用户名和原密码", Toast.LENGTH_SHORT).show();
return false;
}else if(user.equals("")&&(!password_before.equals(""))&&password_after.equals("")){
Toast.makeText(getApplicationContext(), "请输入用户名和修改密码", Toast.LENGTH_SHORT).show();
return false;
}else if((!user.equals(""))&&password_before.equals("")&&password_after.equals("")){
Toast.makeText(getApplicationContext(), "请输入原密码和修改密码", Toast.LENGTH_SHORT).show();
return false;
}else if(user.equals("")&&(!password_before.equals(""))&&(!password_after.equals(""))){
Toast.makeText(getApplicationContext(), "请输入用户名", Toast.LENGTH_SHORT).show();
return false;
}else if((!user.equals(""))&&password_before.equals("")&&(!password_after.equals(""))){
Toast.makeText(getApplicationContext(), "请输入原密码", Toast.LENGTH_SHORT).show();
return false;
}else if((!user.equals(""))&&(!password_before.equals(""))&&password_after.equals("")){
Toast.makeText(getApplicationContext(), "请输入修改密码", Toast.LENGTH_SHORT).show();
return false;
}else if((!user.equals(""))&&(!password_before.equals(""))&&(!password_after.equals(""))&&password_before.equals(password_after)){
Toast.makeText(getApplicationContext(), "原密码不能和修改密码相等", Toast.LENGTH_SHORT).show();
return false;
}else if((!user.equals(""))&&(!password_before.equals(""))&&(!password_after.equals(""))&&(password_after.length()<6))
{
Toast.makeText(getApplicationContext(), "修改后密码长度不能小于6位", Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
public void thread_after(){
if(flag_test==1){
Toast.makeText(getApplicationContext(), "修改密码成功", Toast.LENGTH_SHORT).show();
}else if(flag_test==0){
Toast.makeText(getApplicationContext(), "用户名错误", Toast.LENGTH_SHORT).show();
}else if(flag_test==2){
Toast.makeText(getApplicationContext(), "原密码错误", Toast.LENGTH_SHORT).show();
}
}
}
总结
这次Android的jdbc之旅呢,收获了很多,本以为和IDEA一样的操作,小改一下就行,结果踩中了那么坑,下次注意喽