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端口 就可以连接成功了。然后在网上找到了一些其他信息
将服务器端口和本地端口绑定,这样就能通过访问本地端口

来访问服务器。
可能这个就是原理吧,反正我觉得挺有道理的。然后在UtilDBUtils中就用到了这个思想。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.xmlidtv_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(“键“)的方法把值拿出来的。在DBUtilsLinkedHashMap用的是这个,这个的意思把数据库的对应的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是软件登录成功会输出的一句话


第三方包 软件 下载文章 Android项目源码