从零构建校园APP:Android+Spring Boot+MySQL全栈开发实战 📅 发布时间:2026/9/4 8:32:37 👁 浏览次数: 简介本资源是一套面向Android开发初学者与高校计算机专业学生的校园服务类APP完整项目解决传统Web校园服务在移动端访问不便的问题适用于课程设计、毕业设计及移动应用实训场景。压缩包共1174个文件含119个Java核心业务代码、191个编译后Class文件、61个XML布局与配置文件、87个Jar依赖库、216个PNG界面资源图及202个GIF动效素材另有SQL建表脚本、APK安装包、Tomcat部署配置及论文文档等整体31.32MB。已有2406人学习下载资源附带完整操作录像直观展示APP功能演示、MySQL数据库连接、Android Studio调试过程及前后端交互逻辑论文部分涵盖需求分析、系统设计、关键技术实现与测试方案代码结构清晰模块划分明确含用户管理、课表查询、通知公告、校园地图等便于理解MVC架构与AndroidJavaMySQLB/S混合开发全流程。1. 项目概述一个校园APP的诞生记最近几年我身边不少朋友和学弟学妹都在问想做一个能拿得出手的、功能完整的移动端项目到底该从何下手特别是对于计算机相关专业的学生来说一个结合了移动开发、后端服务和数据库的“校园APP”几乎是毕业设计或课程大作业的“标配”。今天我就以“基于安卓Android的校园APP系统设计”这个经典命题为例把我自己从零到一搭建一个完整可运行系统的全过程、踩过的坑、以及那些教科书里不会写的实操细节毫无保留地分享出来。这个项目绝不仅仅是一个“源码论文”的压缩包它背后涉及的需求分析、技术选型、架构设计、编码实现和部署测试是一套完整的工程化思维训练。无论你是想完成课业、备战面试还是单纯想提升自己的全栈开发能力这篇长文都能给你提供一条清晰的路径和一堆可以直接“抄作业”的代码与配置。简单来说我们要做的是一个运行在安卓手机上的APP它需要连接一个自己搭建的后台服务器服务器则使用MySQL数据库来存储和管理所有数据比如用户信息、课程表、校园新闻、失物招领信息等等。最终你会得到一个安装包APK、一套可运行的后台代码、一份数据库建表脚本以及所有支撑你完成论文的设计文档和思路。整个过程我会重点解释“为什么”要这么选、这么设计而不仅仅是“怎么做”。2. 核心需求与功能模块拆解在动手写一行代码之前我们必须想清楚这个APP到底要干什么。校园APP的范畴很大我们不能贪多求全而是要抓住几个核心、高频、能形成闭环的场景。基于我过往的经验和学生群体的实际反馈我建议聚焦于以下四个核心模块它们技术代表性足业务逻辑清晰且能很好地串联起安卓端、服务器和数据库。2.1 用户中心注册、登录与个人信息管理这是所有系统的基石。我们需要实现手机号或学号注册、密码登录后续可扩展短信验证码登录。用户信息至少包括学号、姓名、密码加密存储、头像、所属院系等。这个模块会涉及到安卓端的表单验证、网络请求登录接口调用、数据本地缓存如使用SharedPreferences保存登录状态以及服务器端的密码加密如使用BCrypt、会话管理如使用Token和数据库用户表的CRUD操作。注意密码绝对不能在网络中明文传输也不能在数据库中明文存储。服务器端接收到密码后应立即进行哈希加盐处理。这是一个安全底线在论文中也需要着重强调。2.2 校园资讯新闻与通知的发布与浏览模仿一个简单的校园新闻客户端。管理员如学生会或教务处可以在后台发布带标题、正文和图片的新闻或通知。普通学生用户可以在APP首页以列表形式浏览点击进入详情页。这个模块会实践安卓的RecyclerView展示列表、图片加载框架如Glide的使用、以及服务器端富文本内容或简单图文的存储与接口设计。2.3 课程表查询学生核心功能这是最能体现“校园”属性的功能。学生登录后可以查看自己当前学期的课程表以周视图的形式展示。数据来源可以是手动录入更理想的是通过模拟接口从学校教务系统同步毕业设计中常模拟此过程。这需要设计课程表数据表包含课程名、教师、上课地点、周次、星期几、节次等信息。安卓端需要实现一个自定义的课程表视图涉及到自定义View或复杂的GridView布局。2.4 失物招领校园社区互动创建一个轻量级的校园社区功能。用户可以发布失物信息或拾物信息包含物品描述、图片、丢失/拾取地点、时间、联系方式等。其他用户可以浏览列表并通过APP内联系或直接拨打电话联系发布者。这个模块综合了信息发布、列表展示、图片上传到服务器或使用第三方图床、简单的状态管理如“已找到”、“待认领”等功能。确定了这四个模块我们的技术方案选型就有了明确的靶心。接下来我们就要为这个靶心选择合适的“弓箭”。3. 技术栈选型与架构设计技术选型没有绝对的好坏只有是否适合当前场景。对于这样一个学生项目或个人练手项目我们的核心原则是主流、稳定、学习资料丰富、快速上手。基于这个原则我为你梳理出一套经过验证的技术组合。3.1 安卓客户端技术选型开发语言与IDE毫无疑问选择KotlinAndroid Studio。Kotlin已是安卓开发的官方首选语言比Java更简洁安全空指针问题处理得更好。Android Studio是谷歌官方的IDE插件生态和调试工具最完善。网络请求使用Retrofit2OkHttp3Gson这个黄金组合。Retrofit负责将HTTP API接口转化为Java/Kotlin接口用起来像调用本地方法一样简单。OkHttp是强大的底层HTTP客户端。Gson用于JSON数据的序列化与反序列化。图片加载Glide。它链式调用API非常优雅内存缓存和磁盘缓存策略高效能自动处理图片生命周期是当前最主流的图片加载库。本地数据存储轻量级数据如登录Token、用户设置用SharedPreferences需要存储复杂对象或列表时可以考虑RoomSQLite的ORM封装但鉴于我们项目数据主要来自网络SharedPreferences在初期足够使用。架构模式强烈推荐采用MVVMModel-View-ViewModel。它通过LiveData或Flow实现数据驱动UI能有效避免Activity/Fragment过于臃肿并且更利于单元测试。这是目前安卓开发的主流架构。3.2 服务器端技术选型后端框架选择Spring Boot。它是Java领域事实上的微服务标准框架能极大简化Spring应用的初始搭建和开发过程。“约定大于配置”的理念让我们能快速构建出RESTful API。它的生态极其丰富学习资源遍地都是。数据库ORM使用MyBatis-Plus。它是对MyBatis的增强在保留MyBatis灵活性的基础上提供了强大的CRUD通用接口、分页插件、代码生成器等能极大减少编写简单SQL的工作量非常适合快速开发。数据库如题所述MySQL 8.0。它是世界上最流行的开源关系型数据库之一安装、学习、运维资料都非常多。对于校园APP的数据规模性能完全足够。3.3 整体架构设计一个清晰的架构图能让你的论文和代码都更有条理。我们的系统整体上是一个典型的前后端分离架构安卓客户端前端负责UI渲染、用户交互通过HTTP/HTTPS协议调用后端API。Spring Boot后端服务提供RESTful API处理业务逻辑充当安卓客户端和数据库之间的桥梁。MySQL数据库持久化存储所有业务数据。它们之间的数据流是这样的用户在APP上操作 - 安卓端通过Retrofit发送网络请求 - 请求到达Spring Boot后端对应的Controller - Controller调用Service处理业务逻辑 - Service通过MyBatis-Plus与MySQL数据库交互 - 数据原路返回最终在APP界面上更新。这个架构的优点是职责分离安卓端和后端可以独立开发和部署也便于后续扩展例如未来可以很容易地开发一个iOS版或小程序版它们共用同一套后端API。4. 数据库设计与核心表结构数据库设计是系统的“地基”设计得好后续开发事半功倍。根据我们的四个核心模块我设计了以下五张核心表。这里我会给出关键的字段和说明你可以直接用它来创建你的数据库。表1用户表 (sys_user)这是系统的核心表存储所有用户信息。CREATE TABLE sys_user ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键ID, student_id varchar(20) NOT NULL COMMENT 学号唯一, username varchar(50) DEFAULT NULL COMMENT 姓名, password varchar(255) NOT NULL COMMENT 加密后的密码, avatar varchar(500) DEFAULT NULL COMMENT 头像URL, college varchar(100) DEFAULT NULL COMMENT 学院, major varchar(100) DEFAULT NULL COMMENT 专业, phone varchar(20) DEFAULT NULL COMMENT 手机号, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_student_id (student_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;实操心得password字段长度一定要设得足够长如255因为BCrypt等加密算法生成的哈希串很长。使用utf8mb4字符集可以支持存储Emoji表情。create_time和update_time是审计字段建议每张表都加上便于排查问题。表2新闻资讯表 (news_info)存储校园新闻和通知。CREATE TABLE news_info ( id bigint NOT NULL AUTO_INCREMENT, title varchar(200) NOT NULL COMMENT 标题, content text COMMENT 正文内容可存HTML或Markdown, cover_image varchar(500) DEFAULT NULL COMMENT 封面图URL, publisher_id bigint DEFAULT NULL COMMENT 发布者ID关联用户表, publisher_name varchar(50) DEFAULT NULL COMMENT 发布者姓名冗余存储避免连表查询, view_count int DEFAULT 0 COMMENT 浏览次数, is_top tinyint(1) DEFAULT 0 COMMENT 是否置顶0否1是, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT新闻资讯表;注意content字段类型为TEXT可以存储大段文字。publisher_name是典型的“空间换时间”设计在列表展示新闻时我们通常需要显示发布者名字如果每次都去连sys_user表查询会影响性能。直接冗余存储在这里查询效率更高。表3课程表 (course_schedule)存储学生的课程信息。CREATE TABLE course_schedule ( id bigint NOT NULL AUTO_INCREMENT, student_id bigint NOT NULL COMMENT 学生ID关联用户表, course_name varchar(100) NOT NULL COMMENT 课程名称, teacher varchar(50) DEFAULT NULL COMMENT 任课教师, location varchar(100) DEFAULT NULL COMMENT 上课地点, day_of_week tinyint NOT NULL COMMENT 星期几1-7, start_section tinyint NOT NULL COMMENT 开始节次, end_section tinyint NOT NULL COMMENT 结束节次, start_week tinyint DEFAULT NULL COMMENT 起始周, end_week tinyint DEFAULT NULL COMMENT 结束周, single_double tinyint DEFAULT 0 COMMENT 单双周0全部1单周2双周, semester varchar(20) DEFAULT NULL COMMENT 学期如2023-2024-1, PRIMARY KEY (id), KEY idx_student_semester (student_id,semester) COMMENT 根据学生和学期查询课程表的索引 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT课程表;关键点解析课程表设计是难点。这里采用“离散化”存储即把一学期内每周重复的课程拆解成一条条基于“星期几”和“节次”的记录。day_of_week表示周一至周日。start_section和end_section表示从第几节上到第几节。start_week和end_week、single_double字段共同定义了这门课在哪几周有。这种设计虽然会使得同一门课可能产生多条记录如果每周上多次但查询和前端渲染时逻辑非常清晰直接。表4失物招领物品表 (lost_found_item)存储失物和招领信息。CREATE TABLE lost_found_item ( id bigint NOT NULL AUTO_INCREMENT, type tinyint NOT NULL COMMENT 类型1失物2招领, title varchar(100) NOT NULL COMMENT 物品标题, description text COMMENT 详细描述, image_urls varchar(2000) DEFAULT NULL COMMENT 图片URL多个用逗号分隔, location varchar(200) DEFAULT NULL COMMENT 丢失/拾取地点, event_time datetime DEFAULT NULL COMMENT 丢失/拾取时间, contact varchar(100) NOT NULL COMMENT 联系方式, publisher_id bigint NOT NULL COMMENT 发布者ID, status tinyint DEFAULT 0 COMMENT 状态0待处理1已找到/已归还2已关闭, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_type_status (type,status) COMMENT 按类型和状态查询的索引 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT失物招领表;实操心得image_urls字段用于存储多张图片。这里采用简单粗暴的用逗号分隔多个URL字符串的方式。对于毕业设计级别的项目这完全可行。如果追求更规范可以拆分成单独的一张图片表。status字段用于管理物品状态是一个简单的状态机。有了清晰的表结构我们就可以开始搭建开发环境并让前后端跑起来了。5. 后端Spring Boot服务搭建与核心API实现后端是整个系统的大脑我们先把它搭建起来。这里我假设你已经有基本的Java和Maven基础。5.1 项目初始化与依赖配置使用 Spring Initializr 网站或IDE内的Spring Initializr插件快速生成项目。关键依赖选择Spring Web用于构建Web API。MyBatis Framework数据库ORM框架。MySQL DriverMySQL数据库驱动。Lombok简化Java Bean代码的神器强烈推荐。生成项目后在pom.xml中手动加入MyBatis-Plus的依赖dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version最新版本/version !-- 例如 3.5.3 -- /dependency5.2 核心配置详解在application.yml或application.properties中配置数据库连接和MyBatis-Plusserver: port: 8080 # 服务端口 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/campus_app_db?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai username: root password: your_password mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL调试用 global-config: db-config: logic-delete-field: deleted # 全局逻辑删除字段名如果要用 logic-delete-value: 1 # 逻辑已删除值 logic-not-delete-value: 0 # 逻辑未删除值 mapper-locations: classpath:mapper/*.xml # XML映射文件位置注意serverTimezoneAsia/Shanghai非常重要能避免数据库时区问题导致的日期时间错误。log-impl配置在开发阶段非常有用可以实时看到MyBatis-Plus执行的SQL语句方便调试。5.3 用户登录API实现示例我们以实现用户登录为例展示一个完整的API从Controller到Service到Mapper的流程。首先创建用户实体类User.java使用Lombok简化代码Data TableName(sys_user) // 指定对应表名 public class User { TableId(type IdType.AUTO) // 主键自增 private Long id; private String studentId; private String username; private String password; // 存储的是加密后的密码 private String avatar; // ... 其他字段及getter/setter已被Data注解包含 }创建Mapper接口UserMapper.java继承MyBatis-Plus的BaseMapper即可获得通用的CRUD方法Mapper public interface UserMapper extends BaseMapperUser { // 可以在此定义自定义的SQL查询方法 // 例如根据学号查询用户 User selectByStudentId(Param(studentId) String studentId); }对应的XML映射文件UserMapper.xml如果使用注解方式可省略但复杂SQL建议用XML?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.yourpackage.mapper.UserMapper select idselectByStudentId resultTypecom.yourpackage.entity.User SELECT * FROM sys_user WHERE student_id #{studentId} /select /mapper创建Service层UserService.javaService public class UserService { Autowired private UserMapper userMapper; public User login(String studentId, String rawPassword) { // 1. 根据学号查询用户 User user userMapper.selectByStudentId(studentId); if (user null) { throw new RuntimeException(用户不存在); } // 2. 验证密码 (使用BCryptPasswordEncoder) BCryptPasswordEncoder encoder new BCryptPasswordEncoder(); if (!encoder.matches(rawPassword, user.getPassword())) { throw new RuntimeException(密码错误); } // 3. 登录成功生成Token这里简化实际可用JWT // String token JwtUtil.generateToken(user.getId(), user.getStudentId()); // 返回用户信息注意剔除密码字段 user.setPassword(null); return user; } }创建Controller层UserController.java定义RESTful APIRestController RequestMapping(/api/user) public class UserController { Autowired private UserService userService; PostMapping(/login) public Result login(RequestBody LoginForm form) { // LoginForm是一个简单的DTO包含studentId和password字段 try { User user userService.login(form.getStudentId(), form.getPassword()); // 构造返回结果包含用户信息和Token MapString, Object data new HashMap(); data.put(user, user); data.put(token, 模拟的Token字符串); // 实际应替换为JWT Token return Result.success(登录成功, data); } catch (RuntimeException e) { return Result.error(e.getMessage()); } } }这里的Result是一个自定义的统一响应体类包含code、msg、data字段便于前端处理。至此一个完整的登录API就完成了。你可以使用Postman等工具测试POST http://localhost:8080/api/user/login这个接口。其他模块的API如新闻查询、课程表获取、失物发布其代码结构与此类似都是“Controller接收请求 - Service处理业务 - Mapper操作数据库”的模式。6. 安卓客户端开发与关键功能实现后端API准备好后我们开始在安卓端“搭积木”。我们采用MVVM架构使用Jetpack组件让代码更清晰、更健壮。6.1 项目初始化与网络层配置首先在项目的build.gradle文件中添加必要的依赖dependencies { // ... 其他基础依赖 // 网络请求 implementation com.squareup.retrofit2:retrofit:2.9.0 implementation com.squareup.retrofit2:converter-gson:2.9.0 implementation com.squareup.okhttp3:okhttp:4.11.0 implementation com.squareup.okhttp3:logging-interceptor:4.11.0 // 网络日志拦截器 // 图片加载 implementation com.github.bumptech.glide:glide:4.15.1 kapt com.github.bumptech.glide:compiler:4.15.1 // Kotlin注解处理器 // MVVM Jetpack implementation androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1 implementation androidx.lifecycle:lifecycle-livedata-ktx:2.6.1 implementation androidx.activity:activity-ktx:1.7.0 // 协程 implementation org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1 }然后创建Retrofit的单例实例RetrofitClient.ktobject RetrofitClient { private const val BASE_URL http://你的电脑IP:8080/ // 注意不能用localhost要用本机在局域网中的IP private val okHttpClient OkHttpClient.Builder() .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) // 打印请求日志 .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .build() private val retrofit Retrofit.Builder() .baseUrl(BASE_URL) .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create()) .build() fun T createService(serviceClass: ClassT): T retrofit.create(serviceClass) }踩坑提醒在安卓模拟器或真机上访问本地后端服务时BASE_URL不能是http://localhost:8080或http://127.0.0.1:8080因为这在手机环境里指向的是手机自己。你必须将localhost替换为你开发电脑在局域网中的实际IP地址如http://192.168.1.100:8080。同时确保电脑防火墙允许8080端口的入站连接。6.2 实现用户登录功能MVVM模式我们以登录功能为例展示MVVM在安卓端的完整实践。1. 定义数据模型和API接口创建User.kt数据类对应后端的用户实体。 创建ApiService.kt接口定义网络请求interface ApiService { POST(api/user/login) suspend fun login(Body loginForm: LoginForm): ResultLoginResponse // Result是统一响应体LoginResponse包含user和token }2. 创建Repository数据仓库Repository负责协调多个数据源网络、本地数据库等。class UserRepository(private val apiService: ApiService) { suspend fun login(studentId: String, password: String): ResultLoginResponse { return try { val response apiService.login(LoginForm(studentId, password)) if (response.isSuccess()) { // 假设Result有isSuccess方法 // 登录成功可以在这里将token保存到SharedPreferences saveTokenToLocal(response.data?.token) } response } catch (e: Exception) { Result.error(网络请求失败: ${e.message}) } } private fun saveTokenToLocal(token: String?) { // 使用SharedPreferences保存token } }3. 创建ViewModelViewModel持有UI相关的数据并暴露给UI层观察。class LoginViewModel(private val repository: UserRepository) : ViewModel() { // 使用LiveData来观察登录状态和结果 private val _loginResult MutableLiveDataResultLoginResponse() val loginResult: LiveDataResultLoginResponse _loginResult fun login(studentId: String, password: String) { viewModelScope.launch { _loginResult.value Result.loading() // 发送加载状态 val result repository.login(studentId, password) _loginResult.value result // 发送最终结果 } } }4. 在Activity/Fragment中观察ViewModelclass LoginActivity : AppCompatActivity() { private lateinit var viewModel: LoginViewModel private lateinit var binding: ActivityLoginBinding // ViewBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding ActivityLoginBinding.inflate(layoutInflater) setContentView(binding.root) // 初始化ViewModel val factory ViewModelFactory(UserRepository(RetrofitClient.createService(ApiService::class.java))) viewModel ViewModelProvider(this, factory)[LoginViewModel::class.java] binding.btnLogin.setOnClickListener { val studentId binding.etStudentId.text.toString() val password binding.etPassword.text.toString() if (validateInput(studentId, password)) { viewModel.login(studentId, password) } } // 观察登录结果 viewModel.loginResult.observe(this) { result - when (result.status) { Status.LOADING - showLoading() Status.SUCCESS - { hideLoading() result.data?.let { // 登录成功跳转到主页面 startActivity(Intent(this, MainActivity::class.java)) finish() } } Status.ERROR - { hideLoading() Toast.makeText(this, result.message ?: 登录失败, Toast.LENGTH_SHORT).show() } } } } }通过以上步骤我们就完成了一个结构清晰、职责分离的登录功能。其他功能模块如新闻列表、课程表展示都可以遵循同样的MVVM模式进行开发。6.3 课程表UI实现技巧课程表UI是前端的一个小挑战。一个常见的实现方式是使用一个自定义的GridView或者直接使用Canvas绘制。这里提供一个使用RecyclerView多类型ViewHolder的简化思路数据准备将从后端获取的一周课程列表转换成一个7天x N最大节次如12的二维数组或列表。没有课的位置为null。布局设计最左侧一列显示节次1,2,3...最上方一行显示星期一二三...中间区域是课程格子。Adapter实现RecyclerView.Adapter需要处理多种类型的ViewHolder表头星期、表侧节次、课程格子。根据position判断当前项应该渲染成哪种类型。课程格子视图每个课程格子是一个CardView里面显示课程名、地点。需要根据课程的start_section和end_section来计算这个格子应该占据几行span。这可以通过GridLayoutManager的setSpanSizeLookup方法来实现。这个实现有一定复杂度但能很好地锻炼你对RecyclerView和自定义布局的理解。如果时间紧张也可以考虑使用一些开源的课程表控件。7. 项目集成、测试与常见问题排查当后端API和安卓端主要页面都开发完毕后就到了激动人心的联调测试阶段。这个阶段往往是问题集中爆发的时期。7.1 后端服务部署与启动确保你的MySQL服务已启动并已创建好数据库campus_app_db和所有表结构。在IDE中直接运行Spring Boot项目的main方法或在项目根目录下执行mvn spring-boot:run命令。看到控制台输出类似Started Application in 5.123 seconds (JVM running for 5.789)的日志说明启动成功。打开浏览器访问http://localhost:8080如果配置了简单的接口如/hello看是否有响应。更推荐使用Postman直接测试你的API接口如POST http://localhost:8080/api/user/login。7.2 安卓真机调试与网络连接IP地址问题这是最常见的坑。确保安卓代码中的BASE_URLIP地址正确且手机和电脑在同一个局域网连接同一个Wi-Fi。网络权限在AndroidManifest.xml中务必添加网络权限uses-permission android:nameandroid.permission.INTERNET /HTTP明文流量限制Android 9如果你的后端使用HTTP非HTTPS在Android 9及以上版本默认禁止明文流量。解决方法有两种推荐在res/xml下创建network_security_config.xml文件?xml version1.0 encodingutf-8? network-security-config domain-config cleartextTrafficPermittedtrue domain includeSubdomainstrue你的电脑IP/domain /domain-config /network-security-config然后在AndroidManifest.xml的application标签内引用android:networkSecurityConfigxml/network_security_config临时在AndroidManifest.xml的application标签内直接设置android:usesCleartextTraffictrue但这种方法安全性较低仅用于开发测试。7.3 常见问题与排查技巧实录下面我将遇到过的典型问题整理成表方便你快速对照排查问题现象可能原因排查步骤与解决方案安卓APP点击登录无反应或报“Network Error”1. 后端服务未启动。2. IP地址或端口错误。3. 手机和电脑不在同一网络。4. 电脑防火墙阻止了连接。1. 检查后端控制台是否启动成功。2. 在电脑上用curl或浏览器访问http://电脑IP:8080/api/health需自己写个健康检查接口看是否通。3. 手机浏览器访问同一个地址看是否能打开。4. 临时关闭电脑防火墙测试。登录接口返回“404 Not Found”1. 请求URL路径错误。2. 请求方法GET/POST不对。1. 用Postman对比测试确保URL、方法、请求体完全一致。2. 检查后端RequestMapping和PostMapping注解的路径。登录接口返回“500 Internal Server Error”后端代码运行时异常如空指针、数据库连接失败。1.查看后端控制台日志这是最重要的排错信息源异常堆栈会打印在这里。2. 检查数据库连接配置、SQL语句是否正确。能收到响应但状态码是200数据解析失败如Gson解析错误1. 后端返回的JSON格式与安卓端定义的Result或LoginResponse类结构不匹配。2. 字段名或类型不一致。1. 使用Retrofit的HttpLoggingInterceptor查看原始响应JSON。2. 对比后端返回的JSON字符串和安卓端数据类的定义确保字段名、嵌套结构完全一致。可以使用SerializedName注解处理字段名映射。课程表页面列表为空1. 后端查询课程表的API逻辑错误没查到数据。2. 安卓端请求参数如学生ID、学期传错了。3. 安卓端RecyclerView的Adapter设置有问题。1. 用Postman直接调用课程表API看是否返回数据。2. 在安卓端请求成功的回调里打印或调试查看返回的数据对象。3. 检查Adapter的getItemCount()是否正确以及onBindViewHolder里是否设置了数据。图片加载不出来Glide1. 图片URL是错的或为空。2. URL是HTTP而Android 9限制了明文流量见上文。3. 图片服务器问题。1. 打印图片URL在电脑浏览器中直接打开看能否访问。2. 配置network_security_config.xml。3. 使用Glide的错误监听器.listener()来查看具体错误信息。发布失物信息失败特别是带图片1. 文件上传接口有问题。2. 安卓端未正确处理文件选择和Multipart表单上传。1. 先用Postman测试文件上传接口是否正常。2. 安卓端使用MultipartBody.Part来包装文件并与其他表单字段一起通过Multipart注解上传。确保文件路径正确且有读取存储的权限。7.4 性能与优化小贴士图片优化使用Glide它自带了强大的缓存和图片压缩功能。对于用户上传的图片后端最好也做一次压缩再存储或返回缩略图URL。列表分页新闻列表、失物招领列表一定要做分页后端使用MyBatis-Plus的Page对象前端使用RecyclerView的滑动监听或Paging 3库来实现上拉加载更多。这是避免OOM和提升用户体验的关键。数据缓存对于不常变的数据如学院、专业列表可以在安卓端首次加载后缓存到SharedPreferences或数据库中减少不必要的网络请求。代码混淆与加固项目完成后在打包Release APK前记得在build.gradle中启用代码混淆minifyEnabled并使用一些第三方加固平台对APK进行加固防止反编译。走到这一步你的校园APP系统已经从一个想法变成了一个可以真机安装、实际运行的应用了。从数据库设计到API编写再到安卓界面交互你完整地走通了一个全栈项目的开发流程。这份经历和这个可运行、有论文、有源码的项目无论是用于毕业答辩还是充实个人作品集都极具分量。本文还有配套的精品资源点击获取