转载

Android通过Http连接MySQL 实现登陆/注册(数据库+服务器+客户端)

写在最前:

在实际开发中,相信每个项目都会有 用户登陆注册 功能,这个实现的方法很多,下面是我实现的方法,供大家交流。

新人发帖,万分紧张,怎么样才能装作一副经常发帖的样子不被别人看出来呢-,- ?

好了,下面进入正题。

一、开发环境的部署

程序结构:

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

表格视图:

Android通过Http连接MySQL 实现登陆/注册(数据库+服务器+客户端)

三、服务器端设计

1、新建Web Project,命名为HelloWeb,同时删除掉WebRoot的index.jsp

2、项目结构图如下:

这里我们采用servlet编程,所以不需要任何jsp页面。

LogLet类和RegLet类分别用于处理客户端的登陆和注册请求;Service类用于完成servlet对数据库的具体操作;DBManager类用于进行数据库基本操作;

左侧是项目图,右侧是web.xml配置文件截图。

Android通过Http连接MySQL 实现登陆/注册(数据库+服务器+客户端) Android通过Http连接MySQL 实现登陆/注册(数据库+服务器+客户端)

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> 网络通信权限

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<uses-permission android:name="android.permission.INTERNET" />

OK,项目结构出炉,右侧是Manifeast配置文件的主要内容

Android通过Http连接MySQL 实现登陆/注册(数据库+服务器+客户端)

3、现在,我们开始关注具体的代码。

<1> 首先要做的,登陆注册界面,这个不用多说。我直接放图, 大致就是下面这个样子 ,大家可以按照自己爱好设计。

(注意一点,因为登陆和注册的xml、activity都是近乎完全一样,最不一样的sql语句我们在之前已经处理过了,所以这里只写其中的一个即可)

Android通过Http连接MySQL 实现登陆/注册(数据库+服务器+客户端)

<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<String, String> params = new HashMap<String, String>();      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<String, String> params, String encoding) throws Exception {  List<NameValuePair> pairs = new ArrayList<NameValuePair>();  if (params != null && !params.isEmpty()) {      for (Map.Entry<String, String> 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正确,即可正常访问。

客户端截图:测试成功

Android通过Http连接MySQL 实现登陆/注册(数据库+服务器+客户端)

服务器端截图:测试成功

Android通过Http连接MySQL 实现登陆/注册(数据库+服务器+客户端)

六、源码

以上不足之处,还望大家多多指正。 如有问题欢迎给我留言。

代码并未涉及到Session保持,自动登陆等,正在改进中,最终效果应该类似于虎牙直播的登陆注册(刚好举个例子)

正文到此结束
Loading...