如果你最近在逛毕业设计相关的资源站,应该见过不少类似这样的标题格式:“基于xxx的xxx系统(源码+lw+部署文档+讲解等)”。其中“基于Android的企业网络主机IP地址管理系统”就是非常典型的一个。这里的lw指的就是论文文档,整包东西通常包含:一份可运行的Android项目源码、一篇配套毕业论文、一份能照做的部署说明,外加讲解视频或答辩PPT。这篇文章我想从一个既做过类似项目、也带过不少学生做课设的角度,把整个系统从需求到架构、从代码到部署、从论文写作到答辩演示拆开讲清楚。如果你正在选毕业设计题目,或者想完整练一个Android网络项目,这个题目值得好好研究。
1. 项目到底要解决什么问题:需求与功能拆解
1.1 企业IP管理的真实痛点
别把这个题目想得太高大上,它的真实场景很朴素。一个稍微像样点的公司,办公室里少说几十台设备:台式机、笔记本、打印机、监控摄像头、Wi-Fi AP、测试服务器,每台联网设备都要有一个IP地址。
问题来了:这些IP是谁分配的?今天新来的同事分到哪个IP?那台打印机是192.168.1.23还是24?前两天财务说“IP地址冲突,上不了网”,到底是谁的机器占了同一个IP?如果没有一套台账,网管基本靠脑子记,或者翻聊天记录,再或者开一个乱七八糟的Excel表格。Excel确实能记,但是手机上看不了、多人没法共同维护、也不方便快速检索。基于Android的企业网络主机IP管理系统,就是把这张“IP地址台账”搬到手机上,让网管走到哪儿都能查、能录、能改。
1.2 角色划分与核心功能清单
从这个系统最常见的课程设计实现来看,角色一般不需要太复杂,一个管理员账号就能覆盖全部功能,顶多加一个只读的运维人员角色。功能围绕IP台账的全生命周期来设计,我列一张表,你一眼就能看明白:
| 模块 | 功能说明 | 备注 |
|---|---|---|
| 用户登录 | 管理员输入账号密码登录,校验身份 | 必要模块,论文里很好写 |
| 仪表盘 | 展示总IP数、已用数量、空闲数量 | 加分项,统计用SQL就能实现 |
| IP列表 | 分页展示所有主机IP记录,支持关键字搜索 | RecyclerView + 输入框 |
| 新增主机 | 录入IP、MAC地址、主机名、部门、位置等信息 | 需要做IP重复校验 |
| 编辑/删除 | 修改主机信息或删除记录 | 删除前建议二次确认 |
| 操作日志 | 记录谁在什么时间做了什么操作 | 看起来简单,答辩很加分 |
| 连接配置 | 在客户端填写服务器IP和端口,支持保存 | 方便真机调试,必须有 |
有些同学还会额外做“IP冲突检测”,也就是拿现有的IP列表去ping一遍,标记哪些IP被占用但没登记。这个功能理论上可以做成Android端发ICMP包或者调用服务端来探测,但课设阶段容易做得很粗糙,我建议放到论文的“展望”里写,不必死磕成主要功能。
1.3 为什么偏要选Android端来做
同样一套管理系统,做成Web端不是更常见吗?问题在于课程设计和毕业设计的评价逻辑不太一样。Web后台管理系统实在太多了,前后端技术翻来覆去就是那几样;而“Android端 + 自建服务端”的组合,既能体现移动端开发能力,又能表现网络通信和数据库设计能力,从题目上看就比纯Web加分。
还有一个很现实的原因:演示效果好。答辩现场,你当着老师的面掏出手机,打开App登录,查一条IP、新增一台设备、再翻个日志,整个操作过程都在一块小屏幕上,视觉冲击力明显比用浏览器点来点去强。而且“企业网络主机IP地址管理”这个场景本身就适合移动端,网管本来就不会一直坐在工位上,他可能就在机房、在走廊、在同事工位旁边,手机随手一查才是实际需求。所以这个题目在选题层面是站得住脚的。
2. 技术选型与整体架构设计:先想清楚再动手
2.1 整体技术栈全景
拿到一个项目包,第一步不是急着跑代码,而是先看清它用了什么技术、为什么这么选。我按最常见的课设实现方案给你列个全景:
| 层级 | 技术方案 | 选型理由 |
|---|---|---|
| Android客户端 | Java + Android Studio | 资料多、兼容性好,方便调试 |
| 网络通信方式 | Socket / TCP + JSON字符串 | 协议可控,论文里有真东西可写 |
| 服务端 | Java Socket Server + 线程池 | 轻量、好部署,一个Main类就能跑 |
| 数据库 | MySQL 5.7/8.0 | 关系型数据库,字段关系清晰 |
| 数据访问 | JDBC + PreparedStatement | 不需要引入MyBatis/Hibernate,学习成本低 |
| 构造工具 | Gradle、Android SDK 31/33 | 当前主流版本 |
| 部署环境 | Windows/Linux + JDK1.8 | 课设最常见的运行环境 |
这套技术栈没有特别花哨的东西,但每一样都能在论文里写出“为什么选它”。这比堆一堆自己都讲不清楚的新框架要安全得多。
2.2 服务端到底用Socket还是SpringBoot
这是做这个题目时大家最先纠结的问题。我直接给你建议:如果你还想把项目讲透、论文写得扎实,就用Java Socket Server;如果你以后想走Java后端开发,可以改装成SpringBoot。但课设阶段,我不太建议一上来就上SpringBoot全家桶。
理由很简单:一旦引入SpringBoot,你的项目重心很容易从Android客户端滑到后端框架上,Maven、依赖注入、MyBatis、lombok,这些名词你要花大量时间解释,答辩时老师随便问一句“SpringBoot自动配置原理是什么”,很多同学就接不住了。而用Socket Server,你只需要说清楚四件事:服务端怎么监听端口、怎么接收客户端数据、怎么按动作分发处理、怎么返回结果。任何一步都能被演示和日志直观地展示出来,深度反而更可控。
2.3 数据库设计:三张表撑起整个系统
数据库设计这个部分,很多同学容易忽略,但它恰恰是论文里最容易出图、出彩的地方。这个项目核心就三张表:
t_user用户表:存储登录账号和密码散列值,字段包括id、username、password、role、create_time。
t_ip_info主机信息表:存储IP台账记录,字段包括id、ip、mac、host_name、department、owner、location、status、remark、create_time、update_time。其中status表示该IP是“已使用”还是“空闲”,强烈建议你用数值0/1而不是直接删除记录,这样仪表盘统计的时候只需要一个count就能得到已用和空闲数量,历史记录也还在。
t_oper_log操作日志表:字段包括id、user_id、action、detail、create_time。只要有添加、修改、删除操作,就往这张表里插一条记录,后面查“谁动了哪条数据”一目了然。
我在建索引时会把IP字段做成普通索引,因为列表页按IP搜索最频繁;MAC地址也建议加唯一约束或者至少加个普通索引,防止同一台设备被重复录入。
2.4 通信协议设计:JSON加动作码
Socket通信最怕的就是客户端和服务端各说各话。我的习惯是定义一套简单的“动作码”协议,客户端每次发一段JSON字符串,服务端根据action字段分发处理。
比如客户端登录时发送:
{"action":"login","data":{"username":"admin","password":"加密后的密码"}}请求IP列表时发送:
{"action":"list","data":{"page":1,"size":20,"keyword":"192.168"}}服务端统一返回:
{"code":200,"msg":"成功","data":{} }错误时返回类似:
{"code":500,"msg":"IP地址已存在","data":null}为什么用JSON而不是自定义二进制协议?因为JSON调试太方便了,Android端有JSONObject,服务端可以用Fastjson或者Gson,两边解析逻辑完全一样;而且报文可读性强,论文里贴一段示例报文就非常直观。至于TCP粘包拆包问题,课设级别最简单的做法是:每条消息用换行符\n结尾,服务端按行读取,一行就是一条完整消息。这个细节能写进论文,也能在面试时体现你的网络编程素养。
3. Android客户端核心实现:源码里到底写了什么
3.1 工程结构与开发环境
打开Android项目源码后,你会看到典型的Android工程结构。我建议你把包名按职责划分清楚:
- activity包:登录界面、主界面、IP列表、新增/编辑界面
- adapter包:IP列表的RecyclerView适配器
- entity包:用户实体、IP实体、日志实体
- net包:Socket请求工具、接口回调
- utils包:加密工具、校验工具、SharedPreferences缓存工具
这种分包方式清晰,写论文画功能结构图也容易。开发环境建议直接用Android Studio最新稳定版,新建项目时语言选Java即可。编译SDK版本可以选31到34之间,最低支持版本建议不低于21,覆盖绝大多数手机。
3.2 网络请求模块:子线程、Handler与超时
Android开发里有一条雷打不动的规则:网络请求不能放在主线程。所以在源码里你会看到,要么用了Thread、AsyncTask(虽然现在不推荐),要么用了OkHttp。课设级别我用的是Thread加Handler,代码很好理解。核心思路是:开一个子线程,用Socket连接服务器的IP和端口,发送JSON字符串,然后读取服务端返回的一行数据,最后通过Handler把解析结果传回主线程更新UI。
关键点有三个:一是Socket连接一定要设置超时,比如setSoTimeout(5000),否则服务器没开的时候,点击按钮界面会卡住很久;二是在Activity销毁时记得把子线程中断或移除Handler回调,否则会出现内存泄漏;三是把服务器IP和端口做成本地可配置项,不要硬编码在代码里,真机调试的时候要换成电脑的局域网IP。
如果你拿到手的源码用的是OkHttp,那更省心,只需在build.gradle里加依赖,调用异步请求即可。两种方式思路完全一致,都是“构建参数、发起请求、解析响应、更新UI”。
3.3 登录与会话保持
登录模块是整个系统的门面,做得好的能看出工程习惯。客户端拿到用户输入后,不要把明文密码直接传到服务端,至少先做一次SHA-256散列再传。服务端库里存的也不是明文,而是注册时算好的散列值。登录成功后,服务端返回一个userId和角色信息,客户端用SharedPreferences保存,下次启动App就能显示“欢迎XX”并免登录。
课程设计其实不需要纠结JWT或者Token这类高级东西,但建议你登录成功后至少在后续请求里带上userId,服务端可以通过userId判断操作者,写日志时也能直接记录操作人。
我这里给你看一个典型的Socket请求工具伪代码结构:
public class SocketUtil { public static void sendRequest(String json, Handler handler) { new Thread(() -> { try { Socket socket = new Socket(serverIp, serverPort); socket.setSoTimeout(5000); OutputStream os = socket.getOutputStream(); os.write((json + "\n").getBytes("UTF-8")); os.flush(); InputStream is = socket.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); String line = reader.readLine(); Message msg = new Message(); msg.obj = line; handler.sendMessage(msg); socket.close(); } catch (Exception e) { // 发错误消息给主线程 } }).start(); } }这不是完整代码,但结构已经很接近真实项目了,读源码时你可以按这个思路去找对应实现。重点是理解:子线程负责网络操作,Handler负责回到主线程更新界面,Socket关闭放在请求结束后。
3.4 IP列表、搜索与下拉刷新
列表页是这个系统最核心的界面。我建议用SwipeRefreshLayout包住RecyclerView,实现下拉刷新;顶部用SearchView或者EditText做关键字搜索。搜索时可以做一个简单的防抖处理,比如输入停顿500毫秒后再发请求,不然每敲一个字母就请求一次,服务端压力大,界面也会频繁刷新。
列表Adapter里做的事很简单:解析每个IP实体,把IP地址加粗显示,MAC地址和主机名作为第二行,部门和状态用标签展示。如果状态为空闲,建议用一个灰色或绿色小圆点区别显示,视觉上更直观。新增和编辑功能用同一个Activity,根据是否有传入的IP实体来区分是“添加”还是“编辑”,这个模式很多项目都在用,简单实用。
卡片布局建议用MaterialCardView,圆角、内边距设好,稍微加一点阴影,整个界面的质感就上来了。这一套UI配合CoordinatorLayout加AppBarLayout,顶部栏收起时还带一定的视觉效果,看起来完全不像普通课设。
3.5 UI细节:协调布局、Banner和进度条
很多网上的源码喜欢在主页顶部放一个Banner轮播图,我这个项目里也见到过。说实话,Banner放在“企业IP管理系统”里业务价值不大,但课程设计和毕业设计是“作品展示”逻辑,一个轮播图能让首屏看起来更丰满。如果你拿到手的源码里有,别急着删,留着做演示开场的视觉展示也挺好,轮播图换成技术架构图或者统计图卡片就行。
加载进度条也是这类项目必备的。页面刚进入时,网络请求可能耗时几百毫秒,不加反馈用户会以为卡死了。最简单的方案是用AlertDialog封装一个带进度条的加载对话框,请求开始时show,拿到结果后dismiss。后台线程回调主线程的时候要小心空指针,Activity关闭后再回调UI就危险了。
4. 服务端与数据库实现要点:千万别只是“能跑”
4.1 多线程处理客户端连接
服务端是整个系统能不能撑住演示的关键。如果你拿到的源码只有一个单线程的ServerSocket,只要客户端一多,或者一个请求卡住,后面全部排队,演示的时候非常尴尬。标准做法是用线程池。
核心结构是:
ServerSocket serverSocket = new ServerSocket(9999); ExecutorService threadPool = Executors.newFixedThreadPool(10); while (true) { Socket socket = serverSocket.accept(); threadPool.execute(new ClientHandler(socket)); }这样每个客户端连接进来,都会从线程池里分配一个线程去处理,多个客户端的请求互不阻塞。这个设计是你论文里非常值得写的一段。为什么用线程池而不是每次new一个Thread?因为频繁创建线程开销大,线程池控制并发数让系统更稳定。这个解释很简短,但每次答辩讲出来,老师都会点头。
还有一个细节:服务端处理数据库操作时,不要用一个static全局Connection让所有线程共用,MySQL连接不是线程安全的,高并发下会报错。简单做法是每个请求获取连接、用完关闭;如果代码量允许,也可以用C3P0或Druid连接池。
4.2 JDBC的坑与预编译SQL
数据库连接字符串是踩坑重灾区。MySQL 5.7和8.0的JDBC驱动类名不一样:MySQL 5.x是com.mysql.jdbc.Driver,而MySQL 8.x要写成com.mysql.cj.jdbc.Driver。连接URL里还得带上useSSL=false、characterEncoding=utf8、serverTimezone=Asia/Shanghai,否则会出现SSL报错或日期格式错误或中文乱码。
数据库操作无脑用PreparedStatement,不要用Statement拼接字符串。比如按IP搜索时:
String sql = "select * from t_ip_info where ip like ? limit ?,?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, "%" + keyword + "%"); ps.setInt(2, offset); ps.setInt(3, pageSize);这样既防止SQL注入,又让代码更易读、更好排查。新增IP时先执行一条select count(*)检查IP是否已存在,存在就返回业务错误码“IP地址已存在”,不存在才insert。这种业务判断放在服务端,比客户端做校验可靠得多。
4.3 核心接口动作的流程逻辑
服务端收到客户端请求后,大致分三步走:解析JSON取action、根据action调用对应方法、把结果封装成响应JSON返回。我会在源码里看到类似下面的分发逻辑:
JSONObject req = JSONObject.parseObject(line); String action = req.getString("action"); JSONObject data = req.getJSONObject("data"); switch (action) { case "login": handleLogin(data); break; case "list": handleList(data); break; case "add": handleAdd(data); break; case "update": handleUpdate(data); break; case "delete": handleDelete(data); break; case "stats": handleStats(); break; default: ... }这个switch结构本身就能画成论文里的“服务端业务处理流程图”。剩下的事情就是每个方法里执行SQL、处理异常、回写JSON。
我特别建议把日志功能真正实现出来,别只写在文档里。登录成功记一条,新增IP记一条,删除IP记一条。演示时你当着老师的面操作一遍,再打开日志表看一眼,整个系统的闭环就立住了。很多同学论文里写了日志模块但代码里没做,这属于最亏的减分项。
4.4 安全处理:密码散列、参数校验、统一错误码
课设不需要上SSL证书这种重量级安全方案,但该有的基本安全意识要有。
密码不能明文存。注册或初始化用户时,用SHA-256将密码加盐后再存数据库,盐可以是固定值,也可以是用户名。每次登录时把输入密码散列后跟库里的值比对,这样即使数据库泄露,密码也不是明文。
参数校验放在服务端做。IP地址格式用正则校验一下,MAC地址也做基本格式检查,防止用户乱填。端口不在合理范围内的数据直接拒绝。这类校验代码不复杂,但论文能写,答辩能说,还能体现工程师的基本素养。
统一错误码体系也是加分项。100表示参数错误,200表示成功,404表示资源不存在,500表示服务端异常,601表示IP重复。客户端根据code弹不同的Toast,而不是把服务端的异常堆栈直接显示在界面上。
5. 从零部署整个项目:照着做就能跑通
5.1 MySQL初始化与导入数据
拿到项目包后第一件事是导入数据库脚本。一般会有一个sql文件,里面包含建库、建表和几万初始数据。用Navicat新建数据库,建议库名直接叫ipmanager,字符集选utf8mb4,然后执行SQL脚本。
初始账户一般在脚本里已经写死,比如admin / admin123。注意脚本里的密码字段如果是MD5或SHA-256散列值,你没法直接看到明文密码,登录时也要用相同算法处理后再去匹配。如果脚本里的初始数据只有一条,我建议你手动再补几条有代表性的记录,比如办公电脑、财务打印机、监控摄像头,演示时搜索关键词效果会更好。
5.2 服务端环境配置与启动
服务端不需要复杂的安装。前提是你机器上装了JDK1.8以上版本,MySQL能正常连接,并且已经把mysql-connector-java的jar包放到了服务端项目的lib目录下。如果源码是IntelliJ IDEA项目,直接打开,等Gradle或Maven同步完,运行Main类即可。如果源码不带构建工具,那就用命令行编译并运行,注意classpath要包含驱动包。
启动日志最好能在控制台打印出来,比如“服务端启动成功,监听端口9999”。万一客户端连不上,第一件事就是看这条日志到底打没打出来。如果端口被占用,会直接抛异常,换个端口就好。服务端代码里如果监听了localhost而不是0.0.0.0,那么真机永远连不上,这个细节相当容易出问题。
我自己习惯在Windows上写完用命令行跑,在Linux服务器上部署时用nohup java -jar xxx.jar > app.log 2>&1 &方式启动,日志重定向到文件,排查问题非常方便。裸Class文件同理,只要把依赖jar一起引进去就行。
5.3 Android端连接配置:模拟器和真机不一样
这是新手最容易卡壳的地方。
模拟器连接本机服务端,不能用127.0.0.1,因为模拟器里的127.0.0.1指的是模拟器自己。Android模拟器访问宿主机的固定地址是10.0.2.2。所以如果你开着模拟器调试,服务器IP应该填10.0.2.2,端口和你的服务端一致。
真机连接就不一样了。手机和电脑必须处于同一个局域网,手机里填的是电脑的局域网IP,比如192.168.1.50。很多人填了IP还是连不上,原因基本就三个:一是电脑防火墙拦了TCP端口,解决方法是入站规则里放行对应端口;二是服务端绑定了localhost而不是0.0.0.0;三是WiFi开了AP隔离,解决方法是改用同一网段或关闭AP隔离。
还有一个高版本Android的问题:targetSdk 28及以上,默认不允许HTTP明文请求。如果是Socket连接,倒没有这个限制,但要是一部分源码换成了OkHttp走HTTP接口,就必须要配置明文流量。最简单的做法是在manifests里加上android:usesCleartextTraffic="true"。如果还不行,就用networkSecurityConfig写一个允许所有域名的配置文件。
5.4 用Docker让部署更体面
虽然纯Socket服务端部署很简单,但如果你想在论文里加一个“容器化部署”的亮点,也可以写一个简单的Dockerfile把服务端打成镜像,再用docker-compose同时启动MySQL和App服务。热词里的docker部署其实就是这个思路。
用Docker有一个实际好处:老师电脑上没有装JDK和MySQL,也能通过docker-compose一键起服务。不过我要提醒你,Docker本身又是一个大坑,网络模式、端口映射、MySQL数据卷,每一样都要理解清楚再动手。课设做到后面如果时间紧张,宁可先把传统部署跑通,再加Docker作为扩展阅读内容,不要为了花哨把自己绕进去。
6. 配套论文lw和答辩讲解怎么准备
6.1 论文目录与各章写法
标题里的lw就是论文,一般指毕业论文或课程设计报告。论文写得再漂亮,结构也要遵循学校模板,常规布局是:绪论、相关技术介绍、需求分析、系统设计、系统实现、系统测试、总结与展望。
绪论部分写研究背景和意义,结合企业网络IP管理的痛点来写,别抄百度百科;相关技术介绍主要写Android、Socket通信、MySQL、JSON,每节两三页就够;需求分析写角色、功能用例和非功能需求,配上用例图;系统设计是重头戏,要含系统架构图、功能结构图、数据库E-R图、关键流程时序图;系统实现对应功能截图加核心代码片段,不用全部贴代码,挑登录、列表、添加这几个核心功能即可;系统测试写功能测试用例表格,再加简单的性能测试描述。
论文最怕的是图和代码脱节。先有系统截图,再贴和截图对应的代码,最后写一两句实现说明。老师翻的时候能顺着看下来,印象分直接翻倍。
6.2 必须画好的几张图
论文插图别用手机随手拍,也别用截图软件硬截。我推荐用ProcessOn或者draw.io画图,统一配色,导出高分辨率图片。
必画图包括:系统总体架构图,分层画客户端、服务端、数据库;功能结构图,树状图列出每个模块;用例图,标出管理员和系统之间的关系;E-R图,把三张表的主键外键和字段关系画清楚;登录流程图和IP添加流程图;服务端多线程处理连接流程图。每张图都要能在答辩时指着讲两分钟。
画图工具不需要学得多复杂,ProcessOn的模板库里有现成的用例图和架构图,改改文字就行。但注意改完整理格式,不要留下模板水印和自带的多余元素。
6.3 答辩演示和常见问题应答
讲解项目一般是10到15分钟。我带的学生的标准节奏是:前1分钟讲背景痛点,2分钟讲系统功能和技术架构,5分钟真机或模拟器功能演示,剩下时间回答提问。
演示前务必做一次全流程走场:登录、刷新列表、搜索一条记录、添加一条记录、修改、删除、展示统计、查看日志。手机上要提前把IP地址配置好;如果现场网络不稳定,建议提前用模拟器或者录一段演示视频作为后备方案。
老师喜欢问的问题我也列一下:
- 客户端为什么不能在主线程请求网络?答:主线程阻塞会导致ANR,Android规定网络操作必须在子线程。
- 服务端如何支持多个客户端同时访问?答:使用线程池处理每个连接,避免单线程阻塞。
- 连接失败可能是什么原因?答:IP地址错误、防火墙拦截、端口未监听、服务端未启动。
- 数据库为什么选择MySQL?答:开源稳定,和JDBC配合成熟,符合业务的数据关系模型。
- 你如何防止SQL注入?答:使用PreparedStatement预编译,不拼接SQL字符串。
这类问题回答思路比背诵标准答案重要,把底层逻辑讲清楚,老师基本不会为难你。
7. 实操中常见的坑与排查实录
7.1 客户端连不上服务端
这恐怕是这个项目里出现频率最高的问题。桌面端服务端启动看起来正常,但Android端就是转圈圈或者提示连接失败。排查顺序我整理成了一张表:
| 现象 | 排查项 | 解决方式 |
|---|---|---|
| 模拟器提示失败 | 是否用了10.0.2.2 | 改为10.0.2.2,不要用127.0.0.1 |
| 真机提示失败 | 手机和电脑是否同一网段 | 连同一个WiFi或开热点 |
| 能ping通但连不上 | 电脑防火墙拦截端口 | 入站规则放行9999端口 |
| 服务端控制台没日志 | 是否监听0.0.0.0 | 改成serverSocket = new ServerSocket(9999) |
| 日志打了但客户端收不到 | Socket超时过短 | 调大setSoTimeout到5000以上 |
这个表格可以直接写进论文的测试章节,也能帮你自己理清排查思路。
7.2 中文乱码与编码问题
中文乱码几乎每个做Socket通信的人都会遇到。源头只有一个:客户端和服务端没有统一字符编码。你写出去的时候用getBytes("UTF-8"),读取时也要用InputStreamReader的UTF-8;数据库连接URL里也要带characterEncoding=utf8,表结构字符集用utf8mb4。只要这三处一致,乱码基本不可能出现。
7.3 数据库驱动加载失败
MySQL版本不同,驱动类名不同,这是我在带学生时反复强调的。Class.forName("com.mysql.jdbc.Driver")是5.x写法,MySQL 8及以上要用com.mysql.cj.jdbc.Driver。连接URL中8.x还要显式配置serverTimezone=Asia/Shanghai,否则默认取UTC时间,数据库写入的时间会比北京时间早8小时,看起来非常别扭。
7.4 高版本Android访问HTTP受限
如果源码里用的是OkHttp走HTTP接口而不是Socket,那么targetSdk 28以上默认禁止明文HTTP。AndroidManifest.xml的application节点加android:usesCleartextTraffic="true"就能解决。如果还不行,检查是否有网络权限,也就是AndroidManifest里要声明uses-permission android.permission.INTERNET。
7.5 页面卡顿和ANR
ANR大多发生在“在主线程做了网络请求”或者“主线程做了大量JSON解析”。如果列表有上千条数据,一次性全部解析并填充RecyclerView也会卡顿。解决办法是服务端做分页,每页最多20条或50条;如果坚持一次性拉全量,也要放到子线程解析完再交给Adapter。还有一个容易被忽略的问题:Activity销毁后,Handler里的回调还在执行,轻则内存泄漏重则崩溃,所以在onDestroy里要把Handler的callback置空或者用弱引用持有。
8. 给后来人的一点实在建议
8.1 拿到项目包后按什么顺序跑起来
别一上来就打开Android Studio编译,那会各种报错。我的建议顺序是:先建数据库导数据,再启动服务端看控制台日志,然后用Android模拟器配置10.0.2.2去登录,最后换成真机测试。每一步成功后再走下一步,出了问题至少能定位到是哪一层。
8.2 如何把课设做出“不像课设”的质感
想在评优或答辩时让人觉得这个项目下了功夫,不用堆新框架,把几个细节做规范就行:所有按钮和页面风格统一;空数据时显示“暂无数据”而不是白屏;删除操作弹确认框;加载过程有进度提示;服务端日志完整;代码里加注释。这些细节加起来,比你用十个新技术都有说服力。
8.3 一些真实体会
这个项目我前后带过不少学生做过,最大的感受是:单看每一块技术都不难,但把它们串成一个完整系统,需要的是对端到端流程的理解。Socket请求、JSON解析、数据库增删改查、线程池、gradle配置,任何一个点出问题,整个系统就动不了。但也正因为如此,做完这个项目你对Android开发的整体理解会上一个台阶,而不只是会摆弄几个控件。
真到答辩前,我强烈建议你做一个动作:把服务端的日志窗格打开放在投影旁边,然后拿着手机操作。你每添加一台主机,日志里就出现一条带时间的操作记录;你每查一次列表,日志里就打印一次请求参数。这种可视化反馈比你在PPT上画一百张架构图都直观,老师看到的是“系统真的活着”,而不只是一个演示壳子。这个小技巧,是我觉得整个项目实操中最值钱的一个经验。