⼀、技术概述
使⽤短信验证码验证注册、登录和找回密码⼏乎是每⼀个APP、甚⾄是许多⽹页所需要⽀持的技术。对于我们学⽣完成⾮商⽤项⽬,往往需要⼀个免费提供短信验证码技术⽀持的SDK,⽽许多平台需要收费,很难找到适合的平台。
⼆、技术详述
1.在MobTech中获取App Key和App Secret
(1)⾸先进⼊MobTech官⽹:
(2)登陆过后,选择开发者服务,点击SMSSDK。
(3)点击开始使⽤
(4)点击创建应⽤,按要求填写信息后创建,并接⼊SMSSDK
(5)随后点击创建好的应⽤查看应⽤的App Key和App Secret
2.实现短信验证码验证功能
这⾥给出MobTech的开发⽂档链接:
(1)在项⽬中相应位置插⼊脚本和MobSDK插件和扩展 对应项⽬中插⼊脚本: 对应项⽬中插⼊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\ 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(\"确定\ //设置确定按钮 @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(\"取消\设置取消按钮 @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); }
下⾯给出完整前后端代码:前端:
android:layout_height=\"wrap_content\" android:text=\"⽤户名:\" android:textSize=\"20dp\" android:layout_weight=\"0\"/> android:id=\"@+id/reg_et_userName\" android:layout_weight=\"1\" android:layout_width=\"0dp\" android:layout_height=\"wrap_content\" /> android:layout_height=\"wrap_content\" android:text=\"⼿机号:\" android:textSize=\"20dp\" android:layout_weight=\"0\"/> android:id=\"@+id/reg_et_phonenum\" android:layout_weight=\"1\" android:layout_width=\"0dp\" android:layout_height=\"wrap_content\" /> android:layout_height=\"wrap_content\" android:text=\"密 码:\" android:textSize=\"20dp\" android:layout_weight=\"0\"/> android:layout_height=\"wrap_content\" /> android:layout_height=\"wrap_content\" android:hint=\"验证码\" />
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\ 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\ 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(\"确定\ //设置确定按钮 @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(\"取消\设置取消按钮 @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. 作者:2. 作者:3. 作业:4. 作者:
因篇幅问题不能全部显示,请点此查看更多更全内容