转载本专栏文章,请注明出处,尊重原创 。文章博客地址:道龙的博客
很多应用刚打开的时候,让我们输入手机号,通过短信验证码来登录该应用。那么,这个场景是怎么实现的呢?其实是很多开放平台提供了短信验证功能的接口供我们使用,我们只需要关心客户端,无需关心服务端的业务逻辑,而且更加稳定安全。今天这篇文章就通过Jpush官方的JSMS模块,来实现短信验证登录功能。虽然,Jpush对于极光推送是非常有名出色的,短信验证也是它官网的新内容,不过,多种开放平台使用方式大同小异,而且综合对比,Jpush官网的短信验证使用起来很简单!既然人家给了接口,就来感受一下它的短信验证的实现吧。
首先咱们了解一下短信验证的请求流程:
1.以用户提供的手机号码作为参数,请求连接到JSMS的服务器
2.JSMS服务器校验号码格式成功,请求短信服务商发送短信验证码到该手机号
3.手机终端接收到验证码,用户输入验证码
4.验证码传入应用后台服务端,服务端通过JSMS接口验证,再把验证结果传回手机端
JPush官方地址:http://docs.jiguang.cn/jsms/resources/
进入官网,JSMS模块,官网是这么给出的
短信验证码概述
功能
极光面向开发者提供短信验证码服务,主要包括:
- SDK 短信验证功能,满足 App 短信验证需求。
- REST API 短信验证,主要满足网站和其他终端访问需求。
主要场景
- 用户注册;
- 登录验证;
- 关键信息修改;
- 支付确认;
- 人员身份有效性确认。
试用步骤
- 注册成为极光开发者。
- 通过极光后台创建 APP 得到 AppKey(如果之前创建过可以通用)。
官网概述的确言简意赅,我觉得使用图文告知您,可能更贴切一些。看下图:
步骤一 (获取appkey)
1.在极光官网平台上新建短信应用(根据要求包名和应用名称填写好)
2.获取得到Jpush_AppKey。
步骤二 (导入第三方库和so文件)
1.用Android studio打开应用
2.切换应用状态Android为Project状态
3.在libs文件夹下把对应的jar文件拷入 然后as libraries
步骤三 (填写配置文件)
1.配置应用权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
2.配置meta (appkey 从平台上新建的对应应用中来)
<meta-data android:name="JPUSH_APPKEY" android:value="You JPUSH_APPKEY"/>
步骤四(初始化以及调用)
1.新建Application初始化
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
SMSSDK.getInstance().initSdk(this);
SMSSDK.getInstance().setIntervalTime(30*1000);
}
}
接口的意思是:
SMSSDK.getInstance().init(this);
该接口为初始化接口,主要是检测一些配置信息,如果配置错误将会初始化失败,将会打印错误日志。调用其它接口前必须先调用该接口,仅且仅需调用一次,建议在 Application 或初始 Activity 中调用。我这里自定义了一个Applicatiion,这样的话会排除一些可能存在的问题。
SMSSDK.getInstance().setIntervalTime(60000);
设置前后两次获取验证码的时间间隔,默认 30 秒。参数是一个时间间隔,单位是毫秒(ms)
2.在activity中使用
public class CommonSMSActivity extends Activity { private EditText editText;
private Button btn; private EditText codeText;
private Button signBtn; private TimerTask timerTask;
private Timer timer;
private int timess;
private ProgressDialog progressDialog; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash); progressDialog = new ProgressDialog(this);
editText = (EditText) findViewById(R.id.edt_code);
codeText = (EditText) findViewById(R.id.edt_write_code);
btn = (Button) findViewById(R.id.btn);
signBtn = (Button) findViewById(R.id.btn_sign); // 获取验证码
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String phoneNum = editText.getText().toString();
if(TextUtils.isEmpty(phoneNum)){
Toast.makeText(CommonSMSActivity.this, "请输入手机号码", Toast.LENGTH_SHORT).show();
Log.e("执行", "请输入手机号码!");
return;
}
btn.setClickable(false);
// 开始倒计时
startTimer();
SMSSDK.getInstance().getSmsCodeAsyn(phoneNum, 1+"", new SmscodeListener() {
@Override
public void getCodeSuccess(final String uuid) {
Toast.makeText(CommonSMSActivity.this,uuid,Toast.LENGTH_SHORT).show();
Log.e("执行", "获取验证码成功!");
} @Override
public void getCodeFail(int errCode, final String errmsg) {
//失败后停止计时
stopTimer();
Toast.makeText(CommonSMSActivity.this,errmsg,Toast.LENGTH_SHORT).show();
Log.e("执行", "获取验证码失败,请重新获取!");
}
}); }
});
//开始验证
signBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) { String code = codeText.getText().toString();
String phoneNum = editText.getText().toString(); if(TextUtils.isEmpty(code)){
Toast.makeText(CommonSMSActivity.this,"请输入验证码",Toast.LENGTH_SHORT).show();
Log.e("执行", "验证前请确保您已经输入验证码了!");
return;
}
if(TextUtils.isEmpty(phoneNum)){
Toast.makeText(CommonSMSActivity.this,"请输入手机号码",Toast.LENGTH_SHORT).show();
Log.e("执行", "验证前请确保您有输入手机号");
return;
}
progressDialog.setTitle("正在验证...");
progressDialog.show();
SMSSDK.getInstance().checkSmsCodeAsyn(phoneNum, code, new SmscheckListener() {
@Override
public void checkCodeSuccess(final String code) {
if(progressDialog!=null&&progressDialog.isShowing()){
progressDialog.dismiss();
}
Toast.makeText(CommonSMSActivity.this,code,Toast.LENGTH_SHORT).show();
Log.e("执行", "验证成功!");
} @Override
public void checkCodeFail(int errCode, final String errmsg) {
if(progressDialog!=null&&progressDialog.isShowing()){
progressDialog.dismiss();
}
Toast.makeText(CommonSMSActivity.this,errmsg,Toast.LENGTH_SHORT).show();
Log.e("执行", "验证失败!");
}
});
}
});
}
//开始计时
private void startTimer(){
timess = (int) (SMSSDK.getInstance().getIntervalTime()/1000);//获取短信时长
btn.setText(timess+"s");
if(timerTask==null){
timerTask = new TimerTask() {//启动task任务
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
timess--;
if(timess<=0){
stopTimer();//停止倒计时
return;
}
btn.setText(timess+"s");
}
});
}
};
}
if(timer==null){
timer = new Timer();
}
timer.schedule(timerTask, 1000, 1000);//每隔1s执行一次任务
}
private void stopTimer(){//停止计时
if(timer!=null){
timer.cancel();
timer=null;
}
if(timerTask!=null){
timerTask.cancel();
timerTask=null;
}
btn.setText("重新获取");
btn.setClickable(true);
}
}
这里的代码稍微有点长,核心的API说明一下:
SMSSDK 类:对外的类,该类为单例,调用该类的方法都需要获取该类的唯一实例,获得方法为 SMSSDK.getInstance()。
SmscodeListener:获取验证码的回调接口,在调用 SMSSDK 的 getSmsCode 时需要传入接口实例。
SmscheckListener:检查验证码的回调接口,在调用 SMSSDK 的 checkSmsCode 时需要传入接口实例。
SMSSDK.init(Context context)
接口说明
该接口为初始化接口,主要是检测一些配置信息,如果配置错误将会初始化失败,将会打印错误日志。调用其它接口前必须先调用该接口,仅且仅需调用一次,建议在 Application 或初始 Activity 中调用。
调用示例
SMSSDK.getInstance().init(this);
SMSSDK.getSmsCode(String phone, String tempId, SmscodeListener listener)
接口说明
获取验证码。
该接口是在非 UI 线程回调,需要在 UI 线程回调可调用 SMSSDK.getSmsCodeAsyn()。
参数说明
- phone:手机号码
- tempId:短信模板
- listener:回调接口
调用示例
SMSSDK.getInstance().getSmsCodeAsyn("159xxxxxxxx", "1", new SmscodeListener() {
@Override
public void getCodeSuccess(final String uuid) {
// 获取验证码成功,uuid 为此次获取的唯一标识码。
}
@Override
public void getCodeFail(int errCode, final String errMsg) {
// 获取验证码失败 errCode 为错误码,详情请见文档后面的错误码表;errMsg 为错误描述。
}
});
SMSSDK.checkSmsCode(String phone, String code, SmscheckListener listener)
接口说明
验证码验证接口。
该接口是在非 UI 线程回调,需要在 UI 线程回调可调用 SMSSDK.checkSmsCodeAsyn()。
参数说明
- phone:手机号码
- code:短信验证码
- listener:回调接口
调用示例
SMSSDK.getInstance().checkSmsCodeAsyn("159xxxxxxxx", "123456", new SmscheckListener() {
@Override
public void checkCodeSuccess(final String code) {
// 验证码验证成功,code 为验证码信息。
}
@Override
public void checkCodeFail(int errCode, final String errMsg) {
// 验证码验证失败, errCode 为错误码,详情请见文档后面的错误码表;errMsg 为错误描述。
}
});
SMSSDK.setIntervalTime(long intervalTime)
接口说明
设置前后两次获取验证码的时间间隔,默认 30 秒。
参数说明
- intervalTime:时间间隔,单位是毫秒(ms)。
调用示例
SMSSDK.getInstance().setIntervalTime(60000); // 设置间隔时间为 60 秒
SMSSDK.getIntervalTime()
接口说明
获取当前设置的时间间隔。
返回值
- long:单位为毫秒(ms)。
调用示例
SMSSDK.getInstance().getIntervalTime();
我们平时使用短信验证太频繁了,相信上边代码对你来说是soeasy;只不过是做了两个点击事件,然后获取短信以及验证短信交流jsms去处理了,我们并不需要实现业务逻辑。我们额外再做的,也就是启动倒计时任务罢了。
最后再贴上error可能的情况表:
错误码描述
错误码 | 错误码描述 | 备注 |
---|---|---|
3001 | 请求超时 | |
3002 | 无效的手机号码 | |
4001 | body 为空 | |
4002 | 无效的 AppKey | |
4003 | 无效的来源 | |
4004 | body 解密失败 | |
4005 | aes key 解密失败 | |
4006 | 时间戳转化失败 | |
4007 | body 格式不正确 | |
4008 | 无效时间戳 | |
4009 | 没有短信验证权限 | |
4011 | 发送超频 | 单个设备或同一手机号每天获取验证码次数(默认10次) |
4013 | 模板不存在 | |
4014 | extra 为空 | |
4015 | 验证码不正确 | |
4016 | 没有余额 | |
4017 | 验证码超时 | |
4018 | 重复验证 | |
2993 | 验证码校验失败 | 短信已下发但获取 uuid 异常 |
2994 | 本地数据有误 | |
2995 | 数据解析错误 | |
2996 | 两次请求不超过 1 分钟 | 本地校验 |
2997 | 未获取验证码 | |
2998 | 网络错误 | 没有网络等 |
2999 | 其它错误 |
很多人以为已经完成了,其实不然!翻阅了很多有关jsms相关的博客,大家仅仅是给出了怎么使用,罗列了一堆代码。并没有运行截图。也未给出后面遇到问题的解释。不卖关子了,我们发现,上面的代码没有任何错误,运行也能成功。但是,运行发现一直提示no money的错误,并不能接收短信。这是因为我们仅仅是在开放平台集成了人家的功能,并没有作为真正的开发者使用这个功能。那么有什么办法吗?一张图说明一切吧:
如果你是刚刚创建的账号,那么点击这个位置跟我的不一样,最下端会提醒你 “点击联系服务” 大致这个意思。那么你就填写完整个人信息并提交,成功后,就会获得每日20条免费短信的功能。其实,在公司实际开发中不会只给你20条的,到底后边怎么实现,到公司跟你老板商量吧!
最后,我们再来运行一下,看看效果:
我使用的模拟器,短信下发到了我的手机。为了不暴露自己的手机号,就截了下方的验证部分。可以肯定的是,验证登录功能是实现完毕了~
刚才连续发了好几条验证信息。再看看后台数据吧:
JSMS基本使用就介绍这么多,更多功能请参考官方文档。
参考文章:极光推送(短信验证)
喜欢我的朋友,可以关注我的博客。
也可以打开微信搜索公众号 Android程序员开发指南 或者手机扫描下方二维码 在公众号阅读更多Android文章。
微信公众号图片: