一、技術概述
使用短信驗證碼驗證注冊、登錄和找回密碼幾乎是每一個APP、甚至是許多網頁所需要支持的技術。對於我們學生完成非商用項目,往往需要一個免費提供短信驗證碼技術支持的SDK,而許多平台需要收費,很難找到適合的平台。
二、技術詳述
1.在MobTech中獲取App Key和App Secret
(1)首先進入MobTech官網:https://www.mob.com/
(2)登陸過后,選擇開發者服務,點擊SMSSDK。

(3)點擊開始使用

(4)點擊創建應用,按要求填寫信息后創建,並接入SMSSDK

(5)隨后點擊創建好的應用查看應用的App Key和App Secret

2.實現短信驗證碼驗證功能
這里給出MobTech的開發文檔鏈接:https://www.mob.com/wiki/detailed?wiki=SMSSDK_for_Android_kuaisujicheng&id=23
(1)在項目中相應位置插入腳本和MobSDK插件和擴展



腳本代碼:
buildscript {
repositories {
jcenter()
}
dependencies {
// 注冊MobSDK
classpath "com.mob.sdk:MobSDK:2018.0319.1724"
}
}
MobSDK插件和擴展:
apply plugin: 'com.mob.sdk'
MobSDK {
appKey "替換為mob官方申請的appkey"
appSecret "替換為mob官方申請的appkey對應的appSecret"
SMSSDK {}
}
(2)功能實現
i)短信驗證按鈕60s計時
Handler hd = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == CODE_REPEAT) {
btn_check.setEnabled(true);
btn_sure.setEnabled(true);
tm.cancel();//取消任務
tt.cancel();//取消任務
TIME = 60;//時間重置
btn_check.setText("重新發送驗證碼");
}else {
btn_check.setText(TIME + "重新發送驗證碼");
}
}
};
ii)回調
EventHandler eh=new EventHandler(){
@Override
public void afterEvent(int event, int result, Object data) {
if (result == SMSSDK.RESULT_COMPLETE) {
if (event == SMSSDK.EVENT_SUBMIT_VERIFICATION_CODE) {
phoneNum = et_phonenum.getText().toString();
password = et_password.getText().toString();
userName = et_userName.getText().toString();
//
//這里將數據userName password phoneNum發送到數據庫
//
//
Intent intent = new Intent(RegisterActivity.this,MainActivity.class);
intent.putExtra("phone",phoneNum);
startActivity(intent);
toast("驗證成功");
}else if (event == SMSSDK.EVENT_GET_VERIFICATION_CODE){ //獲取驗證碼成功
toast("獲取驗證碼成功");
}else if (event ==SMSSDK.EVENT_GET_SUPPORTED_COUNTRIES){//如果你調用了獲取國家區號類表會在這里回調
//返回支持發送驗證碼的國家列表
}
}else{//錯誤等在這里(包括驗證失敗)
//錯誤碼請參照http://wiki.mob.com/android-api-錯誤碼參考/這里我就不再繼續寫了
toast("驗證碼不匹配,請重新輸入驗證碼");
}
}
};
iii)彈窗確認下發
private void alterWarning() {
//構造器
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("提示"); //設置標題
builder.setMessage("我們將要發送到" + phone + "驗證"); //設置內容
builder.setIcon(R.mipmap.ic_launcher);//設置圖標,圖片id即可
builder.setPositiveButton("確定", new DialogInterface.OnClickListener() {
//設置確定按鈕
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss(); //關閉dialog
//通過sdk發送短信驗證(請求獲取短信驗證碼,在監聽(eh)中返回)
SMSSDK.getVerificationCode(country, phone);
//做倒計時操作
Toast.makeText(RegisterActivity.this, "已發送" + which, Toast.LENGTH_SHORT).show();
btn_check.setEnabled(false);
btn_sure.setEnabled(true);
tm = new Timer();
tt = new TimerTask() {
@Override
public void run() {
hd.sendEmptyMessage(TIME--);
}
};
tm.schedule(tt,0,1000);
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { //設置取消按鈕
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
Toast.makeText(RegisterActivity.this, "已取消" + which, Toast.LENGTH_SHORT).show();
}
});
//參數都設置完成了,創建並顯示出來
builder.create().show();
}
iiii)銷毀短信注冊
protected void onDestroy() {
super.onDestroy();
// 注銷回調接口registerEventHandler必須和unregisterEventHandler配套使用,否則可能造成內存泄漏。
SMSSDK.unregisterEventHandler(eh);
}
下面給出完整前后端代碼:
前端:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="50dp"
android:gravity="center"
tools:context="com.example.messagetest.RegisterActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="70dp"
android:layout_height="wrap_content"
android:text="用戶名:"
android:textSize="20dp"
android:layout_weight="0"/>
<EditText
android:inputType="phone"
android:id="@+id/reg_et_userName"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="70dp"
android:layout_height="wrap_content"
android:text="手機號:"
android:textSize="20dp"
android:layout_weight="0"/>
<EditText
android:inputType="phone"
android:id="@+id/reg_et_phonenum"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="70dp"
android:layout_height="wrap_content"
android:text="密 碼:"
android:textSize="20dp"
android:layout_weight="0"/>
<EditText
android:inputType="phone"
android:id="@+id/reg_et_key"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/reg_et_checkecode"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="驗證碼" />
<Button
android:background="@color/colorPrimary"
android:id="@+id/reg_btn_check"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="獲取驗證碼" />
</LinearLayout>
<Button
android:background="@color/colorAccent"
android:id="@+id/reg_btn_register"
android:layout_marginTop="20dp"
android:textColor="#131313"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="注冊" />
</LinearLayout>
后端:
package com.example.messagetest;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import com.mob.MobSDK;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import cn.smssdk.EventHandler;
import cn.smssdk.SMSSDK;
public class RegisterActivity extends AppCompatActivity{
private TimerTask tt;
private Timer tm;
private EditText et_phonenum;
private EditText et_userName;
private Button btn_check;
private EditText et_checkecode;
private Button btn_sure;
private EditText et_password;
private String password;
private String userName;
private String phoneNum;
private int TIME = 60;//倒計時60s這里應該多設置些因為mob后台需要60s,我們前端會有差異的建議設置90,100或者120
public String country="86";//這是中國區號,如果需要其他國家列表,可以使用getSupportedCountries();獲得國家區號
private String phone;
private static final int CODE_REPEAT = 1; //重新發送
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
MobSDK.init(this, "24793dde94dc6", "6e636da9b16e5bf8d5fae19ca30ea6ac");
SMSSDK.registerEventHandler(eh); //注冊短信回調(記得銷毀,避免泄露內存)
et_password =(EditText)findViewById(R.id.reg_et_key) ;
et_phonenum = (EditText) findViewById(R.id.reg_et_phonenum);
et_userName = (EditText) findViewById(R.id.reg_et_userName);
btn_check = (Button) findViewById(R.id.reg_btn_check);
et_checkecode = (EditText) findViewById(R.id.reg_et_checkecode);
btn_sure = (Button) findViewById(R.id.reg_btn_register);
et_password=(EditText) findViewById(R.id.reg_et_key);
btn_check.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
phone = et_phonenum.getText().toString().trim().replaceAll("/s","");
if (!TextUtils.isEmpty(phone)) {
//定義需要匹配的正則表達式的規則
String REGEX_MOBILE_SIMPLE = "[1][358]\\d{9}";
//把正則表達式的規則編譯成模板
Pattern pattern = Pattern.compile(REGEX_MOBILE_SIMPLE);
//把需要匹配的字符給模板匹配,獲得匹配器
Matcher matcher = pattern.matcher(phone);
// 通過匹配器查找是否有該字符,不可重復調用重復調用matcher.find()
if (matcher.find()) {//匹配手機號是否存在
alterWarning();
} else {
toast("手機號格式錯誤");
}
} else {
toast("請先輸入手機號");
}
}
});
btn_sure.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//獲得用戶輸入的驗證碼
String name = et_userName.getText().toString().replaceAll("/s","");
String code = et_checkecode.getText().toString().replaceAll("/s","");
String pn = et_phonenum.getText().toString().trim().replaceAll("/s","");
String pw = et_password.getText().toString().replaceAll("/s","");
if (TextUtils.isEmpty(name)) {//判斷用戶名是否為空
toast("請輸入用戶名");
}
else if (!TextUtils.isEmpty(name)) {//用戶名非空的情況下判斷唯一性
/**
*
*
* 判斷填寫的用戶名(這里的變量是name)是否是唯一的
*
*
*/
}
else if (TextUtils.isEmpty(pn)) {//判斷手機號是否為空
toast("請輸入手機號");
}
else if (!TextUtils.isEmpty(pn)) {//手機號非空的情況下判斷唯一性
/**
*
*
*
* 判斷填寫的手機號(這里的變量是pn)是否是唯一的
*
*
*/
}
else if (TextUtils.isEmpty(pw)) {//判斷密碼是否為空
toast("請輸入密碼");
}
else if (!TextUtils.isEmpty(code)) {//判斷驗證碼是否為空
//驗證
SMSSDK.submitVerificationCode( country, phone, code);
}else{//如果用戶輸入的內容為空,提醒用戶
toast("請輸入驗證碼后再提交");
}
}
});
}
Handler hd = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == CODE_REPEAT) {
btn_check.setEnabled(true);
btn_sure.setEnabled(true);
tm.cancel();//取消任務
tt.cancel();//取消任務
TIME = 60;//時間重置
btn_check.setText("重新發送驗證碼");
}else {
btn_check.setText(TIME + "重新發送驗證碼");
}
}
};
//回調
EventHandler eh=new EventHandler(){
@Override
public void afterEvent(int event, int result, Object data) {
if (result == SMSSDK.RESULT_COMPLETE) {
if (event == SMSSDK.EVENT_SUBMIT_VERIFICATION_CODE) {
phoneNum = et_phonenum.getText().toString();
password = et_password.getText().toString();
userName = et_userName.getText().toString();
//
//這里將數據userName password phoneNum發送到數據庫
//
//
Intent intent = new Intent(RegisterActivity.this,MainActivity.class);
intent.putExtra("phone",phoneNum);
startActivity(intent);
toast("驗證成功");
}else if (event == SMSSDK.EVENT_GET_VERIFICATION_CODE){ //獲取驗證碼成功
toast("獲取驗證碼成功");
}else if (event ==SMSSDK.EVENT_GET_SUPPORTED_COUNTRIES){//如果你調用了獲取國家區號類表會在這里回調
//返回支持發送驗證碼的國家列表
}
}else{//錯誤等在這里(包括驗證失敗)
//錯誤碼請參照http://wiki.mob.com/android-api-錯誤碼參考/這里我就不再繼續寫了
toast("驗證碼不匹配,請重新輸入驗證碼");
}
}
};
//吐司的一個小方法
private void toast(final String str) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(RegisterActivity.this, str, Toast.LENGTH_SHORT).show();
}
});
}
//彈窗確認下發
private void alterWarning() {
//構造器
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("提示"); //設置標題
builder.setMessage("我們將要發送到" + phone + "驗證"); //設置內容
builder.setIcon(R.mipmap.ic_launcher);//設置圖標,圖片id即可
builder.setPositiveButton("確定", new DialogInterface.OnClickListener() {
//設置確定按鈕
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss(); //關閉dialog
//通過sdk發送短信驗證(請求獲取短信驗證碼,在監聽(eh)中返回)
SMSSDK.getVerificationCode(country, phone);
//做倒計時操作
Toast.makeText(RegisterActivity.this, "已發送" + which, Toast.LENGTH_SHORT).show();
btn_check.setEnabled(false);
btn_sure.setEnabled(true);
tm = new Timer();
tt = new TimerTask() {
@Override
public void run() {
hd.sendEmptyMessage(TIME--);
}
};
tm.schedule(tt,0,1000);
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { //設置取消按鈕
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
Toast.makeText(RegisterActivity.this, "已取消" + which, Toast.LENGTH_SHORT).show();
}
});
//參數都設置完成了,創建並顯示出來
builder.create().show();
}
//銷毀短信注冊
@Override
protected void onDestroy() {
super.onDestroy();
// 注銷回調接口registerEventHandler必須和unregisterEventHandler配套使用,否則可能造成內存泄漏。
SMSSDK.unregisterEventHandler(eh);
}
}
短信驗證碼驗證其他功能和接口可以參照實現短信驗證碼功能開頭給的文檔
三、遇到的問題和解決過程
(1)
在參考文檔時,短信驗證碼錯誤的處理有圖下畫框這一句
在我實際開發時,輸入錯誤的短信驗證碼會直接跳轉到登錄頁面,並且沒有任何提示,會讓用戶誤以為驗證碼通過。在排查了過后覺得是驗證碼錯誤這個else分支里的問題,最后我改成了使用toast告訴用戶驗證碼錯誤,如下圖所示:
(2)
在開發完成過后,我們小組測試安卓APP時,發現收不到短信的情況。最開始以為是出現了BUG,但是經過我排查過后發現不應該有BUG,感到很奇怪。組長突然提醒我,說我之前好像提到過每天接收短信是有上限的,仔細閱讀文檔過后找到了收不到短信的原因。
需要注意的是,每天提供的免費短信只有20條,每個手機最多只能收10條等,若達到發送的短信上限,將不會再收到短信(具體請閱讀文檔,下面給出部分截圖)
其余遇見問題我認為大部分參考文檔可以解決。
四、總結
(1)使用短信驗證碼驗證注冊、登錄和找回密碼幾乎是每一個APP、甚至是許多網頁所需要支持的技術。對於我們學生完成非商用項目,往往需要一個免費提供短信驗證碼技術支持的SDK,而許多平台需要收費,我在尋找免費的平台上花了許多時間,最終發現MobTech是一個很適合學生在課程上開發的項目使用,提供的SDK和接口都很方便實用,且每天提供20條免費的短信驗證碼。
(2)在使用SDK前,建議先仔細閱讀文檔,上面有完整的產品和接口等的介紹,其實在開發中遇到的大部分問題都可以在文檔中找到解決方案。
(3)由於我在使用中遇到了直接跳轉到登錄頁面的問題,后面改成了toast直接告訴驗證碼錯誤更合適。
五、參考
1.MobTech文檔中心\SMSSDK 作者:MobTech
2.解決mob網站短信驗證SDK更新后,android studio下的mob短信驗證接入問題 作者:haibowen
3.Android開發實現短信驗證碼功能(總結) 作業:lpCrazyBoy
4.Android實現短信驗證功能(功能的使用) 作者:陳旭金-小金子