1. 程式人生 > >Android專案開發筆記之登入註冊模組實現(客戶端+服務端)

Android專案開發筆記之登入註冊模組實現(客戶端+服務端)

寫在前面

  斷斷續續開發了幾個月的App終於告一段落,雖然它可能還很不完美,不過作為上手Android的第一個完整專案,確實從中學到了蠻多,所以開個系列記錄一下~本篇先從基本上每個App都會有的登入註冊講起,包含自動登入、記住密碼功能的實現=w=

實現:登入功能

思路

  整個登入功能的邏輯為:使用者提交賬號、密碼->判斷賬號密碼是否為空->選項處理(自動登入及記住密碼)->向服務端LoginServlet提交賬號密碼->服務端查詢資料庫判斷賬號是否存在->服務端查詢資料庫判斷賬號密碼是否匹配->返回resCode(登入成功/失敗)

客戶端

注: 客戶端使用Litepal進行資料庫的管理,關於Litepal的配置自行百度=。=

  1. 在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實現,這個網上也有很多資料,不多說

  1. 因為使用的是POST方式,所以以下步驟均在Servlet的doPost中進行;
  2. 獲取客戶端發來的請求,將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. 1-4步與登入功能裡的一致,不再贅述
  2. 核心邏輯:填充查詢引數->查詢賬號是否存在->若不存在執行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~