BiliBili视频地址:https://www.bilibili.com/video/BV1Ly4y1k7Po/
项目源代码GitHub地址(源码中有详细注释):https://github.com/littlecurl/LoginDemo
apk下载体验地址(2019年7月之后服务器到期就不能体验了):...明天我把它放服务器再更新
### 交代一些资源
我曾经写过一篇受大家欢迎的博客:
现在这篇文章是原先那篇的升级版,实现了前后端分离。
配套的后端服务器的笔记如下:
后端视频链接:【慕课网 · 免费课】龙虾三少《SpringBoot构建电商基础秒杀项目》
我就是跟着三少的视频学的注册登录的后端服务,墙裂推荐大家去听一听三少的课,讲的是相当的棒!听课的时候,记得把宿舍门关好,这样你就像我一样也是三少的关门弟子了!O(∩_∩)O哈哈~,三少还有一个升级课程,是收费的,条件允许的小伙伴可以去参观一下《Java电商秒杀系统深度优化 从容应对亿级流量挑战》
另外要想真正学会我这个登录注册的Demo推荐你看几本书:
-
《Spring 实战(第四版)》
-
《SpringBoot实战》
-
《看透SpringMVC源代码分析与实践》
-
《第一行代码(第二版)》
话说去年的这个时候刚刚接触Android,有幸在一开始就看到郭神的《第一行代码(第二版)》,然后我连着一个星期,每天最早都是晚上三点睡,白天八点醒,中间不睡觉的学习Android,因为我习惯在看书的时候关上宿舍门,所以我也算是郭神的关门弟子了,O(∩_∩)O哈哈~
我在开发这个项目之前,看过大量的书籍和博客,都没有适合的源码。于是我带着问题去问郭神,我问了两个问题,郭神答了两个问题,因此我很受鼓舞,于是,又是几乎通宵了一周,做出来现在这个Demo,我就是想做出来一个别人认为是奇葩,实际是因为我没钱租服务器,而迫不得已用自己电脑当服务器来学习的项目。
- Gradle依赖
- Android布局
- 利用正则表达式进行输入校验
- 自动登录的实现
- okhttp异步发送POST请求
- Gson解析okhttp回调响应的JSON数据
- 在子线程中更新UI,自动填写验证码
- 内网穿透,真机安装运行体验
如果把这个版本的登录注册掌握了,基本的前后端联合开发就可以入门了。
本来Android布局应该是在第一的位置来写的,但是Gradle依赖和打开项目的时间有关系,为了能节省大家打开一个项目的时间,我把Gradle放在第一位来讲了。
我用加粗的格式说一句来表示强调:当你从网上下载下来一个项目时,先别急着用AndroidStudio打开。
对应上面两个文件,在目录中显示如下
怎么改呢?很简单,就是你先打开一个你以前写过的项目,进去看看你电脑能轻松打开的项目的这两个版本号分别是多少,然后再用记事本打开从网上下载下来对应的文件,修改版本号为你本地已有的版本号,最后再打开项目。
之所以这样做是因为,如果本地没有对应的Gradle,下载的话很慢,而且一旦下载,很难暂停。不如花两分钟手动修改一下版本号节省时间。
当然,对于某些项目,有可能修改之后,导致无法编译。那样的话,没法,只能下载指定的Gradle版本了。
最后,有些人不在乎浪费时间,比如我,我经常忘记修改,导致我电脑现在几乎下遍了所有的Gradle版本,然后现在可以随意打开项目了。
如果学过JavaEE的同学应该都知道Maven,它可以帮我们管理依赖的jar包,实现自动导入。Gradle的作用和Maven一样,只不过Maven使用XML语言来实现的,Gradle是用Groovy语言来实现的。后者明显比前者简洁许多。Android中导入jar包依赖的地方如下,比如我这个登录注册就使用了两个第三方库okhttp和Gson,这些库都可以在Maven仓库:https://mvnrepository.com搜索到。别忘了修改后需要点一下右上角的Sync Now才能生效。
如果你的项目里,出现了大面积的红字,有可能就是Gradle没有工作或者工作到一半不小心被你中途暂停了。解决方法就是如下,重新让Gradle跑一下,编译项目。
有时候我们会遇到jar包冲突的bug,或者说就是想单纯的看看我那简单的一句implementations到底导入了多少个jar包,这也是为什么Groovy语言的Gradle和XML语言的Maven相比,前者就能如此简洁。这时候,就可以去AndroidStudio右侧边栏,点开Gradle工具条,然后找到app底下的AndroidDependencies,双击运行之后,就可以去控制台查看你这个项目到底依赖了多少个jar包。
学习Gradle,道阻且长,不过对于目前这个项目,我上面介绍的已然够用了。
Android布局开发有两种方式,Design和Text
我最近才发现有些同学因为没跟对老师,每次写布局的时候都是直接手敲代码,原因就是老师就这么教的。
其实那样并不好,一方面,虽说常用的控件就那么几个但每个控件的属性可是一大堆的,直接写代码容易漏掉某个属性;另一方面,通过Design可视化进行布局,能够获得及时反馈,而且一拖即成会感觉很爽。
所以推荐大家首先是要Design方式拖拉出来界面,如果需要微调的话,或者拖拉不能实现了,再使用代码进行改动。
因为这个属于操作性比较强的,如果有机会,我会录个视频分享给大家,这里我就给大家贴一张图吧
这次登录界面我用的是ConstraintLayout,以实现不同分辨率手机的自适应。注册界面因为需要填写的有点多,用的是ScrollView+LinearLayout实现了滚动布局。
官方文档不容易看懂,最好的学习方法就是上手实践一下。
本来这个小标题应该是:观察者模式校验前端输入数据
我本想根据AndroidStudio自带的LoginActivity写写呢MVVM模式的,但是研究了一天没搞懂。
所以下面按我实现的写了。
在登录界面,我们需要输入账号和密码
本来我想实现账号既可以是用户名也可以是手机号还可以是邮箱,但是考虑到有点麻烦,我就只实现了手机号验证,而且是非严格的正则校验,代码如下:
// 校验账号不能为空且必须是中国大陆手机号(宽松模式匹配)
private boolean isTelphoneValid(String account) {
if (account == null) {
return false;
}
// 首位为1, 第二位为3-9, 剩下九位为 0-9, 共11位数字
String pattern = "^[1]([3-9])[0-9]{9}$";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(account);
return m.matches();
}
密码要求不少于6位
// 校验密码不少于6位
private boolean isPasswordValid(String password) {
return password != null && password.trim().length() > 5;
}
而且我实现了EditText的setOnFocusChangeListener方法,根据输入数据的类型调用对应的方法进行校验。这样就可以在输入框改变焦点的时候进行校验,判断输入格式是否正确,如果错误,则会在右侧出现错误提示。
代码如下:
private void setOnFocusChangeErrMsg(EditText editText,String inputType, String errMsg){
editText.setOnFocusChangeListener(
new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
String inputStr = editText.getText().toString();
if (!hasFocus){
if(inputType == "phone"){
if (isTelphoneValid(inputStr)){
editText.setError(null);
}else {
editText.setError(errMsg);
}
}
if (inputType == "password"){
if (isPasswordValid(inputStr)){
editText.setError(null);
}else {
editText.setError(errMsg);
}
}
}
}
}
);
}
实现效果如下:
自动登录的原理就是,用户第一登录时会有一个token为null,如果登录成功,那么就在本地存一个token,关掉应用(不是退出登录)再次打开的时候,判断token是否正确,如果正确就自动登录。
这里本地存放token使用了Android自带的SharedPreferences。
在onCreate里面获取SharedPreferences对象,这里的语法有点奇怪,我什么都没有呢就进行get,但是Android 就是这么规定的。下面这句代码相当于声明了一个小型数据库,名为login_info,模式是私有模式,应该是会对数据进行加密,这种小型数据库只能存放k,v键值对。
sp = getSharedPreferences("login_info",MODE_PRIVATE);
往sp内存放数据需要先获取一个SharedPreferences.Editor对象editor,然后执行下面这样的代码
editor = sp.edit();
editor.putString("token","token_value");
editor.putString("telphone",telphone);
editor.commit(); // 或者 editor.apply();
这次大动干戈的将前后端进行分离,主要就是依靠像okhttp这样的框架来实现的。
因为请求URL属于耗时操作,所以开一个线程,避免耗时操作在子线程中进行。
okhttp异步POST请求,总共5步,如下代码所示:
private void asyncValidate(final String account, final String password){
new Thread( new Runnable() {
@Override
public void run() {
// 1、初始化okhttpClient对象
OkHttpClient okHttpClient = new OkHttpClient();
// 2、构建请求体requestBody
RequestBody requestBody = new FormBody.Builder()
.add("telphone", account)
.add("password", password)
.build();
// 3、发送请求,因为要传密码,所以用POST方式
Request request = new Request.Builder()
.url(loginURL)
.post(requestBody)
.build();
// 4、使用okhttpClient对象获取请求的回调方法,enqueue()方法代表异步执行
okHttpClient.newCall(request).enqueue( new Callback() {
// 5、重写两个回调方法
@Override
public void onFailure(Call call, IOException e) {
// ...
}
@Override
public void onResponse(Call call, Response response)
// ...
}
});
}
}).start();
}
这里,我用的这个版本okhttp要求API level 21+ 但夜神模拟器API是19
后端Controller层的方法上都加了@ResponseBody的注解,功能就是把返回值封装成JSON格式字符串。
所以,okhttp回调接收的是一个JSON格式的字符串。这个字符串被封装在response对象的body里。
我们要做的就三步
// 1、获取response对象的body里的字符串
String responseBodyStr = response.body().string();
// 2、将其解析为JSON对象
JsonObject responseBodyJSONObject = (JsonObject) new JsonParser().parse(responseBodyStr);
// 3、使用JSON对象获取具体值
String status = responseBodyJSONObject.get("status").getAsString();
这样就能解析形如下面的JSON数据
{
"status" : "success",
"data" : null
}
在子线程中更新UI的方式有好多,大家可以自行百度,大致有四种,我综合考虑了一下简洁、易懂与适用性,选择了runOnUiThread这种方式
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
}
});
在子线程中更新UI,将后端返回的验证码解析并填充到对应输入框
/* 在子线程中更新UI ,实现自动填充验证码 */
private void setTextInThread(EditText editText,String otpCode){
runOnUiThread(new Runnable() {
@Override
public void run() {
editText.setText(otpCode);
}
});
}
电脑模拟器运行的IP说明:
如果使用AndroidStudio自带或者Genymotion模拟器,可以使用10.0.0.2来指明本机IP地址。Android里127.0.0.1和localhost值得是模拟器的地址,而非你电脑的地址。
如果使用夜神、雷电等模拟器需要打开cmd然后执行ipconfig命令,查看并使用本地电脑的IP地址。
真机运行的IP说明:
两种方式,一种就是自己租服务器,把后台代码放到远程服务器里跑,这样就有公网IP了。
另一种就是还是使用自己电脑,使用花生壳等工具进行内网穿透,这样也可以拥有公网IP。
有机会的话,我会录个视频,来专门说说这件事。
- 防止疯狂登录不存在的用户
- 防止疯狂注册用户
- 实现分布式Session
- ......