Android studio进阶开发(七)---做一个完整的登录系统(前后端连接)

我们已经讲过了okhttp登录系统的使用,我们今天做一个完整的登录系统,后端用spring+mybatis去做

数据库内容

sql 复制代码
-- 创建学生信息表
CREATE TABLE student_info (
    id SERIAL PRIMARY KEY,  -- 添加自增主键
    name VARCHAR(255) NOT NULL,
    number INT NOT NULL,
    code INT NOT NULL,
    student_id INT UNIQUE NOT NULL,  -- 学号唯一
    Ptteacher VARCHAR(255),
    Zone INT,
    grade INT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP  -- 添加创建时间戳
);

-- 插入示例数据
INSERT INTO student_info (name, number, code, student_id, Ptteacher, Zone, grade)
VALUES 
    ('张三', 1001, 2023, 20230001, '王老师', 2, 1),
    ('李四', 1002, 2023, 20230002, '刘老师', 1, 1),
    ('王五', 2001, 2022, 20220001, '张老师', 3, 2),
    ('赵六', 2002, 2022, 20220002, '谢老师', 2, 2),  -- 导师可以为空
    ('钱七', 3001, 2021, 20210001, '陈老师', 1, 3);

-- 查询验证数据
SELECT * FROM student_info;

效果图

后端

Student.java

java 复制代码
package com.example.threes;



public class Student { // 改为更有意义的类名
    private Integer id; // 添加主键
    private String name;
    private Integer number;
    private Integer code;
    private Integer studentId; // 改为Java命名规范
    private String ptTeacher; // 改为Java命名规范
    private Integer zone;
    private Integer grade;

    // 所有getter/setter
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getNumber() {
        return number;
    }

    public void setNumber(Integer number) {
        this.number = number;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public Integer getStudentId() {
        return studentId;
    }

    public void setStudentId(Integer studentId) {
        this.studentId = studentId;
    }

    public String getPtTeacher() {
        return ptTeacher;
    }

    public void setPtTeacher(String ptTeacher) {
        this.ptTeacher = ptTeacher;
    }

    public Integer getZone() {
        return zone;
    }

    public void setZone(Integer zone) {
        this.zone = zone;
    }

    public Integer getGrade() {
        return grade;
    }

    public void setGrade(Integer grade) {
        this.grade = grade;
    }
}

StudentController.java

java 复制代码
package com.example.threes;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/students")
public class StudentController {

    @Autowired
    private StudentService studentService;

    @GetMapping("/all")
    public List<Student> getAllStudents() {
        return studentService.getAllStudents();
    }

    @GetMapping("/search")
    public List<Student> getStudentById(
            @RequestParam(value = "student_id", required = true) Integer studentId) {
        return studentService.getStudentById(studentId);
    }
}

StudentDao.java

java 复制代码
package com.example.threes;
import java.util.List;


import java.util.List;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface StudentDao { // 接口改名
    List<Student> findAll();
    List<Student> findByStudentId(Integer studentId); // 单一查询方法
    // 删除其他不相关的方法
}

StudentService.java

java 复制代码
package com.example.threes;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class StudentService {
    @Autowired
    private StudentDao studentDao;

    public List<Student> getAllStudents(){
        return studentDao.findAll();
    }

    public List<Student> getStudentById(Integer studentId){
        return studentDao.findByStudentId(studentId);
    }
}

Student.xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.threes.StudentDao"> <!-- 更新为正确的接口名 -->

    <!-- 结果映射 -->
    <resultMap id="studentResultMap" type="com.example.threes.Student">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="number" column="number"/>
        <result property="code" column="code"/>
        <result property="studentId" column="student_id"/>
        <result property="ptTeacher" column="ptteacher"/>
        <result property="zone" column="zone"/>
        <result property="grade" column="grade"/>
    </resultMap>

    <!-- 查询所有学生 -->
    <select id="findAll" resultMap="studentResultMap">
        SELECT * FROM student_info
    </select>

    <!-- 按student_id查询 -->
    <select id="findByStudentId" resultMap="studentResultMap" parameterType="int">
        SELECT * FROM student_info
        WHERE student_id = #{studentId}
    </select>
</mapper>

效果图

客户端

student.java

java 复制代码
package com.example.project_a.Information;

public class Student {
    public String name;
    public int number;
    public int code;
    public int student_id;
    public String Ptteacher;
    public int Zone;
    public int grade;
    //学生的必要信息,可从数据库中修改


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    // number 属性的getter和setter
    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    // code 属性的getter和setter
    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    // student_id 属性的getter和setter
    public int getStudent_id() {
        return student_id;
    }

    public void setStudent_id(int student_id) {
        this.student_id = student_id;
    }

    // Ptteacher 属性的getter和setter
    public String getPtteacher() {
        return Ptteacher;
    }

