安卓疫苗预约App开发实战:需求拆解到exe封装全流程 📅 发布时间:2026/8/27 5:39:24 👁 浏览次数: 简介移动应用开发中预约类App是常见的业务场景但疫苗预约因涉及批次、针次、库存等业务约束比普通预约更富挑战。本文从安卓开发基础出发讲解如何用Java与SQLite构建本地数据层通过RecyclerView实现疫苗列表与预约流程并重点解决预约冲突校验、库存同步等核心逻辑。针对期末验收场景文章详细介绍了将APK封装为Windows可执行exe的几种方案让应用能一键启动演示。同时涵盖设计报告撰写、数据库表设计、测试用例等工程实践要点帮助开发者理解安卓项目从需求拆解、技术选型到打包交付的完整链路是一份兼顾教学与实战的安卓开发指南。1. 从期末大作业到“能答辩”的疫苗预约App需求拆解与功能边界每年期末周安卓方向的大作业里十有八九是“XX管理系统”或者“XX预约App”。这次拿到“智慧医疗-疫苗预约”这个题我一开始也没当回事觉得无非就是照着管理系统模板换个皮。但真正动手才发现疫苗预约和普通预约类应用有个很大的区别它有一套业务约束——疫苗有批次、有针次、有库存同一人同一针次不能重复预约预约之后还能不能取消这些规则如果前期不梳理清楚写代码的时候会反复返工。1.1 先把交付物拆明白源码、设计报告、exe文件题目要求里有一句话很容易被忽略“包含源码设计报告导出exe文件”。源码和设计报告是期末大作业的常规配置但“导出exe文件”就很微妙——安卓App的标准产物是APK怎么和exe扯上关系这里其实暗含了两个意思老师大概率是在Windows电脑上验收不想折腾安卓模拟器或者手机真机调试。老师希望拿到一份“双击就能跑”的演示程序而不是一个需要配环境的工程。所以这个项目真正的技术难点反而不是App本身而是交付方式的处理。这个我放在第4章单独展开因为里面坑很多方案也分好几档选错了会在答辩现场翻车。1.2 用户角色和核心功能清单我把疫苗预约拆成两类角色、五个功能模块角色核心诉求功能模块普通用户快速找到疫苗、选择时间、完成预约、可查记录登录注册、疫苗列表、疫苗详情、预约下单、我的预约、个人中心管理员维护疫苗信息、查看预约统计疫苗管理、预约记录查询、用户管理实际开发的时候我的建议是优先保证用户侧的完整链路注册、登录、浏览、预约、取消、查看记录这六件事全部跑通大作业的及格线就到了。管理员端属于加分项如果时间充裕再做不迟——但有一条做管理员端一定要确保权限隔离别让普通用户改自己的角色字段这个低级错误答辩时必被点名。1.3 功能取舍原则不要为了炫技堆功能我当时列了一个“绝不接受清单”不做短信验证码需要第三方服务期末环境申请不到。不做在线支付预约本身免费加支付会显得逻辑不通。不做推荐算法用一个简单的时间排序就够了。不做多语言、深色模式、无障碍适配属于锦上添花不是刚需。期末大作业的核心评价点其实就三个功能完整、逻辑清晰、写法规范。与其花三天做一个华而不实的动画效果不如把预约的时间冲突校验写清楚后者在答辩时能讲出来的内容密度要高得多。2. 技术选型不能靠惯性Java/Kotlin、数据库、UI框架怎么定很多同学拿到项目的第一步就是打开Android Studio新建一个Empty Activity这不算错但如果你什么都不规划就开干写到第2000行代码的时候很大概率会被自己的代码绕晕。我这次特意在写代码前花了一个下午做选型和架构设计回头看是非常值得的。2.1 语言选Java还是Kotlin看你的答辩底气这个题网上搜一圈绝大多数教程都是Java写的因为Java可被借鉴的资料最多、报错也能直接搜到答案。如果你平时上课用的就是Java那继续用Java不要因为Kotlin“更新潮”就临时切换——期末周不是学新语言的好时机。但有一个例外如果你已经能熟练读Kotlin代码并且想体现一点差异化Kotlin的协程和空安全特性确实能让预约这类网络/数据库操作写起来更清爽。我当时选的是Java原因非常简单网络排错时Java的报错信息我能一眼看懂Kotlin的语法糖反而会干扰我的判断。2.2 数据库选型为什么我用SQLite而不是Room桌面端可能有MySQL、PostgreSQL但安卓本地数据库的经典选择就是SQLite这一点没有争议。问题在于直接写SQLite还是上RoomRoom官方ORM编译期验证SQL写起来像Java函数调用配合LiveData和ViewModel有奇效。SQLite原生自己写SQL、自己写Contract类、自己管理游标和关闭代码量大一点但逻辑透明。我的选择是原生SQLite 自定义DBHelper理由有三个期末考试如果被问“你的表结构怎么设计的”“SQL怎么写的”原生SQL可以直接现场写出来Room会有“查着查着就被框架藏住了”的感觉。大作业的数据量很小一张疫苗表撑死几十条ORM的性能优势完全用不上。原生SQLite的代码在网上有海量可直接参考的写法期末周效率优先。当然Room也没问题如果你想往工程化的方向走Room ViewModel LiveData是更标准的组合。但是有一条要注意别同时用两种数据库访问方式有人写到一半嫌原生SQL麻烦又引入了Room结果数据错乱踩了自己埋的坑。2.3 项目目录结构包名见功力我见过太多大作业把所有Activity塞在一个包下文件名叫做Activity1、Activity2……这种写法老师看到第一眼就会降低印象分。合理的分层长这样com.example.vaccine ├── activity // 存放所有Activity页面 ├── adapter // RecyclerView适配器 ├── db // SQLiteOpenHelper、DAO ├── entity // 数据实体类 ├── util // 工具类日期、字符串校验等 └── adapter哪怕你每个包下只有一个类这个分层本身就是“我有工程意识”的证明。答辩时老师如果问“为什么这么分包”你只要说“按职责分层便于后续维护”这个回答能直接通行。2.4 UI框架RecyclerView CardView足够页面布局上我全部用了经典组合RecyclerViewCardViewLinearLayout/RelativeLayout。没上DataBinding也没上Jetpack Compose——不是说它们不好而是大作业阶段用传统方式更稳答起题来更可控。如果需要做一个好看一点的首页Banner直接用ViewPager2这个组件网上教程也多踩坑少。3. 核心功能实现从登录到预约每个模块怎么做才不会翻车说完了选型这一章是我实际敲代码的过程记录。我不打算抄整段代码贴出来那样篇幅太长只把每个模块最关键的几段逻辑和当时的思路写下来方便你对照自己的项目调整。3.1 登录注册明文密码是最大减分项用户表的核心字段CREATE TABLE user ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, password TEXT NOT NULL, name TEXT, phone TEXT, role INTEGER DEFAULT 0 -- 0普通用户 1管理员 );密码这一栏用明文存储绝对是答辩时的安全批评点。大作业可以不接后端加密服务但至少要做一次简单的不可逆处理比如SHA-256加盐public static String encryptPassword(String password) { try { MessageDigest md MessageDigest.getInstance(SHA-256); byte[] hash md.digest(password.getBytes(StandardCharsets.UTF_8)); StringBuilder sb new StringBuilder(); for (byte b : hash) { sb.append(String.format(%02x, b)); } return sb.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return password; } }注意这里我加了一个静态salt字段或者直接用用户名拼接做盐这样同一个密码在不同用户名下生成的摘要也不同。虽然严格来说这并不是最安全的设计但作为大作业已经能说明你对安全有概念。答辩时可以说“生产环境应该用bcrypt或接入服务端登录态本地演示用SHA-256加盐是一个折衷方案”。登录界面别搞得太复杂一个用户名输入框、一个密码输入框、一个登录按钮、一个“注册账号”的跳转入口就够了。注册时一定要做两次密码确认这个细节老师也爱问。3.2 疫苗列表展示RecyclerView适配器的关键点疫苗表CREATE TABLE vaccine ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, manufacturer TEXT, type TEXT, -- 一类疫苗/二类疫苗 doses INTEGER DEFAULT 1, -- 需要接种的针次 stock INTEGER DEFAULT 0, -- 当前余量 description TEXT, create_time TEXT DEFAULT (datetime(now, localtime)) );列表页最需要展示的信息是名称、生产厂家、接种针次、余量。余量这个数字一定要大于0才显示“可预约”等于0显示“已约满”。适配器里绑定数据Override public void onBindViewHolder(NonNull VaccineViewHolder holder, int position) { Vaccine vaccine vaccineList.get(position); holder.tvName.setText(vaccine.getName()); holder.tvManufacturer.setText(vaccine.getManufacturer()); holder.tvStock.setText(余量: vaccine.getStock()); if (vaccine.getStock() 0) { holder.btnBook.setEnabled(true); holder.btnBook.setText(立即预约); } else { holder.btnBook.setEnabled(false); holder.btnBook.setText(已约满); } }这张列表页是整个App的门面数据绑定不要写错按钮状态一定要跟库存联动。演示的时候如果明明有库存但按钮置灰或者没库存还能点进去预约都会成为答辩上的硬伤。3.3 预约下单时间与针次的冲突校验是核心难点预约表CREATE TABLE appointment ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, vaccine_id INTEGER NOT NULL, vaccine_name TEXT, dose_number INTEGER DEFAULT 1, -- 第几针 appointment_date TEXT NOT NULL, -- 预约接种日期 status INTEGER DEFAULT 0, -- 0待接种 1已完成 2已取消 3已过期 create_time TEXT DEFAULT (datetime(now, localtime)) );预约逻辑看起来简单选疫苗 → 选日期 → 填信息 → 提交。真正麻烦的是同一用户同一疫苗的针次冲突。比如乙肝疫苗要打三针用户已经约了第一针就不能再约另一个“第一针”只能约第二针。我当时写的核心判断逻辑是这样的public boolean canBook(int userId, int vaccineId, int doseNumber, String date) { // 1. 疫苗库存是否 0 if (getVaccineStock(vaccineId) 0) return false; // 2. 同一用户、同一疫苗、同一针次是否已存在“待接种”记录 ListAppointment pendingList appointmentDao.queryPendingByUserAndVaccine(userId, vaccineId); for (Appointment appt : pendingList) { if (appt.getDoseNumber() doseNumber) { return false; } } // 3. 日期不能早于今天 if (date.compareTo(getToday()) 0) return false; return true; }这个三段判断写完以后预约主流程才算真正闭环。还有一个容易被忽视的点预约成功后一定要同步扣减疫苗库存也就是给vaccine表的stock字段减1。有同学做了预约记录但库存不变结果疫苗约了好几单库存始终是10演示时很容易被追问。3.4 我的预约列表四大状态怎么展示、怎么流转“我的预约”页面做的不是简单的列表查询因为你必须把状态背后的逻辑讲清楚状态含义可执行操作待接种预约成功尚未到接种日期取消预约已完成管理员标记为已接种无已取消用户自己取消重新预约已过期预约日期已过但未标记完成无列表适配器里根据状态显示不同颜色和按钮。状态切换的代码集中在AppointmentAction类里不要散落在各个Activity中这样答辩时你可以很清晰地说“状态的流转我统一管理在一个类中”。取消预约时还有一件事恢复库存。预约时库存减1取消时库存加1这个逻辑如果漏了演示一会儿之后库存就不对了。还有一个隐藏情况如果一条预约已经“已完成”了用户点击取消这个操作要直接拦截。我当时是在按钮点击回调里判断了status 0才允许取消其他状态弹Toast提示。3.5 简易管理后台两周时间内的加分项如果你学有余力做一个小管理员后台非常加分。普通用户登录进首页管理员登录进一个单独的“管理后台”页面里面有两个功能就够用疫苗管理新增疫苗、修改库存、上下架。预约记录查看所有预约按状态筛选支持把“待接种”改为“已完成”。管理员的身份判断在登录成功后根据role字段决定跳转页面。这里有一个安全细节不要只靠隐藏入口做权限控制。哪怕普通用户不知道管理入口的存在也应该在管理接口请求时再次检查当前登录用户的role字段防止界面绕过。大作业虽然不涉及真正的安全攻击但这个写法会在答辩时成为亮点。4. “导出exe文件”完全指南给Windows老师做一个双击就能跑的演示包这应该是绝大多数人最卡壳的地方。安卓App标准打包产物是APK但你交上去的时候总不能说“老师你装个安卓手机或者模拟器自己跑吧”——所以我们需要把APK封装成一个Windows下可以双击运行的exe文件。在介绍具体方案前我得先立一个认知这个exe的本质不是把安卓字节码转成Windows原生程序而是“一键拉起安卓模拟器并自动安装运行你的App”。搞清楚这件事后面所有操作都不会迷路。4.1 方案A模拟器 一键启动脚本最推荐省事且稳雷电模拟器、夜神模拟器、逍遥模拟器都支持命令行启动这就给了我们自动化空间。思路是写一个批处理脚本依次完成启动模拟器。等待模拟器完全启动通过adb wait-for-device检测。安装APKadb install -r app-debug.apk-r表示覆盖安装。通过adb shell am start拉起App主Activity。批处理脚本的核心内容大概是echo off cd /d %~dp0 start D:\leidian\LDPlayer\dnplayer.exe adb wait-for-device adb install -r vaccine_app.apk adb shell am start -n com.example.vaccine/.activity.LoginActivity echo App已启动演示开始...这里要注意几个点给模拟器安装应用需要使用模拟器自带的adb并且要与Android SDK里的adb版本保持一致否则会出现adb server version mismatch的报错。等待启动不要只用一个wait-for-device因为设备连上了不一定代表系统完全加载完。可以再写一个循环直到adb shell getprop sys.boot_completed返回1再继续。脚本里所有路径都用相对路径或者变量不要写死到C:\Users\你的用户名否则换到教室电脑上就全废了。脚本写好之后用一个小工具把.bat转成.exe比如Bat To Exe Converter或者其他同类工具。这样最终交付物就是一个图标可爱的exe双击后自动启动模拟器、安装APK、打开App整个演示过程三分钟内完成。4.2 方案B直接制作模拟器便携版雷电模拟器提供了一个“制作便携版”的功能生成一个绿色免安装的文件夹。你可以把这个文件夹和APK、启动脚本放在一起拷到老师电脑上运行。这个方案的优点是不需要提前安装模拟器缺点是文件夹体积大主流的模拟器动辄2GB以上U盘传输比较慢。如果你用这个方案注意两个优化点打开模拟器设置把分辨率调到演示设备大小比如1280x720那样App打开后不会显示成很奇怪的拉伸比例。在模拟器里装好App并登录好然后生成快照。以后每次启动直接双击快捷方式用快照恢复App已经打开好演示效果最自然。4.3 方案C备选但我不推荐的方案——在线apk转exe工具网上有不少宣称“APK一键转EXE”的在线服务原理五花八门有的实际上是在网页里包了一个模拟器有的是用某种兼容层方案。我在期末周试过一两个结论是稳定性没有保证而且隐私风险高。如果你不想在答辩现场突然闪退建议放弃这些工具老老实实用方案A或方案B。4.4 我推荐的最终交付包结构我最后提交的文件包长这样疫苗预约App_答辩版 ├── 疫苗预约.apk ├── 启动演示.exe ├── 源码/ │ ├── app/ │ ├── build.gradle │ └── ... ├── 设计报告.docx └── 演示说明.txt其中“演示说明.txt”写清楚双击“启动演示.exe”后会先打开模拟器第一次运行可能需要30秒左右请耐心等待。这个说明很不起眼但能帮老师避免“双击了为什么没反应”的尴尬。提示导出exe只是提供一个便捷的演示入口真正项目的主体还是APK和源码。答辩时如果老师问“这个exe是原生转换的吗”你要照实解释是通过模拟器加脚本封装的不要造假。坦诚往往比吹牛更获得信任。5. 设计报告老师真正会仔细看的部分别糊弄设计报告是期末大作业最容易拿分也最容易丢分的地方。代码写得再漂亮报告写得像大杂烩一样老师翻阅体验很差分高不到哪里去。我自己的经验是一份好的安卓大作业报告应该包含六个部分且每部分都有老师期望看到的重点。5.1 报告结构从需求到测试六段式章节核心内容老师想看到的重点一、需求分析用户角色、功能需求、非功能需求能分清“必须做”和“可选的”二、系统设计架构图、功能模块图、数据库ER图图的逻辑自洽和代码能对得上三、详细设计每个模块的流程与关键代码说明关键判断逻辑预约校验要单独讲四、数据库设计表结构说明、字段解释表关系清楚外键/逻辑外键要有五、系统测试测试用例表、测试结果、问题修复记录要有异常用例比如“重复预约被拦截”六、总结与展望收获、不足、未来改进方向诚实承认不足不写空话套话5.2 画图工具三十分钟搞定架构图和ER图架构图、流程图和ER图不需要多精美但必须有。推荐用这几种工具ProcessOn在线画图有免费额度画流程图和架构图都很方便。draw.io免费开源VSCode插件也能打开。PowerPoint/Keynote说实话期末报告里很多图我见鬼都是用PPT画的画完导出成图片再插到Word里其实效果也不差。画图的核心原则是图里的每一个方块和连线都能在代码里找到对应物。如果你画了一个“数据服务层”但代码里根本没有这个类答辩被追问时就很尴尬。5.3 测试用例表怎么填才有说服力很多同学写测试用例就是“正常登录成功”“正常预约成功”这种用例表非常没有信息量。我建议至少包含以下用例编号测试项操作步骤预期结果实际结果是否通过TC01重复预约拦截同一用户对同一疫苗同针次预约两次第二次预约被拒绝并提示“已有待接种记录”与预期一致通过TC02库存不足拦截疫苗库存为0时点击预约预约按钮置灰不可点击与预期一致通过TC03过期日期拦截选择昨天作为接种日期提交时提示“不能选择过去的日期”与预期一致通过TC04取消后恢复库存取消一条待接种预约疫苗库存加1与预期一致通过这一张表写完老师就知道你不是只把正常路径跑了一遍而是认真做了异常场景测试。5.4 报告里常见的五个低级错误以下错误我在这两年帮学弟学妹看报告时反复见到截图和代码不匹配报告中展示的界面截图是旧版本代码却是新版本。我的建议是写完代码后再统一截一次图不要拿开发早期截图丢进去。篇幅分配失衡环境搭建写了两页核心预约逻辑只写了三行。环境搭建要写但两三段带过就好。数据库设计表和代码字段对不上报告里写了appointment_status代码里叫status。这种低级错误老师一眼就能看出来。关键代码讲解变成长代码粘贴写“下面展示核心代码”然后把80行代码全部贴上去这没有任何讲解价值。应该只挑3-5行关键的逻辑然后讲清楚“为什么这么写”。总结部分全是废话“通过这次实践我提高了动手能力加深了对安卓开发的理解”这种话写20个字就够了不要占一整页。6. 期末周避坑手记安卓开发里那些最容易被卡住的地方最后这一章我把开发过程中踩过和被身边同学踩过的典型坑集中写出来挨个说明原因和解决思路。期末周时间宝贵能躲开的坑就别亲自踩一遍。6.1 Android 12及以上安装APK被“未知来源”拦截如果你把APK传到另一台手机上安装Android 12及以上系统默认会拦截“安装未知应用”。这不是你代码的问题是系统安全机制。解决办法有两个安装时手动允许“此来源的应用”安装权限。在应用内测阶段关掉android:testOnly属性AndroidManifest.xml里不要出现testOnlytrue。曾经有人因为开着testOnly生成的APK只能在模拟器上装真机一直解析失败。6.2 RecyclerView列表数据刷新不及时预约成功取消后回到列表页发现数据还是旧的这种问题非常经典。原因是RecyclerView没有收到数据更新通知。解决办法很简单在返回页面或数据变更后调用adapter.notifyDataSetChanged();如果你用了DiffUtil记得正确实现areItemsTheSame和areContentsTheSame。大作业阶段用notifyDataSetChanged()虽然性能不是最佳但胜在简单易懂。6.3 后台线程操作数据库导致崩溃Android不允许在主线程执行网络请求但SQLite操作主线程其实可以跑StrictMode默认不拦截不过复杂查询依然可能卡UI。我当时用了Handler或Thread做异步查询于是碰到了另一个问题线程里直接更新UI控件导致崩溃。记住一条铁律子线程不能直接更新主线程的UI。要么用runOnUiThread()要么用Handler要么干脆用AsyncTask虽然官方废弃了但大作业里用起来真方便原理也容易解释。6.4 真机调试时USB连接不稳定如果你用真机调试换了台电脑经常出现adb识别不到设备。通用解法依次执行adb kill-server、adb start-server然后重新插拔手机。如果还不行检查手机上的“USB调试”授权弹窗有没有点允许很多初学者是这里头一关就被卡住。6.5 答辩被问“为什么不用后端”时怎么答这个问题几乎是必问的。疫苗预约看起来应该联一个云端服务为什么你只做了本地SQLite如果你能提前意识到这个问题你可以在设计报告的“非功能需求”里写清楚本项目定位为课程设计演示考虑到部署和维护成本数据存储采用本地SQLite方案便于单机演示和代码阅读。生产环境中可将数据层替换为RESTful API接口对接服务端MySQL数据库并将本地预约逻辑迁移至服务端实现多端数据统一。这是一段既诚恳又专业的回答既承认了当前方案的边界也说明了生产环境的技术路线老师一般听到这里就不会继续深挖了。6.6 PDF报告导出乱码交作业前最后一道坎设计报告写完之后导出PDF时中文字体会变成乱码框框也是高频问题。如果你用Word写报告在导出PDF时勾选“嵌入所有字体”如果用Markdown转PDF建议直接生成HTML再打印为PDF中文字体兼容性更好。还有一种情况是WPS导出的PDF在Mac端预览正常、Windows端乱码这种一般是字体未嵌入导致同样按嵌入字体处理即可。6.7 最后再分享一个很有用的交付习惯无论你怎么封装exe都把APK文件单独放在提交包的根目录里。为什么因为老师如果需要在自己的安卓手机上快速看一眼直接传APK装上最方便。就算exe出了问题APK这个“保底选项”能让你的项目经受住更多验收场景的考验。这是我期末周实测下来最有用的一个习惯没有之一。本文还有配套的精品资源点击获取