Android專案開發筆記之登入註冊模組實現(客戶端+服務端)
寫在前面
斷斷續續開發了幾個月的App終於告一段落,雖然它可能還很不完美,不過作為上手Android的第一個完整專案,確實從中學到了蠻多,所以開個系列記錄一下~本篇先從基本上每個App都會有的登入註冊講起,包含自動登入、記住密碼功能的實現=w=
實現:登入功能
思路
整個登入功能的邏輯為:使用者提交賬號、密碼->判斷賬號密碼是否為空->選項處理(自動登入及記住密碼)->向服務端LoginServlet提交賬號密碼->服務端查詢資料庫判斷賬號是否存在->服務端查詢資料庫判斷賬號密碼是否匹配->返回resCode(登入成功/失敗)
客戶端
注: 客戶端使用Litepal進行資料庫的管理,關於Litepal的配置自行百度=。=
- 在LogInActivity啟動時要先判斷使用者上次是否有勾選了自動登入或記住密碼,這個是通過SharedPreferences實現的,程式碼如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
initComponents();
setListeners();
// 自動填充
SharedPreferencesUtil spu = new SharedPreferencesUtil(this);
Boolean isRemember = (Boolean) spu.getParam("isRememberPwd",false);
Boolean isAutoLogin = (Boolean) spu.getParam("isAutoLogin",false);
// SharedPreference獲取使用者賬號密碼,存在則填充
String account = (String) spu.getParam("account","" );
String pwd = (String)spu.getParam("pwd","");
if(!account.equals("") && !pwd.equals("")){
if(isRemember){
accountText.setText(account);
passwordText.setText(pwd);
isRememberPwd.setChecked(true);
}
if(isAutoLogin)
Login();
}
}
2.判斷賬號密碼是否合理,這裡設定為只有手機/郵箱才能登入
private String checkDataValid(String account,String pwd){
if(TextUtils.isEmpty(account) | TextUtils.isEmpty(pwd))
return "賬號或密碼不能為空";
if(account.length() != 11 && !account.contains("@"))
return "使用者名稱不是有效的手機或郵箱";
return "";
}
3.記錄自動登入及記住密碼選項,同時將最近登入的賬號密碼寫入SharedPreferences中
void OptionHandle(String account,String pwd){
SharedPreferences.Editor editor = getSharedPreferences("UserData",MODE_PRIVATE).edit();
SharedPreferencesUtil spu = new SharedPreferencesUtil(this);
if(isRememberPwd.isChecked()){
editor.putBoolean("isRememberPwd",true);
// 儲存賬號密碼
spu.setParam("account",account);
spu.setParam("pwd",pwd);
}else{
editor.putBoolean("isRememberPwd",false);
}
if(isAutoLogin.isChecked()){
editor.putBoolean("isAutoLogin",true);
}else{
editor.putBoolean("isAutoLogin",false);
}
editor.apply();
}
4.向服務端發起POST請求,這裡將賬號密碼封裝為JSON字串後再提交,JSON的好處大家都懂得~
// 登入請求
public void LoginPost(String account, String password, final Handler mHandler){
final CommonRequest request = new CommonRequest();
// 填充引數
request.addRequestParam("account",account);
request.addRequestParam("pwd",password);
infoPost(Consts.URL_Login, request.getJsonStr());
// 隔一段時間(2.5s)後再發送資訊給LogInActivity,因為網路請求是耗時操作
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
mHandler.sendMessage(message);
}
}, 2500);
}
// 通用的POST資訊方法
private void infoPost(String url, String json){
HttpUtil.sendPost(url,json,new okhttp3.Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
CommonResponse res = new CommonResponse(response.body().string());
resCode = res.getResCode();
resMsg = res.getResMsg();
property = res.getPropertyMap();
dataList = res.getDataList();
}
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
showResponse("Network ERROR");
}
});
}
5.以上3步合起來即為Login方法,程式碼如下:
/**
* POST方式Login
*/
private void Login() {
// 前端引數校驗,防SQL注入
account = Util.StringHandle(accountText.getText().toString());
password = Util.StringHandle(passwordText.getText().toString());
// 檢查資料格式是否正確
String resMsg = checkDataValid(account,password);
if(!resMsg.equals("")){
showResponse(resMsg);
return;
}
// 顯示進度條
progressDialog = new ProgressDialog(this);
progressDialog.setMessage("登入中...");
progressDialog.setCancelable(false);
progressDialog.show();
OptionHandle(account,password);// 處理自動登入及記住密碼
server.LoginPost(account,password,loginHandler);
}
6.loginHandler根據得到的登入狀態碼進行相應處理:
@SuppressLint("HandlerLeak")
Handler loginHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
String resCode = server.getResCode();
String resMsg = server.getResMsg();
// 登入成功
if (resCode != null && resCode.equals(Consts.SUCCESSCODE_LOGIN)) {
// 查詢本地資料庫中是否已存在當前使用者,不存在則新建使用者並寫入
User user = DataSupport.where("account=?",account).findFirst(User.class);
if(user == null){
user = new User();
user.setAccount(account);
user.setPassword(password);
user.setVisitor(false);
user.save();
}
UserManager.setCurrentUser(user);// 設定當前使用者
autoStartActivity(MainActivity.class);
}
progressDialog.dismiss();// 隱藏進度條
showResponse(resMsg);// Toast相應資訊
break;
}
}
};
服務端
注:服務端使用Java Servlet實現,這個網上也有很多資料,不多說
- 因為使用的是POST方式,所以以下步驟均在Servlet的doPost中進行;
- 獲取客戶端發來的請求,將request物件轉化為字串,進而恢復其JSON格式
// request轉字串
BufferedReader read = request.getReader();
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = read.readLine()) != null) {
sb.append(line);
}
String req = sb.toString();
// 獲取 客戶端 發來的請求,恢復其Json格式——>需要客戶端發請求時也封裝成Json格式
JSONObject object = JSONObject.fromObject(req);
3.提取json中的requestCode和requestParam,requestCode可以用於區分同一類請求下的不同子請求,eg.獲取資料庫的不同資訊
// requestCode、requestParam要和客戶端CommonRequest封裝時候的名字一致
String requestCode = object.getString("requestCode"); // 暫時不用
JSONObject requestParam = object.getJSONObject("requestParam");
4.提取賬號密碼
// json中提取引數
String account = requestParam.getString("account");
String pwd = requestParam.getString("pwd");
5.定義查詢語句、查詢結果集、Response資訊物件
// 自定義的結果資訊類
CommonResponse res = new CommonResponse();
// Sql查詢語句
String sqlQueryExist = "select * from "+DBUtil.table_user_pwd+" where username=?;";
// 查詢結果
ResultSet resultSet = null;
ArrayList<String> args = new ArrayList<String>();
6.核心邏輯:填充查詢引數->執行查詢語句->判斷賬號是否存在->若存在則查詢密碼是否匹配->否則返回錯誤資訊
try {
args.add(account);
resultSet = DBUtil.query(sqlQueryExist, args);
if(resultSet.next()){// 賬號存在,查詢密碼是否正確
if(resultSet.getString("pwd").equals(pwd)){// 密碼正確
res.setResult("100", "登入成功!");
}else{// 密碼錯誤
res.setResult("201", "密碼錯誤!");
}
}else{// 賬號不存在
res.setResult("202", "賬號不存在,請先註冊!");
}
} catch (Exception e) {
res.setResult("300", "資料庫查詢錯誤");
e.printStackTrace();
}
7.將結果封裝為Json格式返回給客戶端
// 注意實際網路傳輸時還是傳輸json的字串
String resStr = JSONObject.fromObject(res).toString();
response.getWriter().append(resStr).flush();
實現:註冊功能
思路
註冊功能邏輯為:使用者提交賬號、密碼、確認密碼->判斷三者是否均不為空->判斷兩次輸入的密碼是否一致->判斷使用者名稱是否合理->提交賬號密碼至服務端->服務端查詢賬號是否已存在->若不存在則插入賬號密碼至user_pwd表中->向客戶端返回資訊
客戶端
註釋寫的很清楚了,直接放原始碼~
/**
* POST方式Register
*/
private void register() {
// 建立請求體物件
CommonRequest request = new CommonRequest();
// 前端引數校驗,防SQL注入
String account = Util.StringHandle(accountText.getText().toString());
String pwd = Util.StringHandle(pwdText.getText().toString());
String pwd_confirm = Util.StringHandle(confirmPwdText.getText().toString());
// 檢查資料格式是否正確
String resMsg = checkDataValid(account,pwd,pwd_confirm);
if(!resMsg.equals("")){
showResponse(resMsg);
return;
}
// 填充引數
request.addRequestParam("account",account);
request.addRequestParam("pwd",pwd);
// POST請求
HttpUtil.sendPost(Consts.URL_Register, request.getJsonStr(), new okhttp3.Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
CommonResponse res = new CommonResponse(response.body().string());
String resCode = res.getResCode();
String resMsg = res.getResMsg();
// 顯示註冊結果
showResponse(resMsg);
// 註冊成功
if (resCode.equals(Consts.SUCCESSCODE_REGISTER)) {
finish();
}
}
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
showResponse("Network ERROR");
}
});
}
private String checkDataValid(String account,String pwd,String pwd_confirm){
if(TextUtils.isEmpty(account) | TextUtils.isEmpty(pwd) | TextUtils.isEmpty(pwd_confirm))
return "賬號或密碼不能為空";
if(!pwd.equals(pwd_confirm))
return "兩次輸入的密碼需保持一致";
if(account.length() != 11 && !account.contains("@"))
return "使用者名稱不是有效的手機號或郵箱";
return "";
}
服務端
- 1-4步與登入功能裡的一致,不再贅述
- 核心邏輯:填充查詢引數->查詢賬號是否存在->若不存在執行Insert操作,同時更新userInfo表->向客戶端返回訊息
// 自定義的結果資訊類
CommonResponse res = new CommonResponse();
// Sql語句
String sqlQueryExist = "select * from "+DBUtil.table_user_pwd+" where username=?;";
String sqlInsert = "insert into "+DBUtil.table_user_pwd+"(username,pwd)"+
" values(?,?);";
// 查詢結果
ResultSet resultSet = null;
ArrayList<String> args = new ArrayList<String>();
try {
DBUtil.checkConnection();
args.add(account);
resultSet = DBUtil.query(sqlQueryExist, args);
if(resultSet.next()){// resSet不為空
res.setResult("203", "賬號已存在,請登入");
}else{
args.add(pwd);
int rows = DBUtil.update(sqlInsert, args);// 返回插入後受影響的行數
if(rows==1){// 插入userpwd表成功
String sqlQueryId = "select userId from "+DBUtil.table_user_pwd+
" where username=?;";
args.clear();
args.add(account);
ResultSet resSet = DBUtil.query(sqlQueryId, args);
if(resSet.next()){
// 獲取使用者id,更新userInfo表
String userId = resSet.getInt("userId")+"";
String sqlInsertId = "insert into "+DBUtil.table_user_info+
"(userId)"+" values(?);";
args.clear();
args.add(userId);
rows = DBUtil.update(sqlInsertId, args);
if(rows == 1 ){
res.setResult("101", "註冊成功");
}
}
}else{
res.setResult("204", "使用者資訊插入失敗");
}
}
} catch (SQLException e) {
res.setResult("300", "資料庫查詢錯誤");
e.printStackTrace();
}
// 將結果封裝成Json格式準備返回給客戶端,但實際網路傳輸時還是傳輸json的字串
String resStr = JSONObject.fromObject(res).toString();
response.getWriter().append(resStr).flush();
最後
登入註冊模組到這裡就完成啦!裡面涉及到一些功能類和功能函式的封裝,不難實現,原始碼放Github了,歡迎star~