本java后台采用Spring boot快速搭建,仅用于小demo。
采用idea开发平台快速搭建Spring boot后台前面已经说过了,这里不再细说,参考下面的文章。
Spring框架(三):SpringBoot框架(由浅入深,深度解读)
本demo为了简单的实现app端访问java后台,仅实现登陆和注册功能,下面是功能演示图。
打开百度App,看更多图片
登陆功能图
注册功能图
完善个人信息
主页
本demo的Android采用gradle项目管理工具自动构建。Android端采用json数据格式和后台进行交互,需要在gradle上配置3个jar包。
gson.jar包,fastjson.jar包,和xutils.jar包。
gradle的使用和maven类似,只需要导入3个jar包的依赖就可以了,导入jar包的文件在src下的build.gradle文件下。
jar包依赖
建立包结构如下图:
Android端包结构
本demo用到了4个activity,我们创建的每一个activity都要在AndroidManifest中配置activity标签。
在编写各个activity源代码之前,首先,要先了解一个工具类,这个工具类的作用是利用socket套接字发送HTTP协议,通过IP地址链接到服务器,然后向服务器传参,具体形式和从浏览器向服务器类似。
HttpPostUtil.java
public class HttpPostUtil {
private static HttpURLConnection connection;
public static String doPostRequest(String path,String content){
BufferedReader br = null;
String result = "";
try {
System.out.println("要发送的信息"+content);
String address = "http://192.168.56.1:8080/"+path+content;
URL url = new URL(address);
connection = (HttpURLConnection)url.openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setReadTimeout(10+1000);
connection.setConnectTimeout(10+1000);
connection.setRequestMethod("GET");
connection.connect();
if (connection.getResponseCode()==200){
br = new BufferedReader(new InputStreamReader(connection.getInputStream(),"utf-8"));
String line = null;
while ((line=br.readLine())!=null){
System.out.println("line="+line);
result+=line;
}
}
System.out.println("服务器返回的数据是"+result);
return result;
} catch (Exception e) {
e.printStackTrace();
}finally {
if (br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
HTTP的工具类就是这样了。接下来是两个实体类。
User.java 是封装用户信息的实体类。ResultData.java是封装服务器返回信息的实体类,二者都与服务器相关。
User.java
public class User implements Serializable {
private int id;
private String username;
private String phone;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
然后是ResultData.java
public class ResultData<T> implements Serializable {
private T data;//携带数据
private int status;//状态码
private String msg;//信息
public ResultData() { }
public ResultData(T data, int status, String msg) {
this.data = data;
this.status = status;
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
好了开始写activity。
loginActivity.java
首先在layout中创建loginActivity所对应的xml文件,作为UI界面展示。
配置代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
tools:context=".activity.LoginActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/t_login"
android:text="欢迎登陆"
android:textSize="30dp"
android:gravity="center"
android:layout_marginTop="50dp"
/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:id="@+id/t_phone"
android:inputType="text|phone"
android:hint="手机号"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:ems="10"
android:id="@+id/t_pwd"
android:hint="密 码"
/>
<Button
android:text="登陆"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="login"
/>
<Button
android:text="注册"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/bt_reg"
android:onClick="register"
/>
</LinearLayout>
我们可以在preView界面看到UI界面显示图。
登陆UI
然后编写loginActivity源代码;
public class LoginActivity extends AppCompatActivity {
private EditText t_phone,t_pwd;
private String phone_num,password;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
t_phone = (EditText)findViewById(R.id.t_phone);
t_pwd = (EditText)findViewById(R.id.t_pwd);
}
public void login(View v){
//获得手机号、密码
phone_num = t_phone.getText().toString();
password = t_pwd.getText().toString();
//清楚焦点
t_phone.clearFocus();
t_pwd.clearFocus();
//判断
if (checkStr(phone_num)&&checkStr(password)){
//上传的参数部分
String param = "?phone="+phone_num+"&password="+password;
String path = "login/login.do";
ResultData<User> resultData =
JsonUtil.getResult(path,param,User.class);
if (resultData!=null){
Toast.makeText(LoginActivity.this,resultData.getMsg(),Toast.LENGTH_SHORT).show();
if (resultData.getStatus()==1){
jumpTo();
}
}
}else {
Toast.makeText(LoginActivity.this,"输入有误",Toast.LENGTH_SHORT).show();
}
}
public void register(View v){
Intent in = new Intent(this,RegisterActivity.class);
startActivity(in);
}
public boolean checkStr(String str){
if(str==null||"".equals(str.trim())){
return false;
}
return true;
}
public void jumpTo(){
Intent in = new Intent(this,TestActivity.class);
startActivity(in);
}
}
大家可以看到,其实代码并不复杂,在这里主要介绍上传参数的部分代码的详解。
上传代码部分其实还有两个工具类。
JSONUtil.java和MyThread.java。
见名而知其意,两个工具类是为了json数据和多线程处理数据的。
String param = "?phone="+phone_num+"&password="+password;
String path = "login/login.do";
ResultData<User> resultData =
JsonUtil.getResult(path,param,User.class);
if (resultData!=null){
Toast.makeText(LoginActivity.this,resultData.getMsg(),Toast.LENGTH_SHORT).show();
if (resultData.getStatus()==1){
jumpTo();
}
}
param:作为http传输协议中的请求数据部分。
path:作为HTTP传输协议中的请求行部分。
下面是JsonUtil.java
JsonUtil是app端用来处理服务器返回的数据的工具类的。
内部有两个静态方法:getJsonObj()和getResult()。
getJsonObj():用来获取json对象。
getResult():用来获取经过处理的服务器返回数据ResultData。
代码如下:
public class JsonUtil {
public static JSONObject getJsonObj(String str){
if (str!=null&&str!=""){
return JSON.parseObject(str);
}
return null;
}
public static ResultData getResult(String path,String param,Class clazz){
ResultData resultData = null;
try {
MyThread run = new MyThread(path,param);
run.start();//开启多线程传输数据
run.join();//接收线程数据,结束线程
JSONObject json = getJsonObj(run.getResponse());
if (json!=null){
resultData = new ResultData<>();
resultData.setStatus(json.getInteger("status"));
resultData.setMsg(json.getString("msg"));
resultData.setData(json.getObject("data", clazz));
}
} catch (Exception e) {
e.printStackTrace();
}
return resultData;
}
}
可以看到getResult()方法中开启了一个线程来传输数据。
其实在这里本来想采用Android的handle机制与多线程配合处理数据的,后来感觉这样有点麻烦,就定义了几个工具类来处理数据的交互,效果在演示时还不错。
可以看到3个参数。param、path、clazz的作用。
前两个已经解释过了,clazz主要是利用java的反射机制将数据反射到传过来的实体类上。
然后我们带入MyThread.java这个类。
MyThread.java
//开启多线程处理的类
public class MyThread extends Thread implements Runnable {
private String path;//开启线程跳转路径
private String param;//开启线程传递参数
private String response;//获取返回的信息
public MyThread(){
path = "";
param = "";
}
public MyThread(String path, String param) {
this.path = path;
this.param = param;
}
@Override
public void run() {
response = HttpPostUtil.doPostRequest(path,param);
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getParam() {
return param;
}
public void setParam(String param) {
this.param = param;
}
public String getResponse() {
return response;
}
public void setResponse(String response) {
this.response = response;
}
}
其实这个类的主要作用就2个。
启动线程向服务器发送数据,接受返回的数据并存到response里面。
HttpPostUtil这个类我们在前面就写了。
这里配合讲解。
String address = "http://192.168.56.1:8080/"+path+content;
URL url = new URL(address);
这两句程序,相当于访问的路径主体了,创建了访问的计划URL,在这里就不解释访问的过程了。
connection.connect();
这句程序开启连接后台。
br = new BufferedReader(new InputStreamReader(connection.getInputStream(),"utf-8"));
String line = null;
while ((line=br.readLine())!=null){
result+=line;
}
这段程序执行了读取服务器返回的json数据过程,将json数据流转换成字符串,然后赋值给response属性。
MyThread run = new MyThread(path,param);
run.start();//开启多线程传输数据
run.join();//接收线程数据,结束线程
JSONObject json = getJsonObj(run.getResponse());
这段程序是开启线程,实现数据交互,并将获取到的字符串转换成json对象,然后映射为ResultData对象,便于我们随意的调用。
然后我们说一下后台的代码吧。
java后台采用Spring Boot搭建,下面是maven的配置项。
导入了阿里的德鲁伊连接池和fastjson jar包,还有web组件,mysqlConnection、以及mybatils等jar包。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.wyq.android</groupId>
<artifactId>testandroid</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>testandroid</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.8</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
其实Spring Boot快速搭建一个小demo挺简单的,代码也很少。采用Spring MVC结构,包结构如下:
后台包结构
Controller的类,以login为例。
@RestController
@RequestMapping("login")
public class LoginController {
@Resource(name = "loginService")
private LoginService loginService;
@RequestMapping("/test.do")
public String test(){
return "hello word";
}
@RequestMapping("/login.do")
public ResultData<User> login(User user){
return loginService.login(user);
}
}
然后是登陆的service层实现类。
@Service("loginService")
public class LoginServiceImp implements LoginService {
@Resource(name = "userDao")
private UserDao userDao;
private User rep;
/**
* ok:1 err:0
* @param user
* @return result
*/
@Override
public ResultData<User> login(User user) {
rep = userDao.login(user);
if(rep!=null){
return getResult(rep,1,"登陆成功");
}
return getResult(null,0,"找不到用户");
}
public ResultData<User> getResult(User user,int status,String msg){
return new ResultData<User>(user,status,msg);
}
}
这里的ResultData类和APP端的一样,就是为了方便数据传输。
下面是dao层的实现。Spring boot支持注解写sql语句,非常方便。
@Repository("userDao")
@Mapper
public interface UserDao {
// 相当于把映射配置文件中的sql语句用注解的方法写在dao层中
@Select("select * from d_user")
List<User> findAll();
@Select("select * from d_user where phone=#{phone} and password=#{password}")
User login(User user);
@Insert("insert into d_user values(null,#{username},#{phone},#{password})")
int add(User user);
@Update("update d_user set username=#{username},password=#{password} where phone=#{phone}")
int update(User user);
}
运行起来,在浏览器上访问测试是否正常。
浏览器测试
可以看到返回json数据格式,正确。
OK,搭载在手机上测试,登陆正常。
然后是注册功能,其实这个功能和登录功能差不多,只不过后台一个是查数据,一个是增加数据和完善数据。
注册分为两步,一步是验证手机,另一步是完善自己的用户名和密码信息,就算完成注册功能了。
下面是注册第一步,验证手机的UI界面。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
tools:context=".activity.RegisterActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="欢迎注册"
android:textSize="30dp"
android:gravity="center"
android:layout_marginTop="50dp"
android:layout_marginBottom="20dp"
/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:id="@+id/re_phone"
android:inputType="text|phone"
android:hint="手机号"/>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:orientation="horizontal"
>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:ems="12"
android:id="@+id/t_code"
android:hint="验证码"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="18dp"
android:text="验证码"
android:onClick="getCode"
/>
</LinearLayout>
<Button
android:text="注册"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:onClick="register"
/>
</LinearLayout>
下面是第二部,完善信息的界面:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="30dp"
tools:context=".activity.AddActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="完善信息"
android:textSize="30dp"
android:gravity="center"
android:layout_marginTop="50dp"
/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/t_nick"
android:inputType="text"
android:hint="昵 称"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:id="@+id/t_set_pwd"
android:hint="密 码"
/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:id="@+id/t_sure_pwd"
android:hint="确认密码"
/>
<Button
android:text="进入应用"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="enterIn"
android:background="@color/colorAccent"
/>
</LinearLayout>
完善信息界面
界面展示完了,下面讲一下连个的activity。
//注册
public class RegisterActivity extends AppCompatActivity {
private EditText t_phone,t_code;
private String phone_num,code;
private boolean isOk = false;
private User user;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
t_phone = (EditText)findViewById(R.id.re_phone);
t_code = (EditText)findViewById(R.id.t_code);
}
public void getCode(View view){
phone_num = t_phone.getText().toString();
if(phone_num!=null&&phone_num.trim().length()==11){
Toast.makeText(RegisterActivity.this,"手机号格式正确",Toast.LENGTH_SHORT).show();
//短信验证码接口
//这里自动生成6位数字
code = "";
for (int i = 0; i < 6; i++) {
code += (int) (Math.random() * 10);
}
Toast.makeText(RegisterActivity.this, "验证码=" + code, Toast.LENGTH_SHORT).show();
t_code.setText(code);
isOk = true;
}else {
Toast.makeText(RegisterActivity.this,"手机号格式错误",Toast.LENGTH_SHORT).show();
}
}
public void register(View v){
phone_num = t_phone.getText().toString();
if(isOk){
String param = "?phone="+phone_num;
String path = "reg/register.do";
ResultData<User> resultData = JsonUtil.getResult(path,param,User.class);
if (resultData!=null){
Toast.makeText(RegisterActivity.this,resultData.getMsg(),Toast.LENGTH_SHORT).show();
if (resultData.getStatus()==1){
user = resultData.getData();
jumpTo();
}
}
}
}
public void jumpTo(){
Intent in = new Intent(RegisterActivity.this,AddActivity.class);
Bundle bundle = new Bundle();
bundle.putString("phone",user.getPhone());
in.putExtras(bundle);
startActivity(in);
}
}
这里的数据传输就不细讲了,和loginActivity一样,主要讲一下,activity之间的数据传递。
可以看到jumpTo()方法里面的程序。
public void jumpTo(){
Intent in = new Intent(RegisterActivity.this,AddActivity.class);
Bundle bundle = new Bundle();
bundle.putString("phone",user.getPhone());
in.putExtras(bundle);
startActivity(in);
}
Intent和Bundle是Android里面的工具类。
Intent负责对应用中一次操作的动作、动作涉及数据、附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将 Intent传递给被调用的组件,并完成组件的调用。
Bundle是一个简单的数据携带包,该对象也包含了多个方法来存入数据,多采用key-value形式。
跳转到AddActivity后,执行下面两行代码,获取传输的数据。
Intent intent = getIntent();
phone = (String)intent.getExtras().get("phone");
AddActivity这个类主要是为了完善用户信息,实际上就是更新信息。
下面是源代码:
public class AddActivity extends AppCompatActivity {
private EditText t_set_pwd,t_nick,t_sure_pwd;
private String phone;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add);
t_set_pwd = (EditText)findViewById(R.id.t_set_pwd);
t_sure_pwd = (EditText)findViewById(R.id.t_sure_pwd);
t_nick = (EditText)findViewById(R.id.t_nick);
//获取用户id
Intent intent = getIntent();
phone = (String)intent.getExtras().get("phone");
}
public void enterIn(View v){
String pwd = t_set_pwd.getText().toString();
String nick = t_nick.getText().toString();
String finalPwd = t_sure_pwd.getText().toString();
boolean isOk = true;
//检查是否为空
if(!checkStr(nick)){
toast("请输入用户名");
isOk=false;
}
if(!checkStr(pwd)){
toast("请输入密码");
isOk = false;
}
if(!checkStr(finalPwd)){
isOk = false;
toast("请确认密码");
}
if (checkStr(pwd)){
if(!pwd.equals(finalPwd)){
isOk = false;
toast("确认密码错误");
}
}
//检查是否合格
if (isOk){
//补全信息
String param = "?phone="+phone+"&username="+nick+"&password="+pwd;
String path = "reg/add.do";
ResultData<User> resultData = JsonUtil.getResult(path,param,User.class);
if (resultData!=null){
Toast.makeText(AddActivity.this,resultData.getMsg(),Toast.LENGTH_SHORT).show();
if (resultData.getStatus()==1){
System.out.println("user="+resultData.getData());
jumpTo();
}
}
}
}
//进入首页
private void jumpTo() {
startActivity(new Intent(AddActivity.this,TestActivity.class));
}
public boolean checkStr(String str){
if(str==null||"".equals(str.trim())){
return false;
}
return true;
}
public void toast(String msg){
Toast.makeText(AddActivity.this,msg,Toast.LENGTH_SHORT).show();
return;
}
}
好了,具体的难点和要点,就这么多了,不说那么多了。
项目下载地址链接: https://pan.baidu.com/s/1d40MR8BFCkCXigvqvRklsw 提取码: 7wet