    public void setPtteacher(String Ptteacher) {
        this.Ptteacher = Ptteacher;
    }

    // Zone 属性的getter和setter
    public int getZone() {
        return Zone;
    }

    public void setZone(int Zone) {
        this.Zone = Zone;
    }

    // grade 属性的getter和setter
    public int getGrade() {
        return grade;
    }

    public void setGrade(int grade) {
        this.grade = grade;
    }
}

Stu_Login_Activity.java

java 复制代码
package com.example.project_a;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.Manifest;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.method.HideReturnsTransformationMethod;
import android.text.method.PasswordTransformationMethod;
import android.text.method.TransformationMethod;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.example.project_a.Information.Student;
import com.example.project_a.tools.PermissionsInfo;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions;

public class Stu_Login_Activity extends AppCompatActivity {
    // 控件声明
    private EditText mEtUsername;
    private EditText mEtPassword;
    private Button mBtnLogin;
    private Button mBtnFound;
    private ImageView mImgEye;
    private CheckBox mChbRemember;
    private TextView tv_result;
    private CheckBox mChbAutoLogin;

    // 额外声明
    private SharedPreferences loginPreferences;

    // Student对象作为成员变量
    private Student student;
    private boolean studentDataLoaded = false;

    // 常量定义
    private static String strUsername = "1";
    private static String strPassword = "1";
    private static boolean hide = true;
    private static String CheckOrNot = "checked";
    private static String UserName = "UserName";
    private static String PassWord = "PassWord";
    private boolean isNetworkVerificationInProgress = false; // 新增标志位

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_stu_login);

        checkingAndroidVersion(); // 检查安卓版本

        // 初始化Student对象
        student = new Student();

        // 控件绑定
        mEtUsername = findViewById(R.id.ed_stuLogin_username);
        mEtPassword = findViewById(R.id.et_stuLogin_Password);
        mBtnLogin = findViewById(R.id.btn_stuLogin_login);
        mBtnFound = findViewById(R.id.btn_stuLogin_find);
        mImgEye = findViewById(R.id.img_stuLogin_eye);
        mChbRemember = findViewById(R.id.cb_stuLogin_remember);
        mChbAutoLogin = findViewById(R.id.cb_stuLogin_autologin);
        tv_result = findViewById(R.id.tv_result); // 确保有这个TextView

        loginPreferences = getSharedPreferences("Login_stu", MODE_PRIVATE);
        boolean checked = loginPreferences.getBoolean(CheckOrNot, false);
        if (checked) {
            Map<String, String> map = readLogin();
            if (map != null) {
                mEtUsername.setText(map.get(UserName).toString());
                mEtPassword.setText(map.get(PassWord).toString());
                mChbRemember.setChecked(checked);
            }
        }

        // 功能实现
        // 登录
        mBtnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 读取是否保存了密码
                configLoginInfo(mChbRemember.isChecked());

                // 防止重复点击
                if (isNetworkVerificationInProgress) {
                    return;
                }

