Android通过Servlet连接MySQL 实现登陆/注册(数据库+服务器+客户端),androidmysql
写在最前:
在实际开发中,相信每个项目都会有用户登陆注册功能,这个实现的方法很多,下面是我实现的方法,供大家交流。
新人发帖,万分紧张,怎么样才能装作一副经常发帖的样子不被别人看出来呢-,- ?
好了,下面进入正题。
一、开发环境的部署
程序结构:
android+servlet+service+mysql
仅供参考:能实现相关功能即可
操作系统:ubuntu 14.10
数据库:mysql-5.5 数据库工具:emma
服务器:tomcat 服务器工具:Myeclipse 10
安卓端:真机 android4.4 安卓端工具:eclipse+adt
注意:
程序调试过程可能会产生乱码,只需保持所有工具编码方式相同即可。
二、数据库设计
数据库名称:test
表名称:student
建表语句:
CREATE TABLE `student` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`username` char(20) NOT NULL DEFAULT '',
`password` char(20) NOT NULL DEFAULT '',
PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
表格视图:
三、服务器端设计
1、新建Web Project,命名为HelloWeb,同时删除掉WebRoot的index.jsp
2、项目结构图如下:
这里我们采用servlet编程,所以不需要任何jsp页面。
LogLet类和RegLet类分别用于处理客户端的登陆和注册请求;Service类用于完成servlet对数据库的具体操作;DBManager类用于进行数据库基本操作;
左侧是项目图,右侧是web.xml配置文件截图。
3、项目代码:
DBManager.java
<1> 私有化DBManager的构造函数,定义一个静态的成员变量,在一个共有方法中实例化该成员变量。若要实例化对象调用此方法即可。
同一时间该类只能存在一个对象。符合sql对象习惯。 (此方式有缺陷,具体自行搜索)
<2> 定义数据库连接、关闭以及增删改查的基本操作,返回结果集。
package com.db;
import java.sql.*;
public class DBManager {
// 数据库连接常量
public static final String DRIVER = "com.mysql.jdbc.Driver";
public static final String USER = "root";
public static final String PASS = "root";
public static final String URL = "jdbc:mysql://localhost:3306/test";
// 静态成员,支持单态模式
private static DBManager per = null;
private Connection conn = null;
private Statement stmt = null;
// 单态模式-懒汉模式
private DBManager() {
}
public static DBManager createInstance() {
if (per == null) {
per = new DBManager();
per.initDB();
}
return per;
}
// 加载驱动
public void initDB() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (Exception e) {
e.printStackTrace();
}
}
// 连接数据库,获取句柄+对象
public void connectDB() {
System.out.println("Connecting to database...");
try {
conn = DriverManager.getConnection(URL, USER, PASS);
stmt = conn.createStatement();
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println("SqlManager:Connect to database successful.");
}
// 关闭数据库 关闭对象,释放句柄
public void closeDB() {
System.out.println("Close connection to database..");
try {
stmt.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
System.out.println("Close connection successful");
}
// 查询
public ResultSet executeQuery(String sql) {
ResultSet rs = null;
try {
rs = stmt.executeQuery(sql);
} catch (SQLException e) {
e.printStackTrace();
}
return rs;
}
// 增添/删除/修改
public int executeUpdate(String sql) {
int ret = 0;
try {
ret = stmt.executeUpdate(sql);
} catch (SQLException e) {
e.printStackTrace();
}
return ret;
}
}
Service.java
这个简单,根据传参得到sql语句,通过DBManager类的 createInstance() 方法实例化对象,调用本类的操作方法,完成数据操作。
写到这里,可以预见:下一个类会通过调用本类方法完成登陆/注册的服务。
package com.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.db.DBManager;
public class Service {
public Boolean login(String username, String password) {
// 获取Sql查询语句
String logSql = "select * from user where username ='" + username
+ "' and password ='" + password + "'";
// 获取DB对象
DBManager sql = DBManager.createInstance();
sql.connectDB();
// 操作DB对象
try {
ResultSet rs = sql.executeQuery(logSql);
if (rs.next()) {
sql.closeDB();
return true;
}
} catch (SQLException e) {
e.printStackTrace();
}
sql.closeDB();
return false;
}
public Boolean register(String username, String password) {
// 获取Sql查询语句
String regSql = "insert into student values('"+ username+ "','"+ password+ "') ";
// 获取DB对象
DBManager sql = DBManager.createInstance();
sql.connectDB();
int ret = sql.executeUpdate(regSql);
if (ret != 0) {
sql.closeDB();
return true;
}
sql.closeDB();
return false;
}
}
LogLet.java
一个简单的Servlet,用于处理Http请求(get/post)。果然,实例化上一个类的对象,并调用了login方法,返回值为布尔类型。
RegLet.java和该类近乎相同,只是在 serv.login(username, password); 换成了 serv.register(username, password); 此处省去~
package com.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.service.Service;
public class LogLet extends HttpServlet {
private static final long serialVersionUID = 369840050351775312L;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 接收客户端信息
String username = request.getParameter("username");
username = new String(username.getBytes("ISO-8859-1"), "UTF-8");
String password = request.getParameter("password");
System.out.println(username + "--" + password);
// 新建服务对象
Service serv = new Service();
// 验证处理
boolean loged = serv.login(username, password);
if (loged) {
System.out.print("Succss");
request.getSession().setAttribute("username", username);
// response.sendRedirect("welcome.jsp");
} else {
System.out.print("Failed");
}
// 返回信息到客户端
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.print("用户名:" + username);
out.print("密码:" + password);
out.flush();
out.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
}
四、客户端设计
1、新建Android App Project,命名为AndroidHTTPDemo
2、现在开始思考需要什么东西…
<1> 登陆和注册页面:布局文件
login.xml , register.xml
<2> 登陆和注册页面对应的Activity组件,在activity中进行具体操作
login.java , register.java
<3> 能够实现Http以get/post方式通信的类
WebService.java , WebServicePost.java
<4> 网络通信权限
OK,项目结构出炉,右侧是Manifeast配置文件的主要内容
3、现在,我们开始关注具体的代码。
<1> 首先要做的,登陆注册界面,这个不用多说。我直接放图,大致就是下面这个样子,大家可以按照自己爱好设计。
(注意一点,因为登陆和注册的xml、activity都是近乎完全一样,最不一样的sql语句我们在之前已经处理过了,所以这里只写其中的一个即可)
<2> 在服务器端编程时我们了解到:服务器端接收客户端发送的信息,对信息进行一系列处理后,最终信息返回到客户端。
首先要想的,就是获取信息并发送出去,然后接收信息并显示出来。
获取信息好办,getText()嘛,不好办的是发送,还有发送所需的线程。
(网络服务由于耗时问题,放在主线程很可能由于网络故障导致ANR;所以要开辟子线程留给http网络服务。当然不使用主线程也可以,只是不推荐)
<3> Login.java 有三点需要注意
第一个是检测网络状态,只能检测流量,无法检测wifi;
第二个是在子线程中,我们利用获得的用户名密码调用了http通信类最后返回的info值,不能直接在子线程中更改主线程的页面值,这里用了handle解决。
第三个是这里有get/post两种http请求方式,两个实现类,我们需要那一个,只要把另一个注释掉即可,返回的数据都是一样的。
package com.httpdemo;
import com.rxz.androidhttpdemo.R;
import com.web.WebService;
import com.web.WebServicePost;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.os.Handler;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class Login extends Activity implements OnClickListener {
// 登陆按钮
private Button logbtn;
// 调试文本,注册文本
private TextView infotv, regtv;
// 显示用户名和密码
EditText username, password;
// 创建等待框
private ProgressDialog dialog;
// 返回的数据
private String info;
// 返回主线程更新数据
private static Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login);
// 获取控件
username = (EditText) findViewById(R.id.user);
password = (EditText) findViewById(R.id.pass);
logbtn = (Button) findViewById(R.id.login);
regtv = (TextView) findViewById(R.id.register);
infotv = (TextView) findViewById(R.id.info);
// 设置按钮监听器
logbtn.setOnClickListener(this);
regtv.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.login:
// 检测网络,无法检测wifi
if (!checkNetwork()) {
Toast toast = Toast.makeText(Login.this,"网络未连接", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
break;
}
// 提示框
dialog = new ProgressDialog(this);
dialog.setTitle("提示");
dialog.setMessage("正在登陆,请稍后...");
dialog.setCancelable(false);
dialog.show();
// 创建子线程,分别进行Get和Post传输
new Thread(new MyThread()).start();
break;
case R.id.register:
Intent regItn = new Intent(Login.this, Register.class);
// overridePendingTransition(anim_enter);
startActivity(regItn);
break;
}
;
}
// 子线程接收数据,主线程修改数据
public class MyThread implements Runnable {
@Override
public void run() {
info = WebService.executeHttpGet(username.getText().toString(), password.getText().toString());
// info = WebServicePost.executeHttpPost(username.getText().toString(), password.getText().toString());
handler.post(new Runnable() {
@Override
public void run() {
infotv.setText(info);
dialog.dismiss();
}
});
}
}
// 检测网络
private boolean checkNetwork() {
ConnectivityManager connManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if (connManager.getActiveNetworkInfo() != null) {
return connManager.getActiveNetworkInfo().isAvailable();
}
return false;
}
}
<4> WebService.java
这里的IP是你的服务器IP,不确定时看下是否能用手机ping工具ping通。
因为我用的是真机,所以模拟器我还真不太清楚,我简单说一下真机与windows/linux下的服务器网络连接流程,详情请百度。
① 你的服务器端程序已发布到互联网:这好办,就是你的IP地址。
② 你是在本地电脑上,这要求你的真机和你的电脑在同一个局域网。两种较方便的方法:路由器/笔记本的无线网卡
是个人都能看出来第二种方便,谁也不能到哪都带个路由器吧,那么好,笔记本开启无线热点,手机wifi连接热点,这是客户端和服务器就在一个局域网内。
查看笔记本ip地址中的无线网卡地址([win]ipconfig/[lnx]ifconfig – wlan),加上你的服务器端口号(服务器为开启状态),访问即可。
conn.setConnectTimeout(3000);需要设置超时时间,否则会执行默认超时时间,30s ?
接收到的输入流需要先转换成比特位,在转换成string类型。
package com.web;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class WebService {
private static String IP = "10.42.0.1:8080";
// 通过Get方式获取HTTP服务器数据
public static String executeHttpGet(String username, String password) {
HttpURLConnection conn = null;
InputStream is = null;
try {
// 用户名 密码
// URL 地址
String path = "http://" + IP + "/HelloWeb/servlet/MyServlet";
path = path + "?username=" + username + "&password=" + password;
conn = (HttpURLConnection) new URL(path).openConnection();
conn.setConnectTimeout(3000); // 设置超时时间
conn.setReadTimeout(3000);
conn.setDoInput(true);
conn.setRequestMethod("GET"); // 设置获取信息方式
conn.setRequestProperty("Charset", "UTF-8"); // 设置接收数据编码格式
if (conn.getResponseCode() == 200) {
is = conn.getInputStream();
return parseInfo(is);
}
}catch (Exception e) {
e.printStackTrace();
} finally {
// 意外退出时进行连接关闭保护
if (conn != null) {
conn.disconnect();
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
// 将输入流转化为 String 型
private static String parseInfo(InputStream inStream) throws Exception {
byte[] data = read(inStream);
// 转化为字符串
return new String(data, "UTF-8");
}
// 将输入流转化为byte型
public static byte[] read(InputStream inStream) throws Exception {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
inStream.close();
return outputStream.toByteArray();
}
}
<5> WebServicePost.java 和上一个大同小异,只不过参数不是放在url中,而是在HashMap中传输,数据传输方式略有不同。
处理方式不变,还有注意别忘了设置超时。
package com.web;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreConnectionPNames;
public class WebServicePost {
private static String IP = "10.42.0.1:8080";
// 通过 POST 方式获取HTTP服务器数据
public static String executeHttpPost(String username, String password) {
try {
String path = "http://" + IP + "/HelloWeb/servlet/MyServlet";
// 发送指令和信息
Map params = new HashMap();
params.put("username", username);
params.put("password", password);
return sendPOSTRequest(path, params, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// 处理发送数据请求
private static String sendPOSTRequest(String path, Map params, String encoding) throws Exception {
List pairs = new ArrayList();
if (params != null && !params.isEmpty()) {
for (Map.Entry entry : params.entrySet()) {
pairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(pairs, encoding);
HttpPost post = new HttpPost(path);
post.setEntity(entity);
DefaultHttpClient client = new DefaultHttpClient();
// 请求超时
client.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 5000);
// 读取超时
client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 5000);
HttpResponse response = client.execute(post);
// 判断是否成功收取信息
if (response.getStatusLine().getStatusCode() == 200) {
return getInfo(response);
}
// 未成功收取信息,返回空指针
return null;
}
// 收取数据
private static String getInfo(HttpResponse response) throws Exception {
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent();
// 将输入流转化为byte型
byte[] data = WebService.read(is);
// 转化为字符串
return new String(data, "UTF-8");
}
}
五、运行效果
以上工作完成后,只需要讲服务器端发布到本地(附上–mysql-jdbc驱动地址–),安卓端发布到手机,确保局域网内部,ip正确,即可正常访问。
客户端截图:测试成功
服务器端截图:测试成功
六、最后
以上不足之处,还望大家多多指正。如有问题欢迎给我留言。
代码并未涉及到Session保持,自动登陆等,正在改进中,最终效果应该类似于虎牙直播的登陆注册(刚好举个例子)
原文地址:http://www.bkjia.com/Androidjc/1045369.html