目录
实验目的
通过使用Android API进行系统注册模块的开发,包括前台的Android原生app以后后台服务模块的开发,要求后台使用JavaEE框架实现。
实验对应知识点
Android的应用编程框架以及Android API的使用,JavaEE框架的应用开发。
实验前任务
Java编程、Android API的编程、JavaEE中JSP、Servlet的编程。
实验要求及步骤
一、Android app的开发
对于Android app,至少需要有如下界面:
(1)登录界面
包含用户名、密码的文字标识以及相应的输入栏,登录以及注册的按钮。当输入用户名以及密码后,点击登录按钮,则交数据提交至后台进行验证,如通过验证则跳转至欢迎界面,否则跳转回登录界面,并提示用户的验证错误原因;当用户点击注册按钮则跳转至注册界面。
(2)注册界面
包含用户名、密码以及确认密码的文字标识以及相应的输入栏,提交以及取消按钮。当输入相关信息后,首先验证输入的信息是否符合要求(用户名至少5位,最多10位,以英文字母开头,只允许包含英文字母、数字以及_,同时必须至少有一个大写英文字母;密码为6-12位,只允许包含英文字母、数字和_,同时要求确认密码必须与密码一致),如不符合要求则在界面内提示错误,只有符合要求才提交给后台进行注册操作。如注册成功则跳转至欢迎界面,否则跳转回注册界面并提示用户的注册错误原因;当用户点击取消按钮则返回登录界面。
(3)欢迎界面
显示对用户的欢迎信息,其中必须包括用户的登录名。
在Android app中的Activity调用后台服务的关键代码:
public void onClick(View v) {
// TODO Auto-generated method stub
String uriAPI = "http://192.168.100.10:8080/testAndroidJSP/index.jsp";
//String uriAPI = getString(R.string.server);
// 将属性文件流装载到Properties对象中
System.out.println("url : " + uriAPI);
System.out.println("连接获取数据...");
/* 建立HTTP Get联机 */
HttpGet httpRequest = new HttpGet(uriAPI);
try
{
/* 发到HTTP request */
HttpResponse httpResponse = new DefaultHttpClient().execute(httpRequest);
//System.out.println(httpResponse.getStatusLine().getStatusCode());
/* 若状态码为200 ok */
if (httpResponse.getStatusLine().getStatusCode() == 200) {
/* 取响应字符串 */
String strResult = EntityUtils.toString(httpResponse
.getEntity());
//System.out.println(strResult);
/*try {
SAXParserFactory factory = SAXParserFactory.newInstance();
XMLReader reader = factory.newSAXParser().getXMLReader();
reader.setContentHandler(new ContentHandler());
reader.parse(new InputSource(new StringReader(strResult)));
}
catch (Exception e) {
e.printStackTrace();
}*/
System.out.println("成功获得数据");
myText.setText(strResult.toString());
}
else {
myText.setText("Error Response: "
+ httpResponse.getStatusLine().toString());
}
}……
当获取到后台响应的字符串后,再在app内进行处理并显示相关信息。
二、后台服务的开发
对于Android app调用的后台服务而言,要求使用JavaEE中的JSP或者Servlet进行编写,响应信息可以使用XML或JSON等方式进行封装,封装的信息由Android app再进行解析处理。
对于注册信息的存储,要求使用Mysql数据库进行存放,其中用户名作为表的主键进行存储。
前言
终于有一次实验拿A+了嘻嘻:)
本次实验笔者在实验二的基础上,修改好服务端代码(JavaWeb项目)后,打成war包部署到阿里云上,然后在室友的电脑上共同合作弄出了客户端代码(AndroidStudio项目)。
服务端和客户端的交互利用的是Volley+JSONObject。
短信验证码利用的是mob平台。
具体实现
传统的Web应用是HTML/JSP+Java EE
,安卓应用是Android+Java EE
。比起实验二,其实就是将JSP部分替换成了Android。
开发环境
服务端:IntelliJ Idea 2019.1
客户端:Android Studio 3.5.2
服务器:阿里云轻量级
JDK:1.8
Tomcat:8.5
Mysql:8.0
SDK:Android 5.0
效果展示
一、登录界面
![](https://i-blog.csdnimg.cn/blog_migrate/d2d7b33f3f3c2874937a5316f3c9d18f.jpeg)
二、注册界面
三、忘记密码界面
四、修改密码界面
![](https://i-blog.csdnimg.cn/blog_migrate/0057d4854782bbc67f1631a56992d183.jpeg)
五、欢迎界面
服务端
![](https://i-blog.csdnimg.cn/blog_migrate/9a4537e9245f6c61fd44484e955904dd.png)
思路上大体是和实验二差不多的,这里主要是添加及修改了servlet
和相关的service
。
- LoginServlet 接收参数username和password,验证users表中是否存在该用户,若存在,验证密码是否正确。
- WelcomeServlet 接收参数username,查找person表中相关信息并返回。
- ForgetPasswordServlet接收参数username和telenum,验证telenum是否是该user的telenum(在此后台验证之前,客户端有实现短信验证码功能)。
- ChangePasswordServlet 接收参数username和password,将users表中该username对应的user的密码修改成password。
- RegisterPasswordServlet 接收参数username,name,password,age,telenum,在users表和person表中插入相关信息,若username或name在表中已经有同样的值,返回出错信息。
以ChangePasswordServlet为例:
package com.wzbfq.web.servlet;
import com.wzbfq.bean.User;
import com.wzbfq.service.Impl.UsersServiceImpl;
import com.wzbfq.util.DButil;
import net.sf.json.JSONObject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
@WebServlet(name="changePasswordServlet")
public class ChangePasswordServlet extends HttpServlet {
DButil dButil = new DButil();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
try(PrintWriter out = response.getWriter()){
String username = request.getParameter("username").trim();
String password = request.getParameter("password").trim();
UsersServiceImpl service = new UsersServiceImpl();
boolean change = service.changePassword(new User(username,password),dButil);
// out.write(username);
// out.write(password);
Map<String,String> params = new HashMap<>();
JSONObject jsonObject = new JSONObject();
if(change){
params.put("Result","ChangeSucceed");
}else{
params.put("Result","ChangeFail");
}
jsonObject.put("params",params);
out.write(jsonObject.toString());
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
大概步骤就是:
1、设置相应内容类型
2、获取参数
3、调用service层函数,完成相关操作,得到返回结果
4、将结果封装到JSONObject中,返回
客户端
![](https://i-blog.csdnimg.cn/blog_migrate/6d5f71bb52f9a323cdadb6b049cbc2f6.png)
一个Activity对应一个页面的相关操作。
1、MainActivity 获取用户填写的username和password,其中password栏支持一个状态开关按钮,可选择是否隐藏密码。
- 点击Login按钮时会向后台的LoginServlet发送数据,LoginServlet处理后返回结果。验证成功则进入欢迎界面,否则,停留在登录界面。
- 点击SignUp按钮会跳转到注册页面。
- 点击忘记密码(Tap here)时,也向后台的LoginServlet发送数据,验证该username是否存在,若存在则进入忘记密码界面,否则停留在登录界面。
2、RegisterActivity会获取用户输入的username、name、age、telenum、passord(需输入两次密码),进行注册操作。
- 点击Submit按钮时会对用户输入的数据进行格式检查,如年龄只能是0~99之间的整型数字,若不输入年龄则默认为18。其中,username、name、telenum和password设置为必填。然后向后台的RegisterServlet发送这些数据,进行注册操作。
3、ForgetPasswordActivity 获取用户填写的手机号,发送短信验证码给手机,判断验证码是否正确。
- 点击Submit按钮时会向后台的ForgetPasswordServlet发送数据,验证用户填写的telenum和登录页面填写的username是否匹配,若匹配并且验证码正确则进入修改密码界面,否则停留在忘记密码界面。一分钟之内不会再次发送验证码。
- 点击Home按钮会返回登录界面。
4、ChangePasswordActivity 获取用户输入新的密码(需输入两次密码),提供密码格式检测。
- 点击Submit按钮时会向后台的ChangePasswordServlet发送数据,进行修改密码的操作。
- 点击Home按钮会返回登录界面
5、WelcomeActivity 选择是否显示欢迎信息。
- 点击ViewInfo按钮时会向后台的WelcomeServlet发送数据,根据username得到name、age、telenum信息,在界面上显示。
- 点击Home按钮会返回登录界面。
此外,一个Activity对应一个layout目录下的xml文件。
- activity_main.xml 对应登录界面;
- activity_register.xml 对应注册界面。
- activity_forget_password.xml 对应忘记密码界面;
- activity_change_password.xml 对应修改密码界面;
- activity_welcome.xml 对应欢迎界面;
以ChangePasswordActivity为例:
package com.example.client;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
public class ChangePasswordActivity extends AppCompatActivity {
private String username;
private String pass1;
private String pass2;
private Button btn_submit;
private Button btn_home;
private EditText p1;
private EditText p2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_change_password);
username = getIntent().getStringExtra("username");
initview();//找到Button、EditText等的位置
}
public void initview(){
btn_submit = findViewById(R.id.button9);
btn_home = findViewById(R.id.button10);
p1=findViewById(R.id.npw);
p2=findViewById(R.id.npw2);
}
public void ChangePasswordRequest(final String username, final String password) {
//请求地址,我这里47.100.39.146是买的阿里云服务器的外网,若是本地则填IP地址
String url = "http://47.100.39.146:8080/server/changePasswordServlet";
String tag = "ChangePassword";
//取得请求队列
RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
//防止重复请求,所以先取消tag标识的请求队列
requestQueue.cancelAll(tag);
//创建StringRequest,定义字符串请求的请求方式为POST(省略第一个参数会默认为GET方式)
final StringRequest request = new StringRequest(Request.Method.POST, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
try {
JSONObject jsonObject = (JSONObject) new JSONObject(response).get("params");
String result = jsonObject.getString("Result");
if (result.equals("ChangeSucceed")) {
Toast.makeText(ChangePasswordActivity.this, "Change Succeed!", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(ChangePasswordActivity.this, MainActivity.class);
startActivity(intent);
} else if (result.equals("ChangeFail")) {
Toast.makeText(ChangePasswordActivity.this, "The username does not exist!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(ChangePasswordActivity.this, "纳尼", Toast.LENGTH_SHORT).show();
}
} catch (JSONException e) {
//做自己的请求异常操作,如Toast提示(“无网络连接”等)
Log.e("TAG", e.getMessage(), e);
toast("无网络连接");
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
//做自己的响应错误操作,如Toast提示(“请稍后重试”等)
toast("请稍后重试");
Log.e("TAG", error.getMessage(), error);
}
}) {
//重写该函数以传递参数
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<>();
params.put("username", username);
params.put("password", password);
return params;
}
};
//设置Tag标签
request.setTag(tag);
//将请求添加到队列中
requestQueue.add(request);
}
public void BackToHome (View view){//点击Home按钮时执行的函数,跳转到登录界面
Intent intent = new Intent(ChangePasswordActivity.this, MainActivity.class);
startActivity(intent);
}
private void toast(final String str) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(ChangePasswordActivity.this, str, Toast.LENGTH_SHORT).show();
}
});
}
public boolean check() {//检查密码格式是否满足要求
pass1 = p1.getText().toString();
pass2 = p2.getText().toString();
boolean isok = false;
boolean f1 = true;
String mess = "";
if (pass1.isEmpty()) {
toast(" Password input is empty ");
f1 = false;
} else {
if (pass1.length() < 6 || pass1.length() > 12)
mess += " Password length needs 6-12 digits ";
String pattern6 = "[a-zA-Z\\d_]*";
boolean match6 = Pattern.matches(pattern6, pass1);
if (!match6) {
mess += " The password can only consist of English letters, numbers and _ ";
}
if (!mess.isEmpty()) {
toast(mess);
f1 = false;
}
}
if (f1) {
if (!pass1.equals(pass2)) {
toast(" Passwords entered twice are inconsistent ");
}else{
isok = true;
}
}
return isok;
}
//点击Submit按钮时执行的函数,先检查格式,格式OK后,就发出一个改密码的请求
public void change (View view){
if(check()){
ChangePasswordRequest(username,pass1);
}
}
}
值得一提的是,常用的网络请求方式主要有:HttpClient、HttpURLConnection、OKHttp和Volley,这里使用的是Volley框架。
后记
这学期这个1学分的课程随着实验四的完成结束了。一开始接触感觉很难,无论是配环境还是完全陌生的知识,但到现在感觉挺有意思的。
这次实验还有许多待改进的地方,比如短信验证那块,验证用户名存在的正确性后,要输入手机号时,可以人性化的给出手机号前3位和后4位的提示,其次还有调整验证码验证手机号和后台检验用户名与手机号是否对应的顺序,等等。
因为时间关系,挑战两周半复习半预习3门核心课全部知识点还是有点难度的,实验四就做到这吧,感谢组员的付出,以后有机会再优化(提示中可能会有英语表述不当,小bug等问题)。
知识浅薄,若有错误、不足之处欢迎私信或留言。
附件
工程文件以及数据库的users表和person表导出的sql文件等,已上传至github:https://github.com/wangzhebufangqi/New-Programming-Technology-Practice/tree/master/lab4
主要参考链接
1、CSDN——自己动手——快速搭建Android应用服务器
2、CSDN——Android Volley完全解析(一),初识Volley的基本用法
3、CSDN——android–activity启动另一个activity 传参
4、CSDN——三方SDK——mob短信验证