                // 正常登录
                if (onCheck(v)) {
                    Intent intent = new Intent(Stu_Login_Activity.this, Activity_Student_Homepage.class);
                    startActivity(intent);
                }
            }
        });

        // 跳转到网页
        mBtnFound.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Uri uri = Uri.parse("http://www.bing.com"); // 账户找回url修改位置
                Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                startActivity(intent);
            }
        });

        // 密码显示与否设置
        mImgEye.setImageResource(R.drawable.invisible);
        mImgEye.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (v.getId() == R.id.img_stuLogin_eye) {
                    if (hide == true) {
                        mImgEye.setImageResource(R.drawable.visible);
                        HideReturnsTransformationMethod method = HideReturnsTransformationMethod.getInstance();
                        mEtPassword.setTransformationMethod(method);
                        hide = false;
                    } else if (hide == false) {
                        mImgEye.setImageResource(R.drawable.invisible);
                        TransformationMethod method = PasswordTransformationMethod.getInstance();
                        mEtPassword.setTransformationMethod(method);
                        hide = true;
                    }
                }
            }
        });
    }

    // 发起GET方式的HTTP请求
    private void doGet() {
        isNetworkVerificationInProgress = true; // 设置网络验证开始标志

        final String username = mEtUsername.getText().toString().trim();
        if (username.isEmpty()) {
            runOnUiThread(() -> {
                Toast.makeText(Stu_Login_Activity.this, "用户名不能为空", Toast.LENGTH_SHORT).show();
                isNetworkVerificationInProgress = false;
            });
            return;
        }

        // 1. 构建带参数的URL
        HttpUrl url = new HttpUrl.Builder()
                .scheme("http")
                .host("192.168.43.9") // 替换为实际IP
                .port(8080)
                .addPathSegments("students/search") // 层级路径的正确写法
                .addQueryParameter("student_id", username) // 查询参数 - 使用真实的用户名
                .build();

        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(15, TimeUnit.SECONDS)  // 添加超时配置
                .readTimeout(20, TimeUnit.SECONDS)
                .build();

        Request request = new Request.Builder()
                .url(url)
                .header("Accept-Language", "zh-CN")
                .build();

        runOnUiThread(() -> Toast.makeText(Stu_Login_Activity.this, "正在验证账号...", Toast.LENGTH_SHORT).show());

        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                runOnUiThread(() -> {
                    Toast.makeText(Stu_Login_Activity.this, "网络请求失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
                    tv_result.setText("请求失败: " + e.getMessage());
                    isNetworkVerificationInProgress = false;
                });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    String resp = response.body().string();
                    try {
                        JSONArray jsonArray = new JSONArray(resp);
                        // 检查是否有数据
                        if (jsonArray.length() > 0) {
                            // 只取第一组学生数据
                            JSONObject jsonObject = jsonArray.getJSONObject(0);

                            // 填充Student对象数据
                            student.name = jsonObject.optString("name", "未知");
                            student.number = jsonObject.optInt("number", 0);
                            student.code = jsonObject.optInt("code", 0);
                            student.student_id = jsonObject.optInt("studentId", 0);
                            student.Ptteacher = jsonObject.optString("ptTeacher", "未知");
                            student.Zone = jsonObject.optInt("zone", 0);
                            student.grade = jsonObject.optInt("grade", 0);

                            // 标记数据已加载
                            studentDataLoaded = true;

                            // 在UI线程中完成密码验证
                            runOnUiThread(() -> {
                                // 获取用户输入的密码
                                String password = mEtPassword.getText().toString().trim();

                                // 验证服务器返回的code是否与密码匹配
                                if (password.equals(String.valueOf(student.code))) {
                                    Toast.makeText(Stu_Login_Activity.this, "登录成功!", Toast.LENGTH_SHORT).show();
                                    Intent intent = new Intent(Stu_Login_Activity.this, Activity_Student_Homepage.class);
                                    startActivity(intent);
                                } else {
                                    Toast.makeText(Stu_Login_Activity.this, "密码错误", Toast.LENGTH_SHORT).show();
                                }

                                isNetworkVerificationInProgress = false; // 重置网络验证标志
                            });
                        } else {
                            runOnUiThread(() -> {
                                Toast.makeText(Stu_Login_Activity.this, "未找到学生信息", Toast.LENGTH_SHORT).show();
                                tv_result.setText("未找到学生信息");
                                isNetworkVerificationInProgress = false;
                            });
                        }
                    } catch (JSONException e) {
                        runOnUiThread(() -> {
                            Toast.makeText(Stu_Login_Activity.this, "JSON解析失败", Toast.LENGTH_SHORT).show();
                            tv_result.setText("JSON解析失败\n原始数据:" + resp);
                            isNetworkVerificationInProgress = false;
                        });
                    }
                } else {
                    runOnUiThread(() -> {
                        Toast.makeText(Stu_Login_Activity.this, "服务器返回错误: " + response.code(), Toast.LENGTH_SHORT).show();
                        tv_result.setText("服务器返回错误\n状态码:" + response.code());
                        isNetworkVerificationInProgress = false;
                    });
                }
                response.close(); // 确保关闭响应资源
            }
        });
    }

    /*onCheck确认密码事件*/
    private boolean onCheck(View v) {
        // 需要获取输入的用户名和密码
        String username = mEtUsername.getText().toString();
        String password = mEtPassword.getText().toString();

        // 弹出内容设置
        String ok = "登陆成功!";
        String fail = "密码或用户名有误,登陆失败。";

        // 1. 检查本地测试账号(特殊硬编码账号)
        if (username.equals(strUsername) && password.equals(strPassword)) {
            Toast.makeText(getApplicationContext(), ok, Toast.LENGTH_SHORT).show();
            return true;
        }

        // 2. 检查是否已加载学生数据(网络验证已成功且加载过数据)
        if (studentDataLoaded && password.equals(String.valueOf(student.code))) {
            Toast.makeText(getApplicationContext(), ok, Toast.LENGTH_SHORT).show();
            return true;
        }

        // 3. 尝试网络验证
        doGet();

        Toast toastCenter = Toast.makeText(Stu_Login_Activity.this, "正在验证账号...", Toast.LENGTH_SHORT);
        toastCenter.setGravity(Gravity.CENTER_HORIZONTAL, 0, 0);
        toastCenter.show();

        return false; // 返回false,等待网络验证回调处理登录结果
    }

    /*保存密码*/
    private void configLoginInfo(boolean checked) {
        SharedPreferences.Editor editor = loginPreferences.edit();
        editor.putBoolean(CheckOrNot, mChbRemember.isChecked());
        if (checked) {
            editor.putString(UserName, mEtUsername.getText().toString());
            editor.putString(PassWord, mEtPassword.getText().toString());
        } else {
            editor.remove(UserName).remove(PassWord);
        }
        editor.commit();
    }

    /*登录临时信息读取*/
    private Map<String, String> readLogin() {
        Map<String, String> map = new HashMap<>();
        String username = loginPreferences.getString(UserName, "");
        String password = loginPreferences.getString(PassWord, "");
        map.put(UserName, username);
        map.put(PassWord, password);
        return map;
    }

    // PermissionInfo
    private static final int REQUEST_PERMISSIONS = 9527;

    public void checkingAndroidVersion() {
        Toast toastCenter = Toast.makeText(getApplicationContext(), "当前安卓版本为" + Build.VERSION.SDK_INT, Toast.LENGTH_SHORT);
        toastCenter.show();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            // Android6.0及以上先获取权限再定位
            requestPermission();
        } else {
            // Android6.0以下直接定位
            Toast toastCenter1 = Toast.makeText(getApplicationContext(), "ok", Toast.LENGTH_SHORT);
            toastCenter1.show();
        }
    }

    @AfterPermissionGranted(REQUEST_PERMISSIONS)
    public void requestPermission() {
        String[] permissions = {
                android.Manifest.permission.ACCESS_COARSE_LOCATION,
                android.Manifest.permission.ACCESS_FINE_LOCATION,
                android.Manifest.permission.READ_PHONE_STATE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
        };

        if (EasyPermissions.hasPermissions(this, permissions)) {
            // true 有权限 开始定位
            Toast info = Toast.makeText(getApplicationContext(), "已获得权限,可以定位啦!", Toast.LENGTH_SHORT);
            info.show();
        } else {
            // false 无权限
            EasyPermissions.requestPermissions(this, "需要权限", REQUEST_PERMISSIONS, permissions);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        // 设置权限请求结果
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }
}

