Android 连接数据库
不算是第一次搞Android软件了,以前也搞过,但最多的是逆向,没搞过什么像样软件。以前就想搞个能被远程控制Android软件(类似自动检测最新版本进行更新)。现在有点Web基础,接触过了数据库,所以回来搞一下。
做Android软件需要AndroidStudio ,以前可以用Eclipse+SDK+ADT,现在一般不用了。这里不多说了,不懂怎么用Android Studio和需要什么配置可以百度。做这个软件是需要两个包的mysql-connector-java-5.1.30-bin.jar 和 jsch-0.1.54.jar 当然你有其他版本也可以。(下载地址在文章末尾)。接下来讲一下怎么用导入这两个包。
先创建一个Android项目,选Empty Activity 就可以。创建的项目一般的项目结构模式是Android的,但我们一般把项目搞成Project项目结构模式。(如图)然后在我们就把那两个包复制进app目录下的libs目录(如图)。
我们对包点击右键就会看到这个:
点击Add As Library 就可以把把包导入项目了。另一个同理,两个都要这样,导入其他的第三方包都是要这样的步骤。
如果想知道是不是导入成功可以打开app 目录下的build.gradle 文件看到如图就证明成功了。
导入包只是第一步而已。接下来就是写代码了。这些代码也是别人写的(感谢作者分享的代码)。我自己还是不怎么懂写的,只会用而已。这里直接把代码分享出来,然后分析一下之间的联系就可以了。
我们还要创建两个java文件,一个DBUtils 一个 Util 。
DBUtils代码:
import android.util.Log;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedHashMap;
publicclass DBUtils{
private static final String TAG = "DBUtils";
private static Connection getConnection(String dbName) {
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver"); //加载驱动
String ip = "localhost";
conn = DriverManager.getConnection(
"jdbc:mysql://" + ip + ":3306/" + dbName,
"数据库账号", "数据库密码");
} catch (SQLException ex) {
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
return conn;
}
static Connection conn = getConnection("数据库名");
public static LinkedHashMap<String, String>getUserInfoByName(String id) {
LinkedHashMap<String,String> map = new LinkedHashMap<>();
try {
Statement st = conn.createStatement();
String sql = "select * from 数据库的表 where id(这个是注释:我是用表中的id来查找东西,你可以用其他的来查找) = '" + id + "'";
ResultSet res =st.executeQuery(sql);
if (res == null) {
return null;
} else {
int cnt = res.getMetaData().getColumnCount();
//res.last(); int rowCnt = res.getRow();res.first();
res.next();
for (int i = 1; i <= cnt; ++i) {
String field =res.getMetaData().getColumnName(i);
map.put(field,res.getString(field));
}
// conn.close();
st.close();
res.close();
return map;
}
} catch (Exception e) {
e.printStackTrace();
Log.d(TAG, " 数据操作异常");
return null;
}
}
}
这个是我的代码,可以参考一下。
Util的代码:
import android.util.Log;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
public class Util {
public static void go() {
String user = " SSH连接用户名";//SSH连接用户名
String password = " SSH连接密码";//SSH连接密码
String host = " SSH服务器ip";//SSH服务器
int lport = 3306;//本地端口(随便取)这个端口要和DBUtils中的3306对应的所以要改两个都要改
String rhost = "localhost";//远程MySQL服务器
int rport = 3306;//远程MySQL服务端口一般3306
int port = ; //SSH访问端口
try {
JSch jsch = new JSch();
Session session = jsch.getSession(user, host, port);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
Log.e("=======>", "服务器连接成功");
System.out.println(session.getServerVersion());//这里打印SSH服务器版本信息
int assinged_port = session.setPortForwardingL(lport, rhost, rport);//将服务器端口和本地端口绑定,这样就能通过访问本地端口来访问服务器
System.out.println("localhost:" + assinged_port + " -> " + rhost + ":" + rport);
} catch (Exception e) {
e.printStackTrace();
}
}
}嗯这就是这两个工具的代码了。如果你只是连接本地的数据库而已只要DBUtils的工具就行了。Util是用来连接远程数据库
的,比如云数据库,和内网穿透的数据库。(其实这个网上几乎找不到的,因为这样连接数据库很容易被反编译获取数据库的账号密码的,本人也试过反编译过,的确是的,所以是不推荐这个方法连接数据库,不仅是这个只要是直接连接数据库都是不行的不安全的,这里只是学习一下而已)。
简单说一下两个的联系。我是在用Navicat Premium连接我的内网穿透的数据库和网上一些教程中得到的启发。我用
Navicat Premium 连接我的数据库时出错,然后在网上找到教程说先用ssh通道先远程连接服务器,再用常规中的
localhost +3306端口 就可以连接成功了。然后在网上找到了一些其他信息将服务器端口和本地端口绑定,这样就能通过访问本地端口
来访问服务器。可能这个就是原理吧,反正我觉得挺有道理的。然后在Util和DBUtils中就用到了这个思想。Util就是用来把服
务器端口和本地端口绑定,DBUtils就是用localhost +本地端口 来连接数据库的。现在你应该能理解我为什么在Util.go()
中的lport写的本地端口(随便取)这个端口要和DBUtils中的3306对应的所以要改两个都要改 这句话了。所以我们也从中知道了,要远程
连接就必须先把服务器端口和本地端口绑定,所以在执行DBUtils之前应该先Util.go() 一下。
现在是MainActivity的代码了:
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import java.util.LinkedHashMap;
public class MainActivity extends AppCompatActivity {
int j; //为获取全部数据库信息个数的
private static final String TAG = "MainActivity";
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message message) { //在onClick的如果改了what的值就对应输出弹窗信息
String str = "登录失败";
if(message.what == 1) str = "登录成功";
Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
return false;
}
});
Handler handler1 = new Handler(new Handler.Callback() {
public boolean handleMessage(Message message) {
j=message.what; //通过改变what值再赋给j,这个j也是为了数据库个数的
Log.e("J",Integer.toString(j)); //输出信息到控制台而已
return false;
}
});
Handler handler2 = new Handler(new Handler.Callback() {
public boolean handleMessage(Message message) {
((TextView)findViewById(R.id.tv_result)).setText((String)message.obj); //这个是把layout/activity_main.xml的id为tv_result 的文本改成数据库获取回来的信息这里要类型转换一下(String)message.obj
return false;
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new Thread() { //这个线程是在app开启时就执行了
public void run() {
Util.go(); //先运行这个,把服务器端口和本地端口绑定
int j;
Message msg = new Message();
for (int i=1;;i++){
String s = Integer.toString(i);
if(DBUtils.getUserInfoByName(s)==null){ //整个的作用时获取数据库个数
j=i-1;
msg.what=j;
handler1.sendMessage(msg);
Log.e("wwwwwwww",Integer.toString(j));
break;
}
}
}
}.start(); //别忘了还要这个才能开启该线程
setContentView(R.layout.activity_main);
(findViewById(R.id.btn_01)).setOnClickListener(new View.OnClickListener() { //这个是点击按钮触发的事件
@Override
public void onClick(View view) {
new Thread(new Runnable() { //这个线程是为了把每一个信息遍历出来,进行登录的账号密码信息对比
@Override
public void run() {
Message msg = new Message();
for (int i = 1; i <= j; i++){
if(msg.what==1){break;}
String s = Integer.toString(i);
LinkedHashMap<String, String> mp =
DBUtils.getUserInfoByName(s);
if (mp == null) {
msg.what = 0;
msg.obj = "查询结果,空空如也";
//非UI线程不要试着去操作界面
} else {
if (checkLogin(mp)) {
msg.what = 1;
}
}
}
handler.sendMessage(msg);
}
}).start();
}
});
}
private boolean checkLogin(LinkedHashMap<String, String> mp) {
Message msg = new Message();
TextView et_username = (TextView) findViewById(R.id.et_username);
TextView et_password = (TextView) findViewById(R.id.et_password);
final String username = et_username.getText().toString().trim();
final String password = et_password.getText().toString().trim();
if((username.equals(mp.get("name"))) && (password.equals(mp.get("message")))) {
msg.obj=mp.get("xx");
handler2.sendMessage(msg);
return true;
}else {
Log.e(mp.get("name"),mp.get("message"));
msg.obj="NULL";
handler2.sendMessage(msg);
return false;
}
}
}这个代码是修改的最多的,因为原来的代码运行效率极低。主要修改了:
1. 数据库只连接一次,而且是打开app就连接了。
2. 自动获取数据库的个数(这个方法不是很好)
3. 加了个简单的登录界面
这里主要是Handler ,这个东西就厉害,可以把线程的信息保存出来。(主要是为了第二的获取数据库个数)。还有线程,我用多线程方法。(但不知道有没有用) 。DBUtils中的getUserInfoByNam()是获取对应id的信息,返回的mp好像是键值对的形式,因为我是用mp.get(“键“)的方法把值拿出来的。在DBUtils中LinkedHashMap用的是这个,这个的意思把数据库的对应的id的信息原顺序输出,还有这些
也没什么好说的了看代码注释就知道得了程序的怎么弄的了,这个只是简单的软件,所以没有什么难得代码。
接下来还剩layout/activity_main.xml的代码了
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="用户名:"
android:textSize="30sp"
/>
<EditText
android:id="@+id/et_username"
android:layout_width="168dp"
android:layout_height="wrap_content"
android:hint="请输入用户名"
android:textSize="20sp"
/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密 码:"
android:textSize="30sp"
/>
<EditText
android:id="@+id/et_password"
android:layout_width="173dp"
android:layout_height="wrap_content"
android:hint="请输入密码"
android:textSize="20sp"
/>
</LinearLayout>
<Button
android:id="@+id/btn_01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="登录"
android:layout_gravity="center"/>
<TextView
android:text="查询结果:"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
<TextView
android:id="@+id/tv_result"
android:text="这里是信息"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</LinearLayout>这个可以根据自己的想法搞啊,这个只是前端显示而已。
对了最重要的一步:
一定要在AndroidManifest.xml中加上网络权限:
<uses-permission android:name="android.permission.INTERNET"/>我之前就没有加就搞了贼久,都崩溃了。
到目前整个项目完成了。可以打包试试了。我附有源代码和app,可以试试,app我进行混淆和加壳了(要不不敢发出来,怕反编译),虽说是混淆和加壳,但还是能破的,但没人会那么无聊就为了这个软件搞死自己吧。
注:我忘记说一下MainActivity代码的mp.get("")这个东西了,现在附上一张我用来连接的表自己对比一下就能看懂代码了。我给附件下载的app是需要我开虚拟机才可以正常使用,账号密码也是这张图的里面的name是账号 message是密码 xx是软件登录成功会输出的一句话