1.引言
使用Android studio+idea构建一个简单的登录页面,使用mysql数据库存储数据,springboot框架构建后端。
2. 构建android前端
使用线性布局创建两个输入框和一个登入按钮:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity">
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" android:paddingLeft="50dp" android:paddingRight="50dp">
<TextView android:id="@+id/tv_Title" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="登录" android:textSize="36sp" />
<EditText android:id="@+id/et_username" android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" android:hint="用户名" android:inputType="text" />
<EditText android:id="@+id/et_password" android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" android:hint="密码" android:inputType="textPassword" />
<Button android:id="@+id/btn_login" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="登录" android:textSize="20sp" /> </LinearLayout> </androidx.constraintlayout.widget.ConstraintLayout>
|
3.idea后端

按照图示创建一个spring项目,不添加任何框架支持。
在pom.xml中添加mybatis,JDBC,JSON依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency>
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
<dependency> <groupId>org.json</groupId> <artifactId>json</artifactId> <version>20210307</version> </dependency>
|
按照如下创建好文件目录:

User对象只有username和password两个属性,生成他的get、set方法即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.example.loginddns.bean; public class User {
private String username; private String password;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; } }
|
UserMapper与数据库构成映射,由mybatis自动映射:
1 2 3 4 5 6 7 8 9 10 11 12
| package com.example.loginddns.dao;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select;
@Mapper public interface UserMapper {
@Select("SELECT count(*) FROM users WHERE username=#{username} AND password=#{password}") int login(String username, String password);
}
|
UserController控制层完成对接口的映射:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| package com.example.loginddns.controller;
import com.example.loginddns.dao.UserMapper; import org.json.JSONObject; import org.json.JSONException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api") public class UserController {
private JSONObject objectTrue = new JSONObject(); private JSONObject objectFalse = new JSONObject();
@Autowired UserMapper userMapper;
public UserController() throws JSONException { objectTrue.put("result", true); objectFalse.put("result", false); }
@ResponseBody @RequestMapping("/login") public String login(String username, String password){ if(userMapper.login(username, password) == 0){ return objectFalse.toString(); } else { return objectTrue.toString(); } } }
|
application.properties开放端口号,数据库链接配置:
1 2 3 4 5 6 7 8 9 10 11
| spring.application.name=LoginDDNS spring.datasource.url=jdbc:mysql://<数据库端口号>/<库名>?serverTimezone=UTC&characterEncoding=utf8 spring.datasource.username=<你的数据库账号> spring.datasource.password=<你的数据库密码> spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.properties.hibernate.hbm2ddl.auto=update spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect spring.jpa.show-sql=true
server.port=10003
|
点击运行按钮,成功运行后显示当前tomcat版本等等信息:

4. 内网穿透
上述项目运行后,打开浏览器输入以下链接测试连通性:
1
| http://localhost:10003/api/login?username=admin&password=password
|

这里使用花生壳内网穿透,6块钱买断制,进去注册好填写本机ipv4地址和后端项目的端口号,将他们创建在项目映射中,开启内网穿透访问外网地址,将上述的本机地址替换为花生壳提供的域名即可在其他环境访问本机端口开放的端口号。

5.开放android联网权限
在AndroidManifest.xml中添加以下语句:
1
| <uses-permission android:name="android.permission.INTERNET" />
|
编写一个HttpUtils对网络发送来的json格式数据进行解析的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| package com.example.loginddns;
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL;
public class HttpUtils {
public HttpUtils(){
}
public static String getJsonContent(String url_path) { try{ URL url = new URL(url_path); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(3000); connection.setRequestMethod("GET"); connection.setDoInput(true); int code = connection.getResponseCode(); if(code == 200){ return changeInputStream(connection.getInputStream()); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return ""; }
private static String changeInputStream(InputStream inputStream) { String jsonString = ""; ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); int len = 0; byte[] data = new byte[1024]; try{ while ((len = inputStream.read(data)) != -1) { outputStream.write(data, 0, len); } jsonString = new String(outputStream.toByteArray()); return jsonString; } catch (IOException e) { e.printStackTrace(); } return ""; } }
|
将layout页面组件事件绑定到mainActivity中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| package com.example.loginddns;
import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast;
import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AppCompatActivity; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat;
import org.json.JSONException; import org.json.JSONObject;
public class MainActivity extends AppCompatActivity {
private TextView et_username; private TextView et_password; private Button btn_login;
private boolean password_currect = false;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initEvent(); }
public void initView(){ et_username = this.findViewById(R.id.et_username); et_password = this.findViewById(R.id.et_password); btn_login = this.findViewById(R.id.btn_login); }
public void initEvent(){ btn_login.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String username = et_username.getText().toString(); String password = et_password.getText().toString(); Thread thread = new Thread(new Runnable() { @Override public void run() { String url = "<花生壳提供的域名>/api/login?username=" +username + "&password=" + password; String result = HttpUtils.getJsonContent(url); try { JSONObject jsonObject = new JSONObject(result); if(jsonObject.getBoolean("result") == true) { password_currect = true; } else { password_currect = false; } } catch (JSONException e) { e.printStackTrace(); } } }); thread.start(); try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } if(password_currect) { Toast.makeText(MainActivity.this, "登录成功!", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, "密码错误!", Toast.LENGTH_SHORT).show(); } } }); } }
|
可以看见页面正常访问到后端并且获取到了数据