stu_log.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".Stu_Login_Activity">

    <ImageView
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:src="@drawable/kids"
        android:layout_marginTop="200dp"
        android:layout_gravity="center_horizontal"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:text="学 生 登 录 界 面"
        android:layout_marginTop="10dp"
        android:gravity="center_horizontal"
        android:layout_gravity="center_horizontal"/>
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_marginTop="65dp"
        android:layout_gravity="center_horizontal">
        <EditText
            android:id="@+id/ed_stuLogin_username"
            android:layout_width="350dp"
            android:layout_height="48dp"
            android:layout_gravity="center"
            android:hint="Username"
            android:maxLines="1"
            android:textSize="16sp"/>
        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <EditText
                android:id="@+id/et_stuLogin_Password"
                android:layout_width="350dp"
                android:layout_height="48dp"
                android:layout_marginTop="6dp"
                android:layout_gravity="center_horizontal"
                android:hint="Password"
                android:inputType="textPassword"
                android:maxLines="1"
                android:textSize="16sp"/>
            <ImageView
                android:id="@+id/img_stuLogin_eye"
                android:layout_width="32dp"
                android:layout_height="32dp"
                android:layout_alignBottom="@+id/et_stuLogin_Password"
                android:layout_alignEnd="@+id/et_stuLogin_Password"
                android:layout_alignTop="@+id/et_stuLogin_Password"
                android:src="@drawable/invisible"/>
        </RelativeLayout>
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_gravity="center_horizontal">
            <CheckBox
                android:id="@+id/cb_stuLogin_remember"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="记住密码"
                android:textSize="13sp"/>
            <CheckBox
                android:id="@+id/cb_stuLogin_autologin"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="自动登录"
                android:textSize="13sp"
                android:layout_marginLeft="76dp"/>
        </LinearLayout>
    </LinearLayout>

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal">
        <Button
            android:id="@+id/btn_stuLogin_login"
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:text="Login"
            android:textSize="17sp"/>
        <Button
            android:id="@+id/btn_stuLogin_find"
            android:layout_width="95dp"
            android:layout_height="wrap_content"
            android:text="found"
            android:textSize="17sp"
            android:layout_toRightOf="@id/btn_stuLogin_login"
            android:layout_marginLeft="5dp"/>
    </RelativeLayout>![请添加图片描述](https://i-blog.csdnimg.cn/direct/f87f0feedfa348a4a6e3add4981b8eed.png)

    <TextView
        android:id="@+id/tv_result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="5dp"
        android:textColor="@color/black"
        android:textSize="17sp" />
</LinearLayout>

之后我们通过数据库中的student_id和code来进行登录,同时还要保证网络连接的相同,就可以正常连接了。

相关推荐
倔强的石头_15 小时前
kingbase备份与恢复实战(二)—— sys_dump库级逻辑备份与恢复(Windows详细步骤)
数据库
阿巴斯甜21 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker21 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android