各類it學習視頻,大家都可以看看哦!我自己本人都是通過這些來學習it只知識的!
下面是視頻鏈接
轉自:http://www.cnblogs.com/yzxk/p/4749440.html
Android通過Http連接MySQL 實現登陸/注冊(數據庫+服務器+客戶端)
時間 2015-08-22 11:31:00 博客園-原創精華區
原文
http://www.cnblogs.com/yzxk/p/4749440.html
主題 MySQL HTTP
寫在最前:
在實際開發中,相信每個項目都會有 用戶登陸注冊 功能,這個實現的方法很多,下面是我實現的方法,供大家交流。
新人發帖,萬分緊張,怎么樣才能裝作一副經常發帖的樣子不被別人看出來呢-,- ?
好了,下面進入正題。
一、開發環境的部署
程序結構:
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> 網絡通信權限
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
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<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正確,即可正常訪問。
客戶端截圖:測試成功
服務器端截圖:測試成功
六、源碼
以上不足之處,還望大家多多指正。 如有問題歡迎給我留言。
代碼並未涉及到Session保持,自動登陸等,正在改進中,最終效果應該類似於虎牙直播的登陸注冊(剛好舉個例子)