
去年我拿到《基于Android的智能老人生活辅助应用设计与实现》这个毕设题目时第一反应不是“好不好做”而是“终于不是商城和后台管理系统了”。说实话计算机毕设里十有八九是点餐、购物、打卡答辩PPT翻来翻去都是增删改查而这个题目不一样它要处理定位、短信、通知、后台任务、权限适配还要把界面做到六十岁的人能看清、按得动。整个项目做下来Android开发里最容易翻车的几个点我几乎踩了个遍也正因为这样答辩时能讲的东西远比想象中多。这篇文章就把我的选题思路、模块设计、核心源码逻辑、真机调试里遇到的诡异问题完整复盘一遍给正在准备同类安卓毕设的同学一个可以直接参考的模板也送给想给家里老人做点东西的朋友。1. 老人生活辅助应用到底在解决什么问题从生活场景倒推需求1.1 为什么“老人App”是一个被低估的真需求这两年类似的选题特别多有些同学会担心“会不会太老套”。我做完之后的感受是题目老不老套不重要关键是你能不能把需求讲清楚。老人辅助类应用和普通管理系统的本质区别在于它的每一个功能都能对应到一个具体的、随时可能发生的真实场景。先说一个最常见的场景子女白天上班老人在家独居降压药一天吃三次经常吃完就忘出门买菜走远了一点想联系又怕麻烦子女万一在家里摔倒或者突发胸闷手机就在旁边但按不准联系人。这些问题听起来琐碎但落到App设计上就是几个硬指标紧急求助必须一键触发、吃药提醒必须准点到达、子女远程必须知道老人大概位置、界面上的字必须够大。我家里也有老人所以做这个题目的时候不是先打开Android Studio写代码而是先把场景写在本子上一个一个问“这个场景在手机上怎么解决”。这个过程其实就是需求分析也是论文里第一章最重要的内容。很多同学毕设做完了才发现功能堆了一堆但不知道解决什么问题就是跳过了这一步。1.2 需求拆分四个核心模块是怎么定下来的我把场景倒推出来的需求整理成了一张表格后续整个项目的开发都是围着这张表展开模块具体功能解决的真实场景紧急求助一键SOS、短信发送位置、快速拨号老人突发不适或摔倒需要第一时间联系子女健康管理血压/心率手动记录、数据图表展示老人定期测血压子女远程了解健康趋势生活辅助吃药提醒、喝水提醒、日程提醒老人容易忘事需要准点语音/通知提示亲情互动位置共享、状态查看、子女远程留言子女想知道老人位置和最近状态减少焦虑这个结构不是从网上抄的是根据“独居老人一天的生活”一条条列出来的。你甚至可以把这个推导过程写进论文的“需求分析”答辩老师会觉得你确实思考过而不是上来就写代码。需要说明的是健康管理这块我没有做蓝牙血压计对接因为毕设周期有限而且硬件对接会让整个项目复杂一个量级。我采用的是手动录入图表展示演示效果也足够直观。如果你有精力预留一个接口以后扩展硬件测量是完全可以的但不要一开始就铺太大。2. 总体架构与数据流从点击按钮到通知弹出来中间发生了什么2.1 为什么我坚持给毕设用三层架构很多同学写安卓毕设喜欢“Activity里一把梭”一个页面几百行代码业务逻辑和界面混在一起。我当时就在想这种写法开发起来确实快但论文第四章的“系统设计”基本没法写而且功能一多光findViewById就能把人逼疯。所以我老老实实用了三层结构界面层Activity、Fragment、XML布局、业务层Manager类比如SosManager、RemindManager、HealthRecordManager、数据层Room数据库、SharedPreferences、以及预留的网络接口。这个结构不是赶时髦它有一个很现实的好处答辩的时候你可以清晰地告诉老师“界面层只负责展示业务层封装逻辑数据层专注存储”这就体现了软件工程的基本素养。当时项目里有一个特别典型的例子吃药提醒跨了两个模块SOS要读紧急联系人健康记录要读用户信息。如果全部写在Activity里改一个字段全得跟着动。拆成Manager之后每个模块只通过方法调用互不干扰。后面加功能的时候感受尤其明显我在做“位置分享”时只改了几十个文件里的三个其他完全不用动。2.2 用一个SOS场景把数据流串起来理解一个系统的运作最快的方式就是追一条完整的数据链路。拿最核心的SOS功能举例整个流程是这样的老人点击主界面“SOS”大按钮设备震动反馈防止老人以为没点到重复点击系统获取当前GPS定位优先拿最近一次缓存定位保证速度拼接求助短信紧急联系人电话 当前时间 经纬度通过SmsManager发送短信同时跳转到系统拨号界面本次求助记录写入本地数据库的sos_record表方便回溯这个链路看起来简单但每一步都有对应的代码模块震动是Vibrator定位是LocationManager短信是SmsManager记录是Room。答辩的时候拿这个流程一讲老师立刻知道你的系统是完整跑通的而不是零散Demo。2.3 Room数据库表设计字段不是越多越好老人辅助应用的数据量其实不大设计上应该以“够用、好懂”为原则。我当时设计了四张表简单到可以直接看懂-- 用户表 CREATE TABLE user_info ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER, blood_type TEXT, emergency_contact TEXT, -- 紧急联系人电话 medical_history TEXT -- 病史备注 ); -- 健康记录表 CREATE TABLE health_record ( id INTEGER PRIMARY KEY AUTOINCREMENT, record_type TEXT, -- blood_pressure / heart_rate value TEXT, -- 例如 120/80 record_time TIMESTAMP ); -- 提醒记录表 CREATE TABLE remind_record ( id INTEGER PRIMARY KEY AUTOINCREMENT, remind_type TEXT, -- medicine / water / schedule remind_time TEXT, is_repeat INTEGER, -- 是否重复 repeat_interval TEXT, enabled INTEGER ); -- SOS记录表 CREATE TABLE sos_record ( id INTEGER PRIMARY KEY AUTOINCREMENT, sos_time TIMESTAMP, location TEXT, is_responded INTEGER -- 是否已处理 );这套表设计的核心思路是每张表对应一个模块每个字段都能在界面上找到入口。写论文画ER图也方便直接把这些字段往图里填就行。不要为了显得高级加一堆外键单机App的毕设项目完全没必要搞那么复杂。3. 四个关键功能的源码落地代码比你想的更简单3.1 一键SOS串起短信、拨号和定位的核心逻辑SOS是整个项目的灵魂也是演示时最容易出效果的地方。我用Java实现核心逻辑可以压缩到几十行先看主要代码骨架public class SosManager { public void triggerSos(Context context) { // 1. 震动反馈防止误触也防止没按到 Vibrator vibrator (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); if (vibrator ! null vibrator.hasVibrator()) { vibrator.vibrate(VibrationEffect.createOneShot(300, VibrationEffect.DEFAULT_AMPLITUDE)); } // 2. 获取最近一次可靠定位优先速度快 Location location getLastKnownLocation(context); String locationText; if (location ! null) { locationText 纬度: location.getLatitude() ,经度: location.getLongitude(); } else { locationText 位置获取失败请电话联系; } // 3. 从数据库拿紧急联系人电话 String contact getUserEmergencyContact(context); if (TextUtils.isEmpty(contact)) { Toast.makeText(context, 请先在设置页填写紧急联系人, Toast.LENGTH_LONG).show(); return; } // 4. 发送求助短信 String message 【紧急求助】我需要帮助!时间: System.currentTimeMillis() 位置: locationText; SmsManager smsManager SmsManager.getDefault(); if (message.length() 70) { ArrayListString parts smsManager.divideMessage(message); smsManager.sendMultipartTextMessage(contact, null, parts, null, null); } else { smsManager.sendTextMessage(contact, null, message, null, null); } // 5. 跳转系统拨号界面作为二次保障 Intent dialIntent new Intent(Intent.ACTION_DIAL, Uri.parse(tel: contact)); dialIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(dialIntent); // 6. 写入本地SOS记录表 saveSosRecord(context, locationText); } }这里有几个细节要注意。短信超过70个字必须拆分成多条发送所以我用divideMessage做了一个判断。另一个是跳转拨号而不是直接拨号这算一个防错设计老人可能误触SOS跳到拨号界面之后看一眼再确认比一键就把电话打过去要安全也体现了一个“辅助应用”应有的谨慎。3.2 吃药提醒我为什么用AlarmManager而不是WorkManager定时提醒是Android开发里的老问题我们当时就在AlarmManager和WorkManager之间纠结了很久。WorkManager更现代解决的是“延迟可容忍的后台任务”比如定期同步数据但它不是为精确到分钟的提醒设计的。吃药提醒必须准点响所以我最终用了AlarmManager。// 设置一次性提醒 AlarmManager alarmManager (AlarmManager) getSystemService(Context.ALARM_SERVICE); Intent intent new Intent(this, RemindReceiver.class); intent.putExtra(remind_msg, 该吃降压药了); PendingIntent pendingIntent PendingIntent.getBroadcast( this, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent );对应的BroadcastReceiver里收到广播之后发起通知public class RemindReceiver extends BroadcastReceiver { Override public void onReceive(Context context, Intent intent) { String msg intent.getStringExtra(remind_msg); NotificationManager manager (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); NotificationChannel channel new NotificationChannel( remind_channel, 用药提醒, NotificationManager.IMPORTANCE_HIGH); manager.createNotificationChannel(channel); Notification notification new Notification.Builder(context, remind_channel) .setContentTitle(生活辅助提醒) .setContentText(msg) .setSmallIcon(R.drawable.ic_remind) .setAutoCancel(true) .build(); manager.notify(1, notification); } }这段代码跑通之后我总结了一个经验Android 8.0以上必须创建NotificationChannel不然通知永远不显示Android 12以上setExactAndAllowWhileIdle需要申请SCHEDULE_EXACT_ALARM权限。这条经验直接让我在后面少踩了一个大坑。3.3 健康数据图表用Room加MPAndroidChart做出“看得见”的成果健康数据如果没有图表就是一串数字不仅老人看不懂答辩老师也看不出工作量。我在项目里引入了MPAndroidChart库这是Android端最常用的图表库用它画血压折线图非常快。LineChart lineChart findViewById(R.id.lineChart); ListEntry entries new ArrayList(); // 从Room数据库读取最近7天的血压记录 for (HealthRecord record : dailyRecords) { float value Float.parseFloat(record.value.split(/)[0]); // 收缩压 entries.add(new Entry(index, value)); } LineDataSet dataSet new LineDataSet(entries, 收缩压趋势); dataSet.setColor(Color.parseColor(#FF5722)); dataSet.setLineWidth(3f); dataSet.setCircleRadius(5f); LineData lineData new LineData(dataSet); lineChart.setData(lineData); lineChart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM); lineChart.invalidate();MPAndroidChart的集成很简单Gradle里加一行implementation com.github.PhilJay:MPAndroidChart:v3.1.0就行。但这张图带来的答辩效果非常显著——只要演示时打开这个界面老师一眼就能看出你处理了数据展示而不是停留在简单的页面跳转。建议做一个“近7天血压/心率变化图”再做一个“每日喝水/吃药完成统计图”工作量可视化程度会更高。3.4 适老化交互字号、色块和防误触老人端UI是整个系统最容易忽略、但最体现设计能力的地方。我做了三件事全界面文字用18sp以上能用24sp的地方不用18spSOS按钮单独做成一个圆角大色块底色用红色文字加粗所有可点击按钮都加了300ms的防连续点击避免老人手抖一下触发两次。适老化设计这部分不需要太多代码几条尺寸规范和颜色规范就够了但它在论文里非常出彩。你可以单独开一节“界面适老化设计原则”内容就是字号、对比度、点击区域尺寸、防误触策略。这一节几乎不用贴代码却能让论文的实用性和人性化程度明显提升答辩时也是一个很好的加分话题。4. 开发顺序与工程准备先做什么后做什么能帮你少走弯路4.1 环境配置Android Studio版本和Gradle是最容易翻车的第一关很多同学拿到新电脑打开Android Studio一路点默认结果新建工程就报错。我当时用的是Android Studio Hedgehog版本配的Gradle 8.2两个必须对应好否则要么同步失败要么跑起来之后编译报一堆乱七八糟的错。建议直接用Android Studio自带的新建项目向导选“Empty Views Activity”模板它会自动匹配好当前版本对应的Gradle不要手动去改版本号。另外一个非常实用的小建议项目创建完先把依赖写好。我当时常用的核心依赖就这几个大部分功能都能覆盖implementation androidx.appcompat:appcompat:1.6.1 implementation com.google.android.material:material:1.9.0 implementation androidx.room:room-runtime:2.6.1 annotationProcessor androidx.room:room-compiler:2.6.1 implementation com.github.PhilJay:MPAndroidChart:v3.1.0Room的注解处理器在Java工程里用annotationProcessor如果是Kotlin工程就要换kapt或者ksp这个千万不能搞错否则生成的代码找不到。4.2 权限设计把Android 12和Android 13的权限变化一次搞清楚老人辅助应用涉及到的权限比普通App多我把它们整理成了一张表开发的时候对着表申请就行权限用途系统版本要求SEND_SMS发送SOS短信Android 6.0动态申请ACCESS_FINE_LOCATION / ACCESS_COARSE_LOCATION获取定位Android 6.0动态申请SCHEDULE_EXACT_ALARM精确闹钟提醒Android 12POST_NOTIFICATIONS显示通知Android 13CALL_PHONE快速拨打联系人跳拨号界面则不需要权限申请有一个通用做法用一个PermissionManager统一封装通过ActivityCompat.requestPermissions申请然后回调里做分支处理。不要在每一次调用的地方散落申请代码否则调试一次就要把所有入口权限重新检查一遍非常痛苦。这段权限适配内容在论文里也很值得写。你可以这样描述“本系统针对Android 6.0至Android 13不同版本的权限机制进行了适配”然后把上面这张表往论文里一放工作量立刻清晰了。4.3 UI适配去搞一台几百块的低端手机测试做老人辅助应用界面适配比一般App更敏感。我自己测试用的是主力机但后来发现两个明显问题一是高分屏上看起来适中的字号放到低端大屏机上变得偏小二是复杂布局在低端机上有明显卡顿。我的解决方法有两个布局尽量用LinearLayout加weight或者ConstraintLayout避免三层以上嵌套列表用RecyclerView不用ScrollView包ListView。另外有条件的话去二手平台收一台两三百块的国产低端机当测试机性能和屏幕比例跟老人真实用的手机更接近。实际调试时发现的问题会比你在模拟器上跑一个月都多。5. 真机调试中的踩坑实录这些问题不是你的代码写错了5.1 华为和小米手机“杀后台”提醒和SOS直接失效这是我整个项目里最痛苦的一次排查。现象是App在后台放半小时闹钟提醒不响SOS的短信也发不出去。当时第一反应是代码写错了在Android Studio里看Logcat却没有任何异常把App切回前台再测试一切正常这就非常诡异。后来逐步排查出来的结论是国产定制ROM华为EMUI、小米MIUI默认对非白名单应用有严格的后台限制AlarmManager的精确闹钟被系统“冻结”了BroadcastReceiver也收不到广播。这不是代码Bug而是系统级策略。解决方法是引导用户把App加入电池优化白名单。通过Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS这个Intent跳转到系统设置页让用户手动开启“无限制”。同时在Semantic层面做了一层兜底每次打开App时检查是否在白名单里如果不在弹窗提醒。这个排查过程非常值得写进论文的测试章节。你可以用它来展示“系统兼容性测试与问题修复”比单纯写“应用运行稳定”有说服力得多。5.2 定位结果“飘到省外”罪魁祸首是getLastLocation缓存SOS调试的时候还遇到过一个很离谱的问题老人明明在家里短信里的定位却显示到了几十公里外的另一个区一度怀疑是定位SDK的问题。后来我在代码里打印了provider和time字段才发现getLastLocation返回的是几小时前的GPS缓存那个位置是之前测试时在户外留下的。解决方法是拿位置后立即判断时间戳和精度Location location locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); if (location ! null System.currentTimeMillis() - location.getTime() 5 * 60 * 1000) { // 如果缓存时间超过5分钟主动请求一次新定位 locationManager.requestSingleUpdate(LocationManager.NETWORK_PROVIDER, locationListener, Looper.getMainLooper()); }另外还有一个心得在室内环境纯用GPS几乎拿不到有效定位一定要把GPS_PROVIDER和NETWORK_PROVIDER混合使用或者直接集成高德/百度定位SDK。考虑到毕设项目不要太重我选择了LocationManager加超时容忍策略效果够用。如果你有精力接入一个第三方定位SDK也可以但注意版号合规和文档更新问题。5.3 通知权限明明开了通知就是不显示做提醒功能的时候有一个特别容易忽略的坑Android 13开始通知权限从安装时默认开启变成了运行时动态申请。我在Android 13的测试机上遇到的现象是App里通知开关是打开的但收不到任何通知。排查下来发现有两个原因一是没有创建NotificationChannel或者创建了但importance级别是LOW导致通知被系统静默二是Android 13需要显式申请POST_NOTIFICATIONS权限并且这个权限要在发送第一条通知之前完成申请。这个坑属于“系统行为差异”而不是代码错误遇到时不要慌。把创建渠道的代码放在Application启动时执行一次把权限申请放在首页进入后的弹窗里问题就解了。这次排查过程也让我养成了一个思维习惯凡是“权限看着都在但功能失效”的优先往系统版本行为差异上查。6. 从“能跑”到“能答辩”演示策略和论文素材怎么准备6.1 真机演示一定要准备“故障预案”答辩现场用真机演示是风险评估最高的一环。我当时提前把另一个模拟器也装好了并且在演示前一天录制了完整的功能操作短视频作为Plan B。这个建议真心强烈真机演示时短信发送偶尔会因为运营商延迟而看不到现象网络定位可能因为室内环境失败这些都不是你的代码问题但如果现场没有Plan B老师会认为是代码问题。我的操作策略是先让老师看到真机界面快速演示吃药提醒的弹窗和健康图表的滑动效果然后打开数据库查看日志记录证明后台数据在持续写入。至于SOS短信提前录好视频现场说“短信发送受运营商影响我们看一下完整录制效果”。让老师看到“救命”短信真实出现在收件箱里的视频比现场等一条短信的演示体验好得多。6.2 日志入库让每一步操作都有据可查答辩时最怕老师说“你这个系统好像没什么工作量”。我的应对办法是在项目里加了一张operation_log表记录每一次关键操作的时间、模块、结果。比如SOS触发了、提醒弹出了、健康数据录入了全部写入数据库。答辩的时候打开这张表能看到测试期间几十条操作记录这就是最直观的工作量证明。CREATE TABLE operation_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, module_name TEXT, action_desc TEXT, result TEXT, log_time TIMESTAMP );当时项目里还有一个亮点功能是“子女远程留言”因为时间关系我没有做实时推送采用的是轮询服务器接口的方式。如果你有自己的服务器或者用第三方云数据库可以把这部分做成简单的HTTP接口调用演示时打开两个界面一个老人端一个子女端数据同步更新效果会非常加分。6.3 论文里这几张图最能体现工作量代码少截最后说论文。很多同学喜欢在论文里贴大段代码其实答辩老师基本不看。真正能为论文加分的是这几张图系统架构图三层结构画清楚、SOS功能时序图从点击到短信发送的流程、数据库ER图、以及真机界面截图对照图。时序图推荐用Visio或者ProcessOn画不要用代码生成工具画得越简洁越好。界面截图一定要包含适老化设计的对比字体大小、按钮颜色、点击区域这些都可以做前后对比一篇论文有这些内容工作量一下就立住了。另外不要把整个项目源码贴到论文里只选核心代码片段比如SosManager的核心方法、AlarmManager设置方法、Room数据库表的定义每个片段控制在20行以内配一句文字说明。老师要源码的时候再通过附录或Git仓库提供完整工程这样轻重得当也给论文答辩留了更多可聊的空间。