简介一份基于Java实现的智慧校园Android客户端与管理系统源码项目面向高校师生、Java/Android方向在校学生及毕业设计者。项目覆盖校园资讯浏览、点赞评论与分享支持个人任务提醒、进度管理以及团队任务安排、申请与资讯发布等常见智慧校园业务适合用于毕设、课设或初期项目演示。压缩包共763个文件包含218个Java源码、210个XML配置与界面、260张PNG图片资源以及so库、jar依赖、Gradle构建脚本和说明文档等整体大小26.8MB目录结构清晰便于按模块检索和二次开发。目前已有106人学习下载源码经测试运行成功功能稳定答辩平均分96分。下载后可打开说明文档快速导入运行中遇到问题可私聊远程教学也可在现有基础上扩展功能满足课程设计、毕业设计或项目立项演示需要。1. 智慧校园这题目难不在“智慧”在“客户端和服务端怎么对齐”很多同学拿到《基于Java实现智慧校园Android客户端及管理系统》这种高分毕设题目第一反应是去网上找一个“课程表App”改一改。真动手才发现课程表只是插在名单里的一个页面整个项目真正重的是一套“双端系统”——Android客户端跑在学生手机上Java管理系统跑在教务老师电脑上两边靠HTTP接口和JSON数据对齐。智慧校园的范围很广常见会拆成用户登录、课表查询、通知公告、选课、成绩、考勤几条线。新手容易卡在“客户端和服务端”怎么联动数据库表建好了但接口没定接口定好了但客户端没做加载态演示时一断网就翻车。这篇文章按“后端管理系统→Android客户端→数据同步→踩坑记录→答辩验证”的顺序把它拆成可以直接复现的落地步骤适合做Java课程设计、毕业设计或想系统练一遍前后端链路的人。2. 先立后端管理系统数据库、接口、权限三个骨架怎么定智慧校园的管理系统是“总装厂”教师发公告、排课表、管理员加学生账号都在管理端完成。Android客户端只负责把结果展示出来。管理端按Web后台来做技术上还是Java——Spring Boot包一层Controller和Service和Android客户端共用同一套数据库和业务逻辑。很多人先写客户端再补后端结果接口改一次客户端跟着改一次来回折腾。我一般会先把管理端这三点定下来数据库表关系、登录接口和统一返回格式、会话状态方案。这三件事决定客户端后面好不好写。2.1 表设计先想清楚学生、班级、课程、课表、通知之间的三张核心表先看最常用的角色学生、老师、管理员。一次登录要同时服务三种人用户表不能和“学生信息”混在同一个表里。常见做法是一张sys_user只存账号、密码、角色、姓名再按角色拆学生表和老师表。比如学生表只存学号、班级、入学年份老师表只存工号、学院、职称。下面这段DDL是跑通的最小集合放在MySQL 8.x下能直接执行CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, role TINYINT NOT NULL DEFAULT 2 COMMENT 0管理员 1老师 2学生, real_name VARCHAR(50) NOT NULL, status TINYINT NOT NULL DEFAULT 1 COMMENT 1正常 0禁用, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE student ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, student_no VARCHAR(20) NOT NULL UNIQUE, class_name VARCHAR(50) NOT NULL, enroll_year VARCHAR(10) NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE course ( id BIGINT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(100) NOT NULL, teacher_user_id BIGINT NOT NULL COMMENT 关联 sys_user 里的老师, week_day TINYINT NULL COMMENT 星期几 1-7, start_section TINYINT NULL COMMENT 第几节开始, end_section TINYINT NULL COMMENT 第几节结束, classroom VARCHAR(50) NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;课程表只存“课程开课信息”还不够学生和课程是多对多关系需要一张student_course关系表记录哪个学生选了哪门课。否则一个学生查课表时课程表上不知道放谁的课。关系表的最小字段是id、student_id、course_id再加一个选课时间做时间戳。这四张表sys_user、student、course、student_course就能支撑登录、课表、角色区分这三个最基础功能。为什么要这样拆同一个用户名只能登录一次但“登录后是谁”由sys_user.role决定。比如Android客户端打开登录页输入账号密码后端校验通过后根据role返回不同菜单学生看到“我的课表”老师看到“我要代课”管理员看到“用户管理”。这个设计也是Java面试题里常问的“用户权限模型”最小实现学会了后面加菜单权限表也不迷路。2.2 用 Spring Boot 把管理端接口立起来登录接口和统一返回体管理端接口的写法不止一种最常见的是Spring Boot MyBatis-Plus。先约定统一返回体所有接口都返回同一个JSON结构Android客户端解析时只需要关心code、msg、data三个字段。好处是接口多了以后客户端网络层不需要为每个接口写单独的解析逻辑。下面是一个最简单的Result类public class ResultT { private Integer code; private String msg; private T data; public static T ResultT ok(T data) { ResultT r new Result(); r.setCode(200); r.setMsg(ok); r.setData(data); return r; } public static T ResultT fail(String msg) { ResultT r new Result(); r.setCode(500); r.setMsg(msg); return r; } // getter/setter 省略 }逻辑说明ok和fail是两个静态工厂方法一个代表业务成功一个代表业务失败。注意业务成功永远是code200HTTP状态码也保持200这样客户端只看code就行。失败时code500HTTP状态码依然可以返回200把业务错误交给客户端弹提示。这样做的好处是网络层和处理层分离不会出现“接口404但客户端误以为是账号被禁用”的问题。登录接口写起来也不复杂。Controller接收JSON用户名密码Service里比对数据库成功就发一个token。这里贴一个简化版PostMapping(/login) public ResultLoginVO login(RequestBody LoginDTO dto) { // 1. 查用户 SysUser user sysUserService.findByUsername(dto.getUsername()); if (user null) { return Result.fail(用户不存在); } // 2. 比对密码实际项目中用 BCrypt 加密存储 String md5 DigestUtils.md5DigestAsHex(dto.getPassword().getBytes()); if (!user.getPassword().equals(md5)) { return Result.fail(密码错误); } if (user.getStatus() 0) { return Result.fail(账号已被禁用); } // 3. 返回 token 和用户基础信息 LoginVO vo new LoginVO(); vo.setToken(UUID.randomUUID().toString().replace(-, )); vo.setRealName(user.getRealName()); vo.setRole(user.getRole()); return Result.ok(vo); }参数说明第一username在数据库里有唯一约束登录查询直接走索引第二密码用MD5只是演示真实项目一般用BCrypt因为MD5有彩虹表风险论文里写“密码使用BCrypt加密存储”是加分项第三role决定客户端菜单status控制账号能否登录这两项是后续所有权限判断的根基。如果你是课程设计建议再补一个登出接口把Redis里的token删掉。注意MySQL的role字段是TINYINTJava实体里对应Integer前端判断角色时不建议直接用魔法数字0/1/2建议定义一个RoleConst常量类。否则管理端写“2”代表学生客户端也写“2”过两周自己都忘了2是什么意思。2.3 为什么要配 Redis 做会话客户端频繁轮询时后端不背锅登录校验通过后客户端每次请求都要带着身份信息。最常见的做法有两个一个是后端用Session一个是自己发token存Redis。智慧校园这种场景Android客户端的学生端会频繁拉课表和公告一旦同时在线人数上来Session内存会越堆越高。我一般会用Redis做token的存储层Redis天然带过期时间内存管理比传统Session清晰。先做一个简单的TokenServiceService public class TokenService { private static final String TOKEN_PREFIX smartcampus:token:; Autowired private StringRedisTemplate redisTemplate; public void saveToken(String token, Long userId, long expireSeconds) { redisTemplate.opsForValue().set(TOKEN_PREFIX token, String.valueOf(userId), expireSeconds, TimeUnit.SECONDS); } public Long getUserId(String token) { String value redisTemplate.opsForValue().get(TOKEN_PREFIX token); return value null ? null : Long.parseLong(value); } public void deleteToken(String token) { redisTemplate.delete(TOKEN_PREFIX token); } }逻辑说明这个类表面上只是set/get字符串但它解决了两个关键问题一是token和userId直接映射客户端不好伪造二是管理端重置密码时能主动删token让指定会话失效。参数说明token过期时间设成2小时7200秒针对“记住我”场景再单独生成一个长期token客户端和短期token一起用。实际排查时如果发现“一会儿能登录一会儿不能”先拿Redis客户端可视化工具看一眼key是否还存在、ttl还剩多少。很多毕设项目做不出来不是代码问题而是Redis服务没启动或者连接配置写错了。Redis和MySQL的连接配置建议用单独的application-dev.yml别把数据库密码硬编码在代码里。把这块梳理明白Android和Java面试题里最常见的会话管理也就基本稳了。3. 把 Android 客户端做成真客户端Java 原生网络层与界面刷新的最小实现管理端定了接口Android端的工作就是“把接口当数据源”。很多人写Android客户端容易犯一个毛病把业务逻辑写在Activity里一个文件几百行网络请求、数据解析、界面刷新全揉在一起。真正要复现的路径是先封装网络层再处理登录态最后才是界面展示。这三层分开后面加新页面就不怕。3.1 网络层OkHttp 封装、响应解析和错误提示的区分Android原生开发里OkHttp是绕不开的库。网络层要做的不是“发一个请求”而是把“请求成功但业务失败”和“请求失败”分开。业务上密码错误、账号禁用都是code500但客户端不能当网络错误处理否则学生看到的提示永远是“网络异常”完全不知道密码错了。下面是一个适合当通讯底层的最小工具类public class HttpUtil { private static final int CONNECT_TIMEOUT 15; private static final int READ_TIMEOUT 20; private static final OkHttpClient client new OkHttpClient.Builder() .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS) .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS) .build(); public static void post(String url, String jsonBody, String token, Callback callback) { RequestBody body RequestBody.create( MediaType.parse(application/json; charsetutf-8), jsonBody); Request request new Request.Builder() .url(url) .header(Authorization, token null ? : token) .post(body) .build(); client.newCall(request).enqueue(callback); } }参数说明connectTimeout 15秒解决“服务器不响应”的卡顿readTimeout 20秒给慢接口留余量。超时值不宜太小学生宿舍Wi-Fi不稳定10秒内连不上直接超时体验很糟糕。Authorization头是后端习惯用的字段名你也可以叫access-token但客户端和后端必须保持一致。因为OkHttp的enqueue回调在子线程回调里不能直接改TextView你需要用runOnUiThread或者Handler切回主线程。这块是新手翻车重灾区从回调里直接findViewById然后setText一运行就崩。最小正确写法是先判断code再切主线程刷新。HttpUtil.post(loginUrl, jsonBody, null, new Callback() { Override public void onFailure(Call call, IOException e) { runOnUiThread(() - Toast.makeText(LoginActivity.this, 网络异常请检查连接, Toast.LENGTH_SHORT).show()); } Override public void onResponse(Call call, Response response) throws IOException { ResultDTO dto new Gson().fromJson(response.body().string(), ResultDTO.class); runOnUiThread(() - { if (dto.getCode() 200) { startActivity(new Intent(LoginActivity.this, MainActivity.class)); finish(); } else { Toast.makeText(LoginActivity.this, dto.getMsg(), Toast.LENGTH_SHORT).show(); } }); } });这里用Gson做JSON反序列化ResultDTO的三个字段和后端Result类对应。注意Gson默认将int解析成int后端code200时返回的data字段可能是对象也可能是数组所以ResultDTO里的data要用Object类型具体业务类型再二次转换。这一步不写清楚后面每个接口都得写一套解析越到后面越头疼。网络层还要留一条调试路径在OkHttpClient.Builder里加一个HttpLoggingInterceptorLogcat里能看到完整请求和响应。不要等到出bug才加日志客户端和后端联调时打开日志一眼就能看出是参数传错还是后端报错。3.2 登录页与主界面的状态切换从 SharedPreferences 到内存态客户端登录成功后不能只跳转页面还要记住“我是谁”。Android常见的本地存储是SharedPreferences存token、用户角色、姓名。下次启动时先读token有token直接进主界面没有则停在登录页。SharedPreferences sp getSharedPreferences(user_info, MODE_PRIVATE); String token sp.getString(token, ); if (token.isEmpty()) { startActivity(new Intent(this, LoginActivity.class)); } else { startActivity(new Intent(this, MainActivity.class)); } finish();这段代码在SplashActivity的onCreate里执行。注意token存本地是方便但别只存token不存role。智慧校园客户端登录后学生、老师、管理员的界面入口不一样学生看到课表、通知、选课老师看到教学任务管理员看到用户管理。判断入口用的就是SharedPreferences里的role字段。role选错后面接口传参也会跟着错。主界面一般用四个底部Tab首页、课表、通知、我的。首页放今日课程和公告课表页用RecyclerView展示周一到周日课程通知页拉公告列表我的页显示姓名、学号和退出按钮。用Fragment ViewPager还是BottomNavigationView都行关键是每个Fragment只在onResume里拉数据避免切Tab时每次都走网络。这里可以加一个“仅首次创建时加载”的布尔标记减少流量消耗。退出登录也不能忘。点击“退出”后先调后端的登出接口删Redis里的token再清本地SharedPreferences最后跳回登录页。如果只清本地不清后端旧token还能用一段时间学生会觉得“账号被偷了”。3.3 WebView 还是原生通知公告模块的取舍智慧校园里最常见的公告内容是一段带HTML的富文本比如学院发的通知可能有加粗、配色、图片。Android端有两种做法一种是用原生TextView解析Spanned支持有限另一种是在通知详情页套一个WebView直接加载后端传来的HTML内容。我一般会选WebView因为管理端用富文本编辑器发的公告样式复杂原生TextView很难完全还原。WebView webView findViewById(R.id.web_view); WebSettings settings webView.getSettings(); settings.setJavaScriptEnabled(true); settings.setDomStorageEnabled(true); webView.setWebViewClient(new WebViewClient()); webView.loadDataWithBaseURL( http://你的后端地址/, contentHtml, text/html, UTF-8, null);参数说明第一loadDataWithBaseURL的第一个参数是baseURL传后端资源根路径HTML里的静态图片就能用相对路径访问第二text/html表示内容类型UTF-8是编码最后null代表不设置虚拟URL。这里最容易踩坑的是用loadData而不是loadDataWithBaseURLloadData遇到中文字符和图片相对路径会乱码或加载不出来。WebView还有一个生命周期问题离开详情页时最好调用webView.destroy()否则多个WebView占内存。像智慧校园这种学生端通知页会频繁打开关闭不销毁会卡顿。做法是在Activity的onDestroy里先移除视图再销毁。你可以在WebView外层套一个ViewGrouponDestroy时removeAllViews再destroy。这样通知模块才算真正收尾。4. 管理系统和客户端的数据同步分页、图片上传、消息推送怎么落地智慧校园管理系统和Android客户端不是两个孤立的App真正的项目复杂度来自“同一份数据两个端都在操作”。管理端在后台发通知、传课表图片、改学生信息客户端看到的是操作结果。要实现这种“同步感”至少要打通三个细节分页参数、文件上传路径、消息到达机制。4.1 列表页分页与下拉刷新客户端和服务端必须约定 pageNum/pageSize/total通知列表、选课列表、成绩列表都是典型的分页列表。Android端下拉刷新、上拉加载对应的就是页号pageNum和每页条数pageSize。服务端不能一口气返回所有数据否则学生端几百条公告全塞进来手机内存直接爆。后端接口用MyBatis-Plus的分页插件时返回结构一般是下面这样{ code: 200, msg: ok, data: { total: 47, list: [ {id: 1, title: 国庆节放假通知, publishTime: 2025-09-20 10:00:00}, {id: 2, title: 图书馆闭馆通知, publishTime: 2025-09-19 16:30:00} ] } }注意分页参数不要叫page和limit业界更常见的是pageNum和pageSize。客户端请求时传两个参数服务端验证pageNum从1开始pageSize最大50。如果pageSize传10000后端要拒绝防止有人把所有数据一次性拖走。这是一个典型的“接口安全”细节写在论文里能说明你想过边界情况。Android端用SwipeRefreshLayout RecyclerView实现分页加载swipeRefreshLayout.setOnRefreshListener(() - { pageNum 1; loadNotice(false); }); recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { LinearLayoutManager lm (LinearLayoutManager) recyclerView.getLayoutManager(); if (lm.findLastVisibleItemPosition() adapter.getItemCount() - 1) { loadNotice(true); // 加载更多 } } });loadNotice里真正请求时拿到total后要和当前列表大小对比如果当前列表大小已经大于total就不继续加载更多。很多项目在这块会翻车上拉加载一直触发但服务端已经没数据了接口一直返回空列表造成死循环。解决办法就是加一个“isLoading”标志在请求开始前置true结束后无论成功失败都置false防止回调重入。4.2 图片上传与访问路径管理端传图客户端显示智慧校园里的图片主要包括课程封面、校园新闻配图、学生头像。管理端用的是Web页面上传图片后要保存到一个静态资源目录Android客户端通过URL访问。这里最容易出问题的是“上传路径”和“访问路径”不一致。后端用一个简单的上传接口PostMapping(/upload) public ResultString upload(RequestParam(file) MultipartFile file) { String originalFilename file.getOriginalFilename(); String ext originalFilename.substring(originalFilename.lastIndexOf(.)); String newFileName UUID.randomUUID().toString().replace(-, ) ext; String saveDir /data/smart-campus/upload/; File dir new File(saveDir); if (!dir.exists()) dir.mkdirs(); file.transferTo(new File(saveDir newFileName)); return Result.ok(http://服务器IP:8080/upload/ newFileName); }参数说明第一originalFilename是客户端原始文件名但最终保存时不能直接用原始名字否则同名文件会互相覆盖必须用UUID或时间戳重命名第二ext一定要取后缀防止用户上传一个叫“xxx.jpg.exe”的文件时判断出错第三实际生产里还要校验文件类型和大小比如只允许jpg/png最大5MB。访问路径上Spring Boot需要在application.yml里配置静态资源映射不然/upload/**路径会404spring: resources: static-locations: file:/data/smart-campus/upload/很多毕设项目把图片保存在项目源码的resources目录打包成jar后图片路径就找不到了。我一般不建议那样做把上传目录放到项目外比如/data/smart-campus/upload再通过static-locations映射。这样重新部署项目不会把用户上传的图片弄丢。Android端加载图片时直接使用Glide加载返回的完整URL即可不需要在客户端再拼一次文件名。4.3 轻量级消息推送累了用轮询还是长连接“管理端发一条通知学生端马上收到”是智慧校园最打眼的演示功能但实现方案要选对。大多数毕设项目不会接入第三方推送SDK因为没有服务器和证书环境。常规做法有两个短轮询和WebSocket。短轮询实现简单学生端进通知页时拉一次之后每隔30秒再拉一次。用Handler的postDelayed就能写缺点是每次轮询都走一次网络服务器压力大。为了演示轮询时间设15-30秒最佳太长演示时“老师等了半天没反应”太短后台被轮询压垮。可以写一个正式一点的做法用WorkManager的PeriodicWorkRequest系统在后台自动跑PeriodicWorkRequest request new PeriodicWorkRequest.Builder( NoticeSyncWorker.class, 15, TimeUnit.MINUTES) .build(); WorkManager.getInstance(context).enqueueUniquePeriodicWork( notice_sync, ExistingPeriodicWorkPolicy.KEEP, request);逻辑说明WorkManager会合并任务、减少耗电适合后台更新。但如果你要演示“发布公告后客户端30秒内看到”就得把间隔改成30秒或者用WebSocket做长连接。WebSocket的优点是实时性好缺点是要维护连接状态和服务端心跳教学项目里敲完也要花不少时间调试。我的建议是毕设里用短轮询足够在论文“不足与展望”里写一句“生产环境可替换为WebSocket”就能加分。同步还要注意“删除”和“修改”的通知。如果管理端把公告撤回了客户端本地已经缓存这条公告重新拉列表后应该把本地旧的移除。最简单的做法是每次同步全量替换当前列表的前2页不做增量合并这样逻辑简单不容易出bug。增量合并要对比id费时间还容易漏。5. 智慧校园项目避坑跑通和答辩前必须知道的问题这里不写“预期怎么做”的空谈直接列我自己踩过、也看学生反复踩的血泪经验。每一条按“现象→原因→解决”的结构来方便你在答辩前对着检查一遍。5.1 Android 9 以上强制要求 HTTPS明文流量被拦下现象在Android Studio模拟器里访问后端接口Logcat报错“CLEARTEXT communication to 192.168.1.10 not permitted by network security policy”接口返回null但浏览器访问同一个接口是正常的。原因Android 9API 28开始默认禁止明文HTTP流量而智慧校园开发阶段后端通常是http://localhost:8080或局域网IP不是https。很多新手不知道这个限制以为是网络层写错了。解决在AndroidManifest.xml的application节点加android:usesCleartextTraffictrue这是开发阶段最快的处理方式。如果目标应用要发布不建议全局放开可以只针对后端IP配置networkSecurityConfig。注意这只影响Android端后端不用改。答辩时记得说明正式环境会用HTTPS开发期为了本地调试放开明文流量。5.2 WebView 显示管理端 HTML 公告中文乱码、CSS 错位现象通知详情页用WebView.loadData加载从后端接口返回的HTML结果是中文字符变成“锟斤拷”段落间距和图片位置完全不对。原因loadData方法默认把内容按HTML的编码头解释但没有在后端HTML内容里声明charset也没告诉WebView用UTF-8解码。另一种情况是CSS和图片链接用的是相对路径loadData的baseUrl为空资源全部无法加载。解决改用loadDataWithBaseURL并明确传UTF-8baseUrl传后端资源根地址例如“http://你的服务器地址/”。同时要求管理端保存通知时HTML内容统一转成UTF-8再入库。如果在后端用Java拼HTML注意对图片标签的src做绝对路径拼接别只存相对路径。5.3 后端 LocalDateTime 返回的 JSON 格式客户端解析不出时间现象管理端通知实体里用LocalDateTime作为publishTime客户端拿到的JSON里publishTime却是一个对象结构类似“{date:{year:2025...}}”用Gson解析成String失败。原因Java 8的LocalDateTime在Jackson默认序列化时会序列化成JavaTime模块的特殊结构不是“2025-09-20 10:00:00”这种直观字符串。Spring Boot如果不配置Jackson的日期格式就会发生这种“前后端字段类型不对齐”的问题。解决在Spring Boot的application.yml里配置日期格式spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8同时后端返回的时间字段类型统一用String接收或在实体字段上直接加注解JsonFormat(pattern yyyy-MM-dd HH:mm:ss)。客户端解析时publishTime也直接用String类型不要在客户端再包一层Date转换。这样最简单的做法能把大部分时间格式问题规避掉。5.4 管理端改了学生密码旧 token 还是能继续访问接口现象管理员在管理系统里重置某个学生的密码但学生手机上没退出登录Android客户端仍然用旧token拉课表、查成绩功能一切正常安全上等于没有改密码。原因后端用token做身份识别时只判断token在Redis里是否存在没有让用户密码和token产生关联。密码改了token依然有效这就是典型的“客户端和服务端状态不同步”。解决在sys_user表加一个password_version字段每次修改密码时把它加1登录时把版本号对应的Redis值存成“userId:版本号”。拦截器从Redis取出userId后再查一次当前用户版本号不相等就判定token失效。这个方案比“主动调deleteToken接口”更工程化因为它不依赖客户端配合。5.5 打包 APK 后连不上本机后端模拟器能跑真机就是不行现象用Android Studio模拟器调试时客户端请求http://10.0.2.2:8080一切正常。打包成APK安装到真机后同样的页面转圈加载请求全部失败。原因10.0.2.2是Android模拟器里分配给宿主机的特殊地址只存在于模拟器网络环境中。真机上的10.0.2.2指向局域网里的某个设备或者根本不可达。代码里把baseUrl写死成10.0.2.2真机自然连不上。解决把baseUrl抽成一个可配置常量放在BuildConfig里不同构建类型用不同的值。调试版用10.0.2.2发布版用后端服务器的局域网IP或公网域名。同时后端Spring Boot的启动端口不要绑定127.0.0.1要监听0.0.0.0否则真机从局域网过来也会被拒。检查方法在Android手机上用浏览器打开http://后端IP:8080/upload/xxx.jpg能打开说明网络通打不开说明端口或防火墙问题。6. 让这个项目多拿两分离线缓存、接口日志和答辩演示技巧项目基本功能做完、坑也踩完已经能跑了。但课程设计验收和答辩最看重的不是“能跑”而是“你怎么证明它稳定”。我带过的学生里翻车最多的不是在功能演示而是现场网络断了Android客户端一片空白。所以最后一件事给客户端加一层离线缓存至少保证课表和公告在断网时还能显示上次的数据。离线缓存最轻量的做法是用文件或SharedPreferences存JSON。比如课表接口返回后把整个data数组的JSON字符串存到SharedPreferences下次进入课表页时先读本地数据渲染再去拉网络网络成功则覆盖本地。关键代码只有一行getSharedPreferences(cache, MODE_PRIVATE) .edit() .putString(course_list, jsonString) .apply();读取时先判断本地是否有值有值直接用再异步刷新。这套逻辑简单却能让答辩现场显得很专业你可以直接说“我对读多写少的课表数据做了本地缓存弱网环境下客户端不会白屏”。第二个值得做的点是接口日志。在OkHttp的拦截器里打印请求URL、耗时、响应codeLogcat里就能看到每次接口调用的状态。答辩演示时打开Logcat老师看到你一边点按钮一边有日志输出比嘴上说“接口没问题”更有说服力。不要等到出bug才加日志项目交出去之前就应该把Log留着。最后一个技巧是答辩时的验证脚本先用管理端新增一条公告再用客户端下拉刷新看到这条公告这个“闭环演示”比单独展示任何一张页面都重要。你可以在纸条上写“先改密码→再开客户端→观察token失效”这种小剧本把前面踩过的坑当成亮点讲。我的习惯是每个功能至少准备一条失败路径的演示比如“断网时客户端提示网络异常而不是闪退”老师反而觉得你考虑得全。智慧校园这个题目看着容易真正拉开差距的从来不是功能数量而是双端数据链路是否经得起追问。把这些细节打磨完这个项目就不是“毕业设计”而是“作品”了。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
PS5模拟器实战:解决《宇宙机器人无线控制器使用指南》无法启动与手柄适配问题 1. 项目背景与真实需求拆解说实话,官方兼容库把《宇宙机器人无线控制器使用指南》标注为“无法启动”的那一刻,我心里就涌起一股不服气——这个项目太有代表性了。作为PS5玩家第一个接触的交互式教程游戏,它不只是用来试手柄的,更… · 2026/9/25 13:23:20
小米澎湃OS init_boot提取实战指南:Magisk 27.0 root避坑全解析 1. 这不是“刷机教程”,而是一份给真实动手者的 init_boot 提取避坑实录如果你正盯着小米澎湃OS手机里那颗被加密锁死的 boot.img,手边刚下好 Magisk 27.0 的 ZIP 包,却卡在“找不到 init_boot 分区”这一步——别急着重刷 ROM 或翻遍 XDA 论… · 2026/9/25 13:23:20
ng-zorro-antd DatePicker 完整指南:日期/范围选择器的 API、国际化配置与源码实现 UI组件前端 【免费下载链接】ng-zorro-antd Angular UI Component Library based on Ant Design 项目地址: https://gitcode.com/gh_mirrors/ng/ng-zorro-antd 点击查看 免费下载 本篇指南围绕 ng-zorro-antd 官方日期选择框文档(components/date-picke… · 2026/9/25 14:22:08
Java视频处理实战:从上传、转码到播放的完整链路方案 看到这个标题第一眼,我愣了一下:java video_video java, video java Suppliers and Manufacturers at Alibaba.com。乍看像电商平台的垃圾搜索页,但拆开其实就两件事:用Java做视频相关开发,给项目找靠谱的“供应商”—… · 2026/9/25 14:22:08
魔兽争霸3第六章黑屏修复:从兼容性到OpenGL渲染的完整排查指南 前一阵子重玩《魔兽争霸3:混乱之治》,人族战役一路打到第六章,结果载入进度条一过,屏幕直接黑掉,鼠标指针倒是还能动,采矿和战斗的音效也在,就是画面全暗。这种状态最折磨人,你说它完… · 2026/9/25 14:22:02
进程杀不掉?任务管理器结束任务无效的深层原因与强杀方案 任务管理器里点“结束任务”,窗口是关了,进程却还躺在列表里稳如泰山;再点一次,直接弹“拒绝访问”;用管理员身份把任务管理器打开,结果该杀不掉的还是杀不掉——这种场景我遇到太多次了。尤其当碰上后台服… · 2026/9/25 14:22:02
闲置域名如何通过2KB平台停放变现?完整实操指南 玩域名久了,手头总会有几个处于“闲置期”的域名——建站暂时用不上,直接卖掉又没到心理价位,每年还要照常续费。这时候很多人都会想到域名停放(Domain Parking)这个回血办法。简单说,就是把暂时不用的域名… · 2026/9/25 14:22:02
基于Hadoop的电视剧收视率分析系统的设计与实现 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片!
一、 背景与意义
随着流媒体平台和数字电视的快速发展,电视剧收视数据呈现出爆炸式增长。传统的单机数据库和分析工具在处理海量、高并发的收视日志时&… · 2026/9/25 14:21:56
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37