简介:面向 Android 开发者与医疗信息化初学者,这是一套智慧医疗 App 的完整工程源码。项目围绕患者信息管理、预约挂号、健康数据跟踪、在线诊疗、药品信息查询等典型功能展开,演示从界面搭建、业务逻辑封装到数据交互的完整开发流程,也展示了医疗类应用对权限、隐私和交互体验的常见处理方式。压缩包共 854 个文件,以 Java 源码、XML 布局与资源配置为主,具体包含 275 个 Java 程序文件、211 个 XML 布局或配置、262 张 PNG 图片,以及 so 动态库和 Gradle 构建配置,包体约 69.59MB,目录划分清晰,适合按模块阅读或二次开发。目前已有 1617 人在 CSDN 浏览学习,若正在准备课程设计、毕业设计或想快速切入移动医疗方向,可通过完整代码理解 Android 常用组件、网络请求、数据存储和屏幕适配的配合方式,并以此为骨架延伸出自己的功能模块。 拿到“智慧医疗App”这个需求,第一反应是不是先把Android Studio装好,然后随手new一个项目,把界面填满再说?我一开始也是这么干的,结果功能堆了几十屏,真正能稳定跑的没几个。这应该不是个例,很多人在android studio开发app项目时,容易把“智慧”理解成炫技,把“医疗”理解成一堆看上去很专业的图标。所以这篇博文我想用一次真实项目经历来复盘:用Android开发一个面向慢病管理场景的智慧医疗App,从需求拆解、技术选型、蓝牙硬件接入,到上架发布,整个链路里踩过的坑和最终保留下来的方案。全文不写悬浮理论,尽量给出可复现的操作路径,适合刚入门Android开发,或者公司、医院、学校正准备孵化医疗类App的开发者拿来当参考。
1. 需求边界与技术选型:把“智慧医疗”四个字翻译成功能清单
1.1 需求梳理:先砍掉80%看起来很酷的伪需求
我见过太多医疗类App死在“功能太多、体验太碎”上。项目启动的第一周,我没有急着写代码,而是把市面上的挂号和问诊应用都过了一遍,拿真实用户的使用场景去反推功能优先级。最后确定下来的核心模块只有三个:BLE体征数据记录、用药提醒、报告与问诊记录查询。
预约挂号这类功能虽然看起来很“智慧医疗”,但它牵扯到HIS系统对接、号源同步、支付结算,一个小团队根本扛不住。AI问诊和智能导诊我也做了技术评估,结论是原型可以Demo,但真要用在家庭健康管理里,容易产生误导,风险太高,直接砍掉。宁可把三个模块做深,也不做十个半成品。这个项目面向的场景是慢病家庭管理,血压计、体脂秤这类BLE外设的接入优先级最高,用药提醒次之,报告查询本质上是一个列表加详情页,技术含量相对低,但必须做稳。
1.2 原生开发的原因:BLE与后台任务的硬性要求
这里要回答一个很多人都会问的问题:为什么不用Flutter或者React Native?
我当时也认真对比过。如果项目只是做界面展示和普通的HTTP请求,跨平台方案完全没有问题,甚至效率更高。但智慧医疗App有两个硬指标:低功耗蓝牙外设接入、后台长时间稳定运行。BLE这块,跨平台框架的封装层出过不少问题,比如连接不稳定、回调丢失、多设备并发时状态错乱,最后定位问题反而比原生更花时间。系统级权限适配,比如Android 12和13的蓝牙权限拆分,原生在manifest和运行时处理上都更直接。再加上团队当时的主力语言就是Kotlin,最后决定用原生Android Studio开发,这个选择在后来的联调阶段被证明是对的——遇到蓝牙协议栈的问题,起码能直接读系统源码,不用先绕一圈框架。
1.3 工程初始化的坑:AS版本与AGP、Gradle的三角关系
用Android Studio建项目本身不难,难的是版本匹配。我当时用的是Android Studio Hedgehog(2023.1.1 Patch 2),这个版本默认绑定AGP 8.2,需要Gradle 8.2以上。很多人一建完项目就报“Minimum supported Gradle version is 8.2”,或者“Could not resolve com.android.tools.build:gradle:8.2.0”,大多数时候不是网络问题,而是gradle-wrapper.properties里distributionUrl对应的Gradle版本太老。
另外一个隐蔽问题是JDK版本。Hedgehog内置的是JDK 17,如果本机装了多个JDK,Gradle编译时会报“Unsupported class file major version 65”。我当时在新电脑上复现项目,花了十几分钟排查才发现是File > Project Structure > SDK Location里的JDK路径被指到了JRE 8上。经验就一条:不要盲目升级到最新的AGP,先确认当前Android Studio版本官方绑定的是哪一版AGP,然后按官方兼容表去配Gradle,稳定压倒一切。
2. MVVM架构落地:以“体征记录”模块为例走一遍完整链路
2.1 技术栈选型:Retrofit、Room、Hilt为什么是安全牌
技术选型这件事,我倾向于选生态成熟、社区踩坑量大的组合,而不是最新最潮的框架。这个项目里用的是Retrofit + OkHttp做网络层,Room做本地数据库,Hilt做依赖注入,协程配合Flow处理异步,WorkManager处理后台同步任务。
Retrofit的优势不用多说,协程挂起函数的支持非常顺,接口定义简单,配合OkHttp的拦截器可以在调试阶段直接看到请求和响应。Room看起来比原生的SQLiteOpenHelper多学一层概念,但上手之后维护成本低太多,编译期就能校验SQL语法,还能直接返回Flow做响应式数据更新。Hilt这点,很多人觉得学起来麻烦,但我更在意的是它能在编译期发现问题,不用等到运行时到处找空指针。
2.2 代码分层演示:从DAO、Repository到ViewModel
以“体征记录”模块为例,分层结构是这样的:Activity/Fragment只面向ViewModel,ViewModel只依赖Repository,Repository负责判断数据来源,它既可以从Room拿缓存,也可以从Retrofit拿网络数据。
先看DAO这层:
@Dao interface HealthRecordDao { @Query("SELECT * FROM health_record ORDER BY measure_time DESC LIMIT :limit OFFSET :offset") fun pagedQuery(limit: Int, offset: Int): Flow<List<HealthRecord>> @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(records: List<HealthRecord>) }Repository给ViewModel暴露的是Flow,Activity在onCreate里通过repeatOnLifecycle去收集StateFlow,这样页面旋转不会丢数据,也不会因为忘记取消协程导致泄漏。实测中最大的感受是:别在Activity里直接写数据访问代码,哪怕只是临时调试。你以为是省事,结果后面要加缓存、要改数据源的时候,所有调用点都要一起改,痛苦指数成倍增加。
2.3 离线优先与同步策略:WorkManager不能用来处理高频任务
慢病管理场景里,用户可能家里没有Wi-Fi,也可能在乡镇等网络不稳定的地方,所以数据必须本地先落库,网络恢复后再同步。Room在这套逻辑里不只是缓存,它其实是数据主库,远端只是同步对端。
同步任务我用了WorkManager,但这里有个很大的误区:WorkManager适合异步、延迟、可重试的任务,比如每6小时上报一次未同步记录,它不适合高频实时任务。如果用户的血压计每五分钟就测量一条数据,实时上传必须走协程加OkHttp的重试机制,WorkManager的最小周期是15分钟,且不是精确调度。上线前我特意整理了同步策略表格,把“实时上传”“定时上报”“失败重试”分别划给协程、WorkManager、自定义RetryInterceptor。想清楚这个边界,后续就不会写出一堆莫名其妙的定时器。
3. 蓝牙外设接入与隐私合规:权限、连接、加密三个硬骨头
3.1 BLE接入完整链路:从扫描到解析的那几个关键回调
BLE接入的成熟套路是:扫描设备、连接Gatt、发现服务、订阅通知、接收特征值、解析字节流、入库。流程本身不复杂,复杂的是每个环节都有坑。
扫描的时候,回调频率非常高,同一台设备一瞬间会冒出来好多次,必须按MAC地址去重。连接之后,所有Gatt操作必须在同一线程串行执行,不能同时去读多个特征值,否则会触发“Gatt operation already in progress”。数据接收在onCharacteristicChanged回调里拿到byte[],这一步是重灾区,很多血压计的数据协议是IEEE-11073编码,直接转String得到的就是乱码,得根据厂商SDK文档拆分字节,解析出收缩压、舒张压、心率。
override fun onCharacteristicChanged( gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic ) { val data = characteristic.value // 根据具体协议解析,例如先取flag,再判断有效字节 val systolic = data[3].toInt() and 0xFF val diastolic = data[4].toInt() and 0xFF }我还遇到过一次很奇怪的现象:部分设备在蓝牙设置里能看到,但App扫描不到。排查了半天,发现是手机定位服务没开。Android要求BLE扫描必须同时满足定位权限和定位开关,这两者都满足才可能扫到设备。这个坑在自查清单里必须排在前面。
3.2 权限适配排查链路:Android 12/13 权限模型变化
这个项目让我最抓狂的就是权限适配,现象是:Android 10上运行正常的包,装到Android 13真机上,一按“扫描设备”就闪退。
第一步,先抓logcat,看到SecurityException,提示缺少BLUETOOTH_CONNECT权限。于是我在AndroidManifest里补了这个权限。第二步,发现补完还是崩,原因是Android 12以上不仅要在manifest声明,仍要在代码里动态申请。第三步,继续排查,Android 12以后BLE扫描还需要BLUETOOTH_SCAN和ACCESS_FINE_LOCATION联合授权,权限弹窗给用户解释为什么要定位权限。第四步,Android 13又新增了POST_NOTIFICATIONS通知权限,如果不弹窗授权,用药提醒类的核心功能就静默失效。
| 场景 | 最低版本 | 需要权限 |
|---|---|---|
| 经典蓝牙连接 | Android 8-11 | BLUETOOTH、BLUETOOTH_ADMIN、ACCESS_FINE_LOCATION |
| BLE扫描 | Android 12+ | BLUETOOTH_SCAN、ACCESS_FINE_LOCATION |
| 连接外设 | Android 12+ | BLUETOOTH_CONNECT |
| 通知提醒 | Android 13+ | POST_NOTIFICATIONS |
把这张表贴在项目Wiki里之后,团队再也没因为权限问题扯皮。我的建议是权限申请尽量用封装库统一管理,不要在十几个页面里各写一份运行时申请逻辑,不然用户拒绝一次之后,你连怎么引导他去设置页开启权限都找不到入口。
3.3 数据安全与隐私合规:体征数据不能裸奔
血压、心率、体脂这些数据属于敏感健康数据,不能当成普通业务字段一样存明文。Room默认是不加密的,我在这套项目里对敏感列做了AES-GCM加密,密钥放在Android Keystore里,而不是硬编码在代码里。有人图省事把密钥写在SharedPreferences里,这个基本等于没锁门。
传输层全部走HTTPS,正式环境开启证书校验,这个不用多说。展示层的脱敏也做了,比如报告页面里姓名显示“张*”,手机号显示“138****8888”,这一条在审核和用户信任度上都很加分。最后是隐私政策,App首次启动就要弹隐私协议弹窗,明确说明收集了哪些数据、存在哪里、用来干什么。即使是学习用的演示项目,也应该在应用里写清楚“数据仅保存在本地,不会上传”,这是一种职业习惯。
4. 联调、打包与发布:从“本地能跑”到“上线可用”的最后一公里
4.1 真机调试与抓包失败的几个常见根源
项目开发到一半,需要联调接口,这是“App抓包失败”问题的高发期。最常见的现象是:抓包工具打开了,代理也设置了,但App里的请求全部超时,或者直接提示证书错误。
根源通常有三个。第一,Android 7及以上默认不信任用户CA证书,抓包工具自己生成的证书在App里根本不被认可,解决办法是在networkSecurityConfig里只对debug构建信任调试证书,发布包不要带这个配置。第二,手机和电脑不在同一个局域网,或者代理端口设置不一致,这种问题最坑,因为报错信息跟真实网络问题一模一样。第三,OkHttp默认不走系统代理,抓不到包也别慌,直接在OkHttpClient里加一个日志拦截器,只在debug构建下打印请求和响应,比依赖外部工具更直接。
4.2 模拟器“虚拟设备无效”的排查链路
网上一直有人问“为什么我的Android Studio虚拟设备无效”,我实际排查过一次,现象是AVD能创建,但点启动之后一直黑屏,过一会儿提示“The emulator process for AVD was killed”。
排查链路是这样走的。第一步,打开任务管理器看虚拟化是否被占用。Windows上老的HAXM已经停止维护,推荐使用Android Emulator Hypervisor Driver,如果开了Hyper-V,两者会冲突,需要二选一。第二步,在命令行执行emulator -accel-check,可以直观看到硬件加速状态。第三步,确认系统镜像和SDK Platform版本匹配,Apple Silicon的机器要下载arm64镜像,别直接复制x86_64镜像。第四步,删除~/.android/avd/下损坏的AVD重新创建。
补充一个独门经验:模拟器黑屏还可以检查一下内存分配,给模拟器分配512MB以下内存加上冷启动,通常能解决旧镜像黑屏问题。蓝牙外设模拟是模拟器干不了的,BLE真机调试绕不开,所以不是所有功能都能在模拟器上验证。
4.3 签名、混淆、多渠道打包与上架注意事项
打包发布阶段,最容易踩的是混淆配置。开启minifyEnabled之后,R8会把没用到的代码干掉,但Gson、Room、Hilt这些用到反射的库,模型类不加keep规则,上线第一分钟就会闪退。后来我强制团队在数据模型上统一加@Keep注解,再配合proguard-rules.pro做兜底,这类问题才算根治。
签名文件一定要用专用的keystore,别把debug签名拿去上架,密钥文件不要提交到Git仓库。多渠道这块,我用productFlavors区分不同环境,从开发环境、测试环境到生产环境的接口地址和包名都分开,省去了每次打包手动改配置的麻烦。上架时要准备隐私政策页面、应用图标、截图,以及商店后台要求的相关材料,涉及健康数据的应用对隐私说明会特别敏感,这块别抱侥幸心理。
4.4 上线后的监控:崩溃日志和性能数据要早接入
App上线不等于结束,恰恰是另一个开始。我习惯在第一个版本就接入崩溃监控SDK,不做的话,用户端发生闪退你完全不知道。接入之后第一天就定位到了两个问题,一个是某款国产ROM回收后台时把用药提醒的AlarmManager任务清了,另一个是低内存设备上图片加载导致的OOM。
用药提醒模块这种系统级功能,最怕进程被回收。后来我把提醒逻辑改成前台服务加通知栏常驻通知,同时引导用户对应用开启自启动权限,问题才明显收敛。对医疗类App来说,稳比新重要得多。你宁可功能少一个,也不要让用户产生“这个App连个提醒都不准时”的信任危机。
整套项目做下来,我个人体会最深的一点是:智慧医疗App真正难的不是某个界面做得多漂亮,而是从硬件外设到数据存储再到系统权限这条链路的稳定性和合规性。如果你正在规划类似项目,建议一开始就把权限模型和加密方案定下来,不要等代码写了一半再回头补,那个改造成本真的会让人崩溃。另外给刚上手Android开发的朋友一个建议:不要一上来就追最新版的Android Studio和AGP,先把官方兼容表确认好,把手头环境跑通,再谈技术选型和架构优化。
本文还有配套的精品资源,点击获取