简介:基于安卓的多方交互老年人慢病管理App开发项目,面向安卓开发学习者、课程设计与毕业设计人员。项目采用安卓与Java技术栈,实现医生端与患者端双角色完整功能:医生端涵盖患者档案、慢病管理、在线交流与健康资讯,便于医生维护患者信息与推送管理建议;患者端支持在线咨询、健康报告、电子病历及特别提醒,帮助老年用户及其家属便捷查看健康数据。系统通过多方交互设计,连接医生、患者与家属,实现慢病管理的远程协作与信息同步。压缩包共1204个文件,大小16.03MB,包含Java源码、Vue前端页面、XML配置、PNG图片及SQL数据库脚本等;Java文件承担Android业务逻辑,Vue用于后台管理界面,SQL为数据库初始化脚本,并附有说明文档辅助理解与部署。当前已有113人学习。资源提供完整源码、数据库与说明,可直接导入开发环境运行,也可按模块二次开发,适合作为课程设计、毕业设计或慢病管理项目实战的参考范例。
1. 这个 Android 慢病管理 App 到底要交什么活
把“基于 Android 的多方交互的老年人慢病管理 App 开发”压缩成一句话,就是以慢病数据为中心的多人协同工具:老年人在 Android 端录入血压、血糖、用药记录,子女或社区医生能远程看到趋势并及时收到提醒。拿到手的是“源码 + 数据库 + 说明”的打包素材,交差之前要想清楚,验收时真正看的不是图标好不好看,而是当场演示时数据真的落到了 SQLite,提醒真的弹得出来。
App 开发入门者容易把“多方交互”想成聊天室,这是最典型的跑偏。多方交互在课程设计语境下,通常只是三个角色、两种查看权限和一条异常判断链路:患者管录入,子女管查看,医生管建议。本文按这个口径把结构、建表、代码通讯和收尾逐一展开,排除掉需要真实短信或云服务的复杂方案,保证一台 Android 模拟器就能跑通。
2. 多方交互模型选型:为什么不是多对多聊天,而是主要照顾者模式
2.1 三种交互模型的对比与判定:你手里的源码更接近哪一种
“多方交互”在需求文档里往往只有一句话,落到设计上却有三种明显不同的模型。第一种是一对一提醒型,App 只有患者和系统两个端点,系统按时间表发通知,患者被动接收。第二种是主要照顾者模式,患者、子女、医生三方参与,子女承担日常监督,医生只在数据异常时介入。第三种是多对多社区型,类似社交平台,患者之间互相点赞、评论、共享经验。
课程设计和毕业设计里最常见的是一对一提醒型或主要照顾者模式,后者更贴合标题里的“多方交互”。判断方法很简单:看数据库里有没有一张包含 relation_type 字段的关系表,以及看提醒触发后是否有第二个人能收到同样的通知。
| 模型 | 角色数 | 数据流向 | 实现成本 | 典型功能 |
|---|---|---|---|---|
| 一对一提醒型 | 1 | 患者 → 本地数据库 | 低 | 用药提醒、记录查询 |
| 主要照顾者模式 | 3 | 患者录入,子女/医生查看 | 中 | 家庭绑定、趋势分享、异常通知 |
| 多对多社区型 | 多 | 全员互相可见 | 高 | 动态、评论、点赞、搜索 |
选择主要照顾者模式的原因很实在:老年人慢病管理的核心矛盾不是“记录不够多”,而是“记录没人看”。让子女通过同一个 App 查看曲线,比让老人主动转发截图更可靠,也避开了实时聊天和高并发带来的工程复杂度。角色少,权限边界就清晰,期末答辩时也更容易讲清楚“谁有什么权限、为什么这样设计”。
2.1.1 角色与权限的最小建模
先不急着写界面,把角色定义成代码里的枚举或常量。常见做法是在 user 表里存 role 字段,在 family_relation 表里存关系类型。
data class User( val userId: Long, val username: String, val role: String, // PATIENT / CAREGIVER / DOCTOR val relationType: String // SELF / FAMILY / MEDICAL )role 决定页面入口,relationType 决定可见范围。比如同样是 CAREGIVER,relationType 为 FAMILY 的只能看到绑定老人的数据,看不到其他患者记录。这不是多余设计,而是评审老师最容易追问的地方:如果没有这张关系表,任何登录用户都能查看全部健康记录,权限设计直接不合格。
2.2 权限边界与通知机制:Android 端交互的三个做法
多方交互在移动端的落地点不只是权限,还有通知机制。同一台手机上,“患者录入完成”和“子女需要知道异常”这两个事件,依赖本地通知就能完成闭环,不需要服务器参与。
通知链路的三步做法是:先建 NotificationChannel,再构造 NotificationCompat.Builder,最后通过 NotificationManagerCompat.notify 发出。下面这段代码是提醒模块的最小实现,可直接放进工具类。
fun sendReminder(context: Context, reminder: Reminder) { val channelId = "medication_reminder" val manager = NotificationManagerCompat.from(context) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( channelId, "用药提醒", NotificationManager.IMPORTANCE_HIGH ) manager.createNotificationChannel(channel) } val intent = Intent(context, ReminderDetailActivity::class.java) intent.putExtra("reminder_id", reminder.id) val pendingIntent = PendingIntent.getActivity( context, reminder.id, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) val notification = NotificationCompat.Builder(context, channelId) .setSmallIcon(R.drawable.ic_notify) .setContentTitle("用药提醒") .setContentText("${reminder.drugName} ${reminder.dosage}") .setContentIntent(pendingIntent) .setAutoCancel(true) .build() if (ActivityCompat.checkSelfPermission( context, Manifest.permission.POST_NOTIFICATIONS ) == PackageManager.PERMISSION_GRANTED ) { manager.notify(reminder.id, notification) } }这段代码里有几个参数值得注意。channelId 必须是常量,不能每次调用都 new 一个字符串,否则通知渠道会重复创建。PendingIntent.FLAG_IMMUTABLE 是 Android 12 之后的强制要求,漏掉会直接崩溃。POST_NOTIFICATIONS 是 Android 13 新增的运行时权限,旧版项目如果只配置了 Manifest 没做运行时申请,在 Android 13 以上机型会静默失败。
3. 用 Android Studio 搭出项目骨架:权限、依赖与页面入口
3.1 创建工程与权限配置:三个必填的 Manifest 项
用 Android Studio 新建项目时选 Empty Views Activity,语言选 Kotlin,minSdk 设到 24 或 26 均可,targetSdk 建议跟着当前稳定版走。模拟器直接用 Android Studio 自带的 AVD,选 Pixel 系列镜像,比折腾第三方模拟器省时间。新版 Android Studio 里可以通过 Settings 的 Plugins 菜单切换中文语言包,对初次接触 Android 工程结构的读者更友好。
AndroidManifest.xml 里三个权限项不能少:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.INTERNET" />POST_NOTIFICATIONS 是通知权限,Android 13 以上必须动态申请。RECEIVE_BOOT_COMPLETED 用来支持重启手机后恢复用药提醒,没有它在开发时感觉不到问题,真机重启后所有闹钟都会丢。INTERNET 权限在纯本地项目里看似没用,但后面做趋势图加载或升级成服务端同步时会用到,提前写上省得改。运行时申请代码放在 MainActivity 的 onCreate 里,用 ActivityResultContracts.RequestPermission 即可。
3.2 Gradle 依赖的三个固定版本
依赖版本建议固定,不要用动态版本号。课程设计最大的坑不是依赖冲突,而是版本号漂移导致的问题无法复现。下面三行是慢病管理 App 最常用的依赖,写在模块级 build.gradle 的 dependencies 里。
implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.11.0' implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'appcompat 和 material 提供页面控件与主题样式,MPAndroidChart 用来画血压、血糖趋势折线图。v3.1.0 是 MPAndroidChart 最稳定的公开版本,后续项目里直接引用这个版本不会出现兼容性问题。加完依赖后 Sync 一次,如果下载超时,检查 Gradle JDK 版本是否和 Android Studio 要求一致。
3.3 页面入口与角色跳转
页面结构按角色拆:登录页 → 患者首页 → 记录页 / 趋势页 / 家庭绑定页;子女登录则直接进入所绑定患者的趋势页。跳转用 Intent 传值即可,不引入 Navigation 组件,答辩时更容易解释。
val intent = Intent(this, TrendActivity::class.java).apply { putExtra("targetUserId", currentUserId) putExtra("viewerRole", "CAREGIVER") } startActivity(intent)currentUserId 在登录成功后写入 SharedPreferences,后续所有查询都以它为条件。viewerRole 决定趋势页顶部是否显示“异常通知”按钮,这是多方交互在 UI 层的第一道卡口。
4. 数据库表结构设计与 SQLite 增量建档方案
4.1 三张核心表的字段设计与建表语句
慢病管理 App 的数据库不需要复杂建模,三张表足够:user 存用户,health_record 存健康指标,medication_reminder 存用药计划。其中 health_record 是数据量最大的表,每次测量生成一条记录,频繁插入和按时间查询是主要压力。
CREATE TABLE user ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL UNIQUE, password TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'PATIENT', relation_type TEXT NOT NULL DEFAULT 'SELF', invite_code TEXT ); CREATE TABLE health_record ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, record_type TEXT NOT NULL, value REAL NOT NULL, unit TEXT DEFAULT '', measure_time INTEGER NOT NULL, note TEXT DEFAULT '', FOREIGN KEY (user_id) REFERENCES user(id) ); CREATE TABLE medication_reminder ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, drug_name TEXT NOT NULL, dosage TEXT DEFAULT '', hour INTEGER NOT NULL, minute INTEGER NOT NULL, enabled INTEGER DEFAULT 1, FOREIGN KEY (user_id) REFERENCES user(id) );record_type 用 TEXT 而不是单独建指标字典表,是因为血压、血糖、心率这些类型的数量在项目周期内基本固定,拆表反而增加 JOIN 复杂度。measure_time 存 Unix 毫秒时间戳,排序和区间查询都比字符串高效。medication_reminder 里的 hour 和 minute 拆开存,方便 AlarmManager 直接读取,不用解析字符串。
4.2 用 SQLiteOpenHelper 实现增量建档
“增量建档”指的是 App 首次启动时创建表并写入基础数据,后续启动只检查版本号,不重复建表。实现方式是 SQLiteOpenHelper 的 onUpgrade 里用版本号控制,但课程设计阶段更常见的是 onUpgrade 里直接 DROP TABLE 重建,这种做法会丢数据,不适合慢病记录场景。
class HealthDbHelper(context: Context) : SQLiteOpenHelper(context, "health.db", null, 2) { override fun onCreate(db: SQLiteDatabase) { db.execSQL(CREATE_USER_TABLE) db.execSQL(CREATE_RECORD_TABLE) db.execSQL(CREATE_REMINDER_TABLE) insertDefaultDict(db) } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { if (oldVersion < 2) { db.execSQL("ALTER TABLE user ADD COLUMN invite_code TEXT") } } private fun insertDefaultDict(db: SQLiteDatabase) { val values = ContentValues().apply { put("username", "admin") put("password", "123456") put("role", "DOCTOR") put("relation_type", "MEDICAL") } db.insert("user", null, values) } }onCreate 只调用一次,适合放建表语句和默认管理员账号。onUpgrade 里用 if 分支而不是 DROP 重建,保证用户升级 App 后历史记录还在。数据库版本号从 1 增加到 2 时,新加的 invite_code 字段通过 ALTER TABLE 补上,这是 Android 数据库迁移的标准姿势。
4.3 同步与备份的取舍:为什么不建议在课程设计阶段做远程同步
看到“数据库 + 多方交互”两个词,很容易联想到 MySQL 和服务端接口。但从课程设计的验收标准来看,远程同步的性价比很低:网络不稳定、联调耗时、答辩现场可能没网。数据库同步工具确实能解决部分问题,但引入第二套系统会让整个项目的复杂度翻倍。
| 方案 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| 纯 SQLite | 简单可靠,离线可用 | 数据只在单机 | 完整记录与查询功能 |
| CSV/JSON 导出 | 用户可自行分享 | 不自动、需手动 | 数据备份与交作业 |
| FTP/网盘同步 | 实现简单 | 安全性差 | 不推荐 |
| 服务端 API | 真正多方实时共享 | 需要服务器与联调 | 毕业后扩展方向 |
如果必须展示“多方”,可以用 App 内的家庭组模拟远程:子女账号登录后查询同一 SQLite 数据库。等到答辩时再口头说明“生产环境会替换为服务端”,这是最稳妥的路径。
5. 多方交互的两个核心动作:家庭绑定与健康数据回传
5.1 家庭绑定实现:邀请码的生成与校验流程
多方交互的第一个动作是把两个账号绑定到一个家庭组。不做实时通信,通过邀请码完成绑定是最常见的离线方案:患者手机生成六位邀请码,子女在 App 里输入邀请码完成绑定。
fun generateInviteCode(seed: Long): String { val chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" val sb = StringBuilder() var v = seed repeat(6) { sb.append(chars[(v % chars.length).toInt()]) v /= chars.length } return sb.toString() }邀请码字符集去掉了 I、O、0、1,避免老人输入时混淆。seed 用当前毫秒时间戳或 userId 均可,时间戳生成的结果更随机。绑定流程是:患者端显示 invite_code,子女端输入后,在 family_relation 表插入一条记录,双方关系即建立。
5.1.1 绑定关系写库代码
fun bindFamily(db: SQLiteDatabase, caregiverId: Long, patientId: Long) { val values = ContentValues().apply { put("caregiver_id", caregiverId) put("patient_id", patientId) put("relation_type", "FAMILY") put("bind_time", System.currentTimeMillis()) } db.insert("family_relation", null, values) }family_relation 表在上一章的建表语句里没有出现,这是刻意的:先把核心三张表跑通,再按模块追加关系表。bind_time 字段用来处理“解绑后再绑定”的时间线问题,按最新 bind_time 查询有效关系即可。
5.2 让子女看到趋势:MPAndroidChart 折线图接入
多方交互的第二个动作是健康数据回传。在不做服务端的前提下,回传的含义是子女登录后能立即看到同一数据库里老人的血压曲线。折线图比数值列表更直观,也更有答辩效果。
val chart = findViewById<LineChart>(R.id.chart) chart.description.isEnabled = false val entries = records.mapIndexed { index, r -> Entry(index.toFloat(), r.value.toFloat()) } val dataSet = LineDataSet(entries, "收缩压").apply { mode = LineDataSet.Mode.CUBIC_BEZIER color = Color.rgb(233, 84, 82) setCircleColor(Color.rgb(233, 84, 82)) lineWidth = 2f } chart.data = LineData(dataSet) chart.invalidate()mode 设为 CUBIC_BEZIER 会生成平滑曲线,但数据点少于五个时曲线会明显失真,所以建议只在查询结果超过三条时启用。加载趋势图时,数据量小可以不加进度条,但如果设计成模拟网络加载,用 MaterialProgressBar 包裹图表区域会更接近真实 App 的体验。
5.3 异常阈值的判定规则:谁来判断、怎么提醒
数据回传后不能只展示,还要有判定。判断逻辑放在查询层而不是 UI 层,确保老人端和子女端看到的是同一套结果。
| 指标 | 正常范围 | 异常阈值 |
|---|---|---|
| 收缩压 | 90–140(mmHg) | ≥ 180 或 ≤ 80 |
| 舒张压 | 60–90(mmHg) | ≥ 110 |
| 空腹血糖 | 3.9–6.1(mmol/L) | ≥ 7.0 |
| 静息心率 | 60–100(次/分) | ≥ 120 |
判定代码在插入记录时执行,命中后走通知渠道发送告警:
fun checkAbnormal(recordType: String, value: Float): Boolean { return when (recordType) { "blood_pressure_systolic" -> value >= 180f || value <= 80f "blood_pressure_diastolic" -> value >= 110f "blood_glucose" -> value >= 7.0f "heart_rate" -> value >= 120f else -> false } }这里特意把阈值写死在代码里,而不是放在数据库配置表,是因为在课程设计阶段,一份可读的判断方法比可配置系统更容易被评审接受。后续升级方向是改成从数据库读取阈值,但那是“系统设计”层面的加分项,不是必选项。
6. 上架与调试前的三个收尾技巧:日志验证、正式签名与发布成本
6.1 用 Logcat 和 dumpsys 验证交互链路
功能写完先别急着装真机,用日志验证数据链路能省掉大量 UI 调试时间。在插入 health_record 和发送通知的代码位置各加一行 Log.d,然后用 adb 命令过滤。
adb logcat -s HealthApp:D adb shell dumpsys notification --noredact | grep 用药提醒-s HealthApp:D只显示 HealthApp 标签下的 Debug 日志。dumpsys 命令直接读系统通知服务,能看到通知是否真的发出。如果 logcat 显示插入成功但 dumpsys 里没有通知,优先查 POST_NOTIFICATIONS 运行时权限是否已授予。
6.2 正式签名与版本号:最容易漏的一步
调试模式安装的 App 用的是 debug 签名,换一台手机就会因签名不一致无法覆盖安装。生成正式签名的命令是:
keytool -genkeypair -v -keystore healthapp.jks -keyalg RSA -keysize 2048 -validity 10000 -alias healthapp生成后修改模块级 build.gradle 的 signingConfigs,把 storeFile 指到 jks 文件路径,然后执行 assembleRelease 打包。这里要留意一点:签署文件名和别名不要用默认值,防止后续换电脑时搞混密钥。
6.3 上架成本与发布检查清单
把“开发一个 App 并上架大概要多少钱”换成维护成本看会更清楚:课程作业阶段只需要真机安装验证,不需要也尽量不要上架。国内应用商店对医疗健康类 App 的资质审核很严格,个人开发者很难走通,且各商店政策不同,成本和周期不可控。
| 场景 | 签名要求 | 服务器 | 成本 |
|---|---|---|---|
| 模拟器验收 | 调试签名即可 | 无 | 0 |
| 真机演示 | 正式签名更稳妥 | 无 | 0 |
| 应用市场上架 | 正式签名 + 软著/资质 | 视功能而定 | 需要提前评估 |
上架不是这个项目的终点,把 assembleRelease 产出的 APK 装到老人手机上,再用返回键退出 App 后重新进入,确认提醒记录仍然存在,才算是把多方交互的最后一环接上。
本文还有配套的精品资源,点击获取