本文的知识点包括android客户端的网络编程、消息机制、IO流、多线程和java web服务器端的servlet、数据库操作、javabean技术、工具类和测试类的使用。
客户端
运行效果图
布局文件
activity_main.xml
程序主入口
MainActivity
package com.ningxiaojian.clientlogindemo;
import android.annotation.SuppressLint;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
public class MainActivity extends AppCompatActivity {
private EditText username;
private EditText password;
private String usernameStr;
private String passwordStr;
private final int LOGINSUCCESS=0;
private final int LOGINNOTFOUND=1;
private final int LOGINEXCEPT=2;
private final int REGISTERSUCCESS=3;
private final int REGISTERNOTFOUND=4;
private final int REGISTEREXCEPT=5;
@SuppressLint("HandlerLeak")
Handler handler=new Handler(){//消息机制,用来在子线程中更新UI
@Override
public void handleMessage(Message msg) {
switch (msg.what){//具体消息,具体显示
case LOGINSUCCESS:
Toast.makeText(getApplicationContext(),(String)msg.obj,Toast.LENGTH_LONG).show();
break;
case LOGINNOTFOUND:
Toast.makeText(getApplicationContext(),(String)msg.obj,Toast.LENGTH_LONG).show();
break;
case LOGINEXCEPT:
Toast.makeText(getApplicationContext(),(String)msg.obj,Toast.LENGTH_LONG).show();
break;
case REGISTERSUCCESS:
Toast.makeText(getApplicationContext(),(String)msg.obj,Toast.LENGTH_LONG).show();
break;
case REGISTERNOTFOUND:
Toast.makeText(getApplicationContext(),(String)msg.obj,Toast.LENGTH_LONG).show();
break;
case REGISTEREXCEPT:
Toast.makeText(getApplicationContext(),(String)msg.obj,Toast.LENGTH_LONG).show();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//找到我们需要的控件
username = (EditText) findViewById(R.id.et_username);
password = (EditText) findViewById(R.id.et_password);
}
//登录按钮的点击事件,也可以用set监听器的方法,不过这种方法简单
public void login(View v){
//获取编辑框内的内容
usernameStr = username.getText().toString().trim();
passwordStr = password.getText().toString().trim();
//判断是否输入为空(在这里就不再进行正则表达式判断了)
if(usernameStr.equals("") || passwordStr.equals("")){
Toast.makeText(MainActivity.this,"用户名或密码不能为空",Toast.LENGTH_SHORT).show();
}//进行登录操作(联网操作要添加权限)
else {
//联网操作要开子线程,在主线程不能更新UI
new Thread(){
private HttpURLConnection connection;
@Override
public void run() {
try {
//封装成传输数据的键值对,无论get还是post,传输中文时都要进行url编码(RULEncoder)
// 如果是在浏览器端的话,它会自动进行帮我们转码,不用我们进行手动设置
String data2= "username="+ URLEncoder.encode(usernameStr,"utf-8")+"&password="+ URLEncoder.encode(passwordStr,"utf-8")+"&sign="+URLEncoder.encode("1","utf-8");
connection=HttpConnectionUtils.getConnection(data2);
int code = connection.getResponseCode();
if(code==200){
InputStream inputStream = connection.getInputStream();
String str = StreamChangeStrUtils.toChange(inputStream);//写个工具类流转换成字符串
Message message = Message.obtain();//更新UI就要向消息机制发送消息
message.what=LOGINSUCCESS;//用来标志是哪个消息
message.obj=str;//消息主体
handler.sendMessage(message);
}
else {
Message message = Message.obtain();
message.what=LOGINNOTFOUND;
message.obj="注册异常...请稍后再试";
handler.sendMessage(message);
}
} catch (Exception e) {//会抛出很多个异常,这里抓一个大的异常
e.printStackTrace();
Message message = Message.obtain();
message.what=LOGINEXCEPT;
message.obj="服务器异常...请稍后再试";
handler.sendMessage(message);
}
}
}.start();//不要忘记开线程
}
}
//注册按钮的点击事件
public void register(View v){
usernameStr = username.getText().toString().trim();
passwordStr = password.getText().toString().trim();
if(usernameStr.equals("") || passwordStr.equals("")){
Toast.makeText(MainActivity.this,"用户名或密码不能为空",Toast.LENGTH_SHORT).show();
}
else {
new Thread(){
HttpURLConnection connection=null;
@Override
public void run() {
try {
String data= "username="+ URLEncoder.encode(usernameStr,"utf-8")+"&password="+ URLEncoder.encode(passwordStr,"utf-8")+"&sign="+URLEncoder.encode("2","utf-8");
connection=HttpConnectionUtils.getConnection(data);
int code = connection.getResponseCode();
if(code==200){
InputStream inputStream = connection.getInputStream();
String str = StreamChangeStrUtils.toChange(inputStream);
Message message = Message.obtain();
message.obj=str;
message.what=REGISTERSUCCESS;
handler.sendMessage(message);
}
else {
Message message = Message.obtain();
message.what=REGISTERNOTFOUND;
message.obj="注册异常...请稍后再试";
handler.sendMessage(message);
}
} catch (Exception e) {
e.printStackTrace();
Message message = Message.obtain();
message.what=REGISTEREXCEPT;
message.obj="服务器异常...请稍后再试";
handler.sendMessage(message);
}
}
}.start();//不要忘记开线程
}
}
}
流转换成字符串的工具类
StreamChangeStrUtils
package com.ningxiaojian.clientlogindemo;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 流转换成字符串的工具类
*/
public class StreamChangeStrUtils {
public static String toChange(InputStream inputStream) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();//数组流,在流的内部有个缓冲区,可以进行转换成字节
//下面是属于io流的知识,在此不再赘述
byte b[]=new byte[1024];
int len=-1;
while ((len=inputStream.read(b))!=-1){
bos.write(b,0,len);
}
inputStream.close();//关闭流,数组流会自动关闭,关闭是否都可以
String str = new String(bos.toByteArray());
//服务器默认返回的是gbk,如果要在android端解决乱码,可以在此设置为gbk,一般提倡的是服务器解决
// 让服务器给我们返回utf-8,因为在android本地默认的是utf-8
return str;
}
}
联网工具类
HttpConnectionUtils
package com.ningxiaojian.clientlogindemo;
import android.os.Message;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLEncoder;
/**
* 获取联网连接
* Created by Justin on 2018/4/16.
*/
public class HttpConnectionUtils {
public static HttpURLConnection getConnection(String data) throws Exception {
//通过URL对象获取联网对象
URL url= new URL("http://192.168.1.104:8080/AndroidLoginDemo/LoginServlet");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");//设置post请求
connection.setReadTimeout(5000);//设置5s的响应时间
connection.setDoOutput(true);//允许输出
connection.setDoInput(true);//允许输入
//设置请求头,以键值对的方式传输(以下这两点在GET请求中不用设置)
connection.setRequestProperty("Content-Type","application/x-www-form-urlencoded ");
connection.setRequestProperty("Content-Length",data.length()+"");//设置请求体的长度
OutputStream outputStream = connection.getOutputStream();
outputStream.write(data.getBytes());//进行传输操作
//判断服务端返回的响应码,这里是http协议的内容
return connection;
}
}
联网操作一定要在
AndroidManifest.xml加上权限
<uses-permission android:name="android.permission.INTERNET"/> 服务器端(Tomcat) 首先去mysql创建一个数据库,表结构如下 因为使用到mysql,所以要把jdbc的驱动类添加到webcontent->web-inf->lib中 mysql-connector-java-5.1.45-bin.jar 在eclipse创建以下包名 com.ningxiaojian.dao:dao层接口 com.ningxiaojian.dao.impl:dao层接口的实现 com.ningxiaojian.domain:javabean对象 com.ningxiaojian.service:逻辑层的接口 com.ningxiaojian.service.impl:逻辑层的实现 com.ningxiaojian.web.control:存放servlet com.ningxiaojian.utils:存放工具类 com.ninxiaojian.test:存放测试类 创建java对象 User.java
package com.ningxiaojian.domain;
/**
*
*设置一个javabean对象,用来封装dao层取出的数据
* @author Justin
*
*/
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
package com.ningxiaojian.dao;
import java.util.List;
import com.ningxiaojian.domain.User;
public interface UserDao {
//找到所有元素,用来验证登录信息
public List findAll();
//插入元素,用来注册
public void insertElement(User people);
}
dao层实现类
package com.ningxiaojian.dao.impl;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.ningxiaojian.dao.UserDao;
import com.ningxiaojian.domain.User;
import com.ningxiaojian.utils.JDBCUtils;
public class UserDaoImpl implements UserDao {
Connection connection=null;
PreparedStatement ps=null;
ResultSet rs=null;
/**
* dao层,从数据库里面取出数据
*/
@Override
public List findAll() {
List list=null;
try {
//通过工具类获得连接
connection = JDBCUtils.getConnetion();
//通过连接对象获取操作数据库的对象
String sql="SELECT * FROM user;";//查询sql语句
ps=connection.prepareStatement(sql);
//返回查询结果集
rs=ps.executeQuery();
//遍历rs,并封装数据
list=new ArrayList();
while(rs.next()) {
User user=new User();
user.setUsername(rs.getString(2));//索引从1开始,id参数不用取
user.setPassword(rs.getString(3));
list.add(user);
}
} catch (Exception e) {
e.printStackTrace();
}
finally{
JDBCUtils.close(connection, ps, rs);//关闭连接
}
return list;
}
@Override
public void insertElement(User people) {
try {
connection=JDBCUtils.getConnetion();
String sql="INSERT INTO user(username,password) VALUES(?,?);";//插入语句
ps=connection.prepareStatement(sql);
ps.setString(1,people.getUsername());//使用prepareStatement可以防止sql注入
ps.setString(2,people.getPassword());
//执行更新语句
ps.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}
finally {
JDBCUtils.close(connection, ps, rs);
}
}
}
package com.ningxiaojian.utils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 因为要多次用到以下的步骤,所以写一个工具类来操作jdbc
* @author Justin
*在这里不要导错包,import com.mysql.jdbc.PreparedStatement;是错的
*/
public class JDBCUtils {
/**
* 获得jdbc连接
*/
static Connection connection=null;
public static Connection getConnetion() throws Exception {
//加载jdbc驱动
Class.forName("com.mysql.jdbc.Driver");
//创建连接数据库的路径
String url = "jdbc:mysql://localhost/android_login?user=root&password=12345";
//通过url获得与数据库的连接
connection = DriverManager.getConnection(url);
return connection;
}
public static void close(Connection connection,PreparedStatement ps,ResultSet rs) {
//一定要确保关闭连接,以下关闭步骤是参照官方文档的,有权威性
if(rs!=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(ps!=null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection!=null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
package com.ninxiaojian.test;
import java.util.List;
import org.junit.jupiter.api.Test;
import com.ningxiaojian.dao.UserDao;
import com.ningxiaojian.dao.impl.UserDaoImpl;
import com.ningxiaojian.domain.User;
class TestDao {
/**
* 测试类,测试dao层那两个方法是否查询和插入正确
*/
@Test
void testFindAll() {
UserDao dao=new UserDaoImpl();
List list = dao.findAll();
for(int i=0;i
package com.ningxiaojian.web.control;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.ningxiaojian.domain.User;
import com.ningxiaojian.service.UserService;
import com.ningxiaojian.service.impl.UserServiceImpl;
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");//解决请求乱码(post)
response.setCharacterEncoding("UTF-8");//解决响应乱码,下面要以字符流输出(若字节流输出则要再次编码)
String username=request.getParameter("username");
String password=request.getParameter("password");
String sign=request.getParameter("sign");
PrintWriter out=response.getWriter();
//把传来的数据封装进javabean中
User user=new User();
user.setUsername(username);
user.setPassword(password);
UserService service=new UserServiceImpl();
if("1".equals(sign)) {//登录操作(设置了一个标记)
String loginInfo=service.checkLogin(user);
out.print(loginInfo);
}
else if("2".equals(sign)) {//注册操作
String registerInfo=service.register(user);
out.print(registerInfo);
}
System.out.println(username);//在控制台输出
System.out.println(password);
System.out.println(sign);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
package com.ningxiaojian.service;
import com.ningxiaojian.domain.User;
public interface UserService {
//查验登录
public String checkLogin(User user);
//注册用户
public String register(User user);
}
package com.ningxiaojian.service.impl;
import java.util.List;
import com.ningxiaojian.dao.UserDao;
import com.ningxiaojian.dao.impl.UserDaoImpl;
import com.ningxiaojian.domain.User;
import com.ningxiaojian.service.UserService;
public class UserServiceImpl implements UserService {
UserDao dao=new UserDaoImpl();
/*
* 主要的逻辑实现
*/
@Override
public String checkLogin(User user) {
List list = dao.findAll();
for(int i=0;i list = dao.findAll();
for(int i=0;i