
简介这是一套面向计算机专业大学生及移动应用开发初学者的创业实践项目源码聚焦垃圾短信智能识别与电信诈骗实时拦截场景融合前端交互、后端建模与安卓系统级权限调用。资源包含55个文件涵盖17个XML布局与配置文件、12个PNG图标资源、4个核心Python脚本含NaiveBayesian.py垃圾短信分类模型、3个Java业务逻辑类、以及HTML/CSS/JS网页界面和Flask后端服务代码整体压缩包61.98MB结构清晰体现AndroidWebPython三层架构设计。已有788人学习下载适合希望掌握跨平台通信HttpClientWebView、移动端短信读取与权限管理ContentProvider、BroadcastReceiver、Service、以及基于scikit-learn朴素贝叶斯算法实现中文文本分类的实践者。项目提供完整可运行APK、训练数据集80万条中文短信、停用词表与消息提示配置附带详细项目说明文档便于复现、调试与二次开发。1. 垃圾短信过滤APP不是“装个软件就完事”它本质是把电信诈骗识别能力塞进安卓端的轻量级NLP流水线很多大学生团队在做“垃圾短信过滤APP”时第一反应是找现成SDK、调用云API、或者直接套用某篇论文的准确率数字——结果上线后拦截率不到40%误杀正常快递通知、银行验证码用户卸载率飙升。真实场景里一条伪装成“【工商银行】您尾号8866账户于今日15:23支出¥9999.00”的短信既不含明显违禁词又来自仿冒号段传统关键词匹配完全失效。这类项目的核心矛盾从来不是“有没有模型”而是如何在无网络依赖、低内存占用30MB、不越权读取全部短信的前提下让NaiveBayesian.py这类轻量分类器真正跑通从文本清洗→特征向量化→实时判别→系统级拦截的全链路。它面向的是Android 8.0设备上未root但已获SMS权限的普通应用目标不是替代运营商风控而是给用户加一道可感知、可解释、可关闭的本地化防线。如果你正卡在“模型训练完却导不出Android可用的权重”“DxyWeb.py能跑通但接不上系统短信广播”“拦截后无法触发系统Toast提示”这些环节这篇就是为你写的实操路径。2. 用NaiveBayesian.py构建可部署的本地分类器从训练数据清洗到Android兼容的模型序列化2.1 为什么必须重写NaiveBayesian.py的特征工程逻辑原始NaiveBayesian.py常见于高校课程作业通常直接对原始短信做空格切分TF-IDF向量化这在PC端跑得通但在Android端会暴露出三个致命问题内存爆炸全量词典加载后常驻内存超15MB远超Android后台服务容忍阈值冷启动失败首次启动需加载数万词汇表导致APP白屏超3秒被系统判定为ANR特征漂移未处理短链接如t.cn/AbcDe、手机号掩码138****1234、金额符号¥9999.00等电信诈骗高频变体测试集准确率虚高20%以上。提示不要复用scikit-learn的MultinomialNB原生pickle序列化——Android端JVM无法反序列化Python对象。必须将模型参数转为纯JSON或FlatBuffer格式。2.2 可落地的特征清洗与向量化方案我们采用两级过滤策略将特征维度从10万压缩至1200以内预处理层Python端完成import re import jieba def clean_sms(text): # 移除所有URL保留域名主体用于识别钓鱼 text re.sub(rhttps?://[^\s], http_domain, text) # 标准化手机号/银行卡号统一替换为占位符 text re.sub(r1[3-9]\d{9}, mobile_num, text) text re.sub(r\d{4}\s?\d{4}\s?\d{4}\s?\d{4}, bank_card, text) # 金额标准化¥9999.00 → money_amt text re.sub(r[¥$€]\d(?:\.\d{2})?, money_amt, text) # 分词并过滤停用词使用精简版停用词表仅含127个高频干扰词 words jieba.lcut(text) return [w for w in words if w not in STOPWORDS and len(w) 1] # 构建极简词典仅收录在诈骗短信中TF-IDF值0.8的词汇 from sklearn.feature_extraction.text import TfidfVectorizer vectorizer TfidfVectorizer( max_features1200, # 强制截断 ngram_range(1, 2), # 加入转账 失败等双词组合 min_df3, # 仅保留至少出现在3条样本中的词 token_patternr(?u)\b\w\b )2.3 模型参数导出为Android可读JSON格式from sklearn.naive_bayes import MultinomialNB import json import numpy as np # 训练模型 nb_model MultinomialNB() X_train_vec vectorizer.fit_transform(train_texts) nb_model.fit(X_train_vec, train_labels) # 导出关键参数非完整pickle model_data { classes: nb_model.classes_.tolist(), feature_log_prob: nb_model.feature_log_prob_.tolist(), # shape: (n_classes, n_features) class_log_prior: nb_model.class_log_prior_.tolist(), feature_names: vectorizer.get_feature_names_out().tolist() # 严格对应索引 } with open(nb_model.json, w, encodingutf-8) as f: json.dump(model_data, f, ensure_asciiFalse, indent2)表导出JSON字段与Android端Java调用映射关系JSON字段Android端用途注意事项feature_names构建SparseVector时的词典索引必须与训练时vectorizer完全一致建议存为assets/nb_vocab.txtfeature_log_prob计算P(wordclass)的核心矩阵class_log_priorP(class)先验概率直接用于贝叶斯公式分子计算classes输出标签映射0→正常1→诈骗避免硬编码从JSON动态读取2.4 在Android端实现轻量推理Kotlin示例// assets/nb_model.json 已预置 data class NBModel( val classes: ListInt, val featureLogProb: ListListDouble, val classLogPrior: ListDouble, val featureNames: ListString ) fun predictSms(text: String): Int { val cleaned cleanSms(text) // 同Python端clean_sms逻辑 val vector buildSparseVector(cleaned) // 将文本转为[0,1,0,0,2...]稀疏向量 var maxScore Double.NEGATIVE_INFINITY var bestClass 0 for (i in model.classes.indices) { var score model.classLogPrior[i] // 先验 for (j in vector.indices) { if (vector[j] 0) { // 累加 log(P(word_j|class_i)) * count_j score model.featureLogProb[i][j] * vector[j] } } if (score maxScore) { maxScore score bestClass model.classes[i] } } return bestClass }注意buildSparseVector()需用HashMap实现稀疏存储避免创建1200长度的稠密数组——这是Android端内存优化的关键。3. DxyWeb.py不是Web服务而是Android短信广播监听与系统拦截的胶水层3.1 DxyWeb.py的真实角色解耦短信解析与系统操作的中间件看到DxyWeb.py这个文件名很多人误以为它是Flask/Django服务实际上在本项目中它承担的是Android短信广播接收器BroadcastReceiver与Python模型推理层之间的协议桥接。其核心价值在于避免在Android主线程直接调用Python解释器会导致UI卡顿将系统级短信拦截动作abortBroadcast()与模型预测结果解耦支持热更新模型而无需重启APP提供标准HTTP接口供Android端异步调用符合大学生团队熟悉的技术栈不用碰JNI。提示DxyWeb.py必须运行在Android的Termux环境或独立Python服务进程如通过android-python3不能依赖Windows/Linux桌面Python环境。3.2 DxyWeb.py最小可行实现基于Flask轻量封装from flask import Flask, request, jsonify import json import os from NaiveBayesian import predict_sms # 加载上一节导出的JSON模型 app Flask(__name__) # 加载模型启动时一次加载避免每次请求重复IO MODEL_PATH /data/data/com.example.smsfilter/files/nb_model.json with open(MODEL_PATH, r, encodingutf-8) as f: model_data json.load(f) app.route(/predict, methods[POST]) def predict(): try: data request.get_json() sms_text data.get(text, ) if not sms_text.strip(): return jsonify({error: empty text}), 400 # 调用本地预测函数已适配Android Termux路径 result predict_sms(sms_text, model_data) # 实现见2.4节逻辑 return jsonify({ is_fraud: bool(result), confidence: 0.92 # 此处可扩展为概率输出 }) except Exception as e: return jsonify({error: str(e)}), 500 if __name__ __main__: # 绑定到localhost:5000仅限Android本机访问 app.run(host127.0.0.1, port5000, debugFalse)3.3 Android端调用DxyWeb.py的健壮性设计// 使用OkHttp异步调用Python服务 private fun callPythonPredictor(smsBody: String) { val client OkHttpClient.Builder() .connectTimeout(3, TimeUnit.SECONDS) .readTimeout(5, TimeUnit.SECONDS) .build() val jsonBody JSONObject().apply { put(text, smsBody) } val request Request.Builder() .url(http://127.0.0.1:5000/predict) .post(RequestBody.create( MediaType.parse(application/json; charsetutf-8), jsonBody.toString() )) .build() client.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { // 网络失败时启用降级策略返回缓存的最近模型结果 handleFallbackPrediction(smsBody) } override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) { val result JSONObject(response.body?.string()) if (result.optBoolean(is_fraud, false)) { // 触发系统级拦截 abortBroadcast() // 需在BroadcastReceiver中调用 showNotification(拦截诈骗短信${smsBody.take(20)}...) } } } }) }表DxyWeb.py在Android不同场景下的存活策略场景推荐方案关键命令/配置APP前台运行启动Foreground Service托管Python进程startForeground(1, notification)防止被系统杀死APP后台休眠使用WorkManager定期唤醒15分钟间隔PeriodicWorkRequestBuilderDxyWorker(15, TimeUnit.MINUTES)设备重启后在BOOT_COMPLETED广播中自动拉起receiver android:name.BootReceiverstartService()Termux环境缺失提供纯Java版备用模型预编译assets/java_nb_model.bin 自定义ClassLoader4. 在Android系统中实现真正的短信拦截绕过MIUI/EMUI权限限制的三步法4.1 为什么abortBroadcast()在新机型上大概率失效Android 8.0系统对短信广播android.provider.Telephony.SMS_RECEIVED实施了严格限制MIUI 12默认禁止第三方APP注册该广播即使声明uses-permission android:nameandroid.permission.BROADCAST_SMS/也无效EMUI 11要求APP必须通过“自启动管理”白名单且用户手动开启“允许后台活动”ColorOS/OriginOS强制要求APP加入“系统安全中心”信任列表否则广播接收器被静默丢弃。单纯依赖abortBroadcast()已无法覆盖主流国产ROM必须采用组合策略。4.2 三步法拦截方案实测覆盖华为Mate40/小米12/OPPO Reno8第一步申请READ_SMSSEND_SMS双权限AndroidManifest.xml!-- 必须声明否则无法获取短信内容 -- uses-permission android:nameandroid.permission.READ_SMS / uses-permission android:nameandroid.permission.SEND_SMS / !-- 针对Android 10还需此权限 -- uses-permission android:nameandroid.permission.RECEIVE_SMS / !-- 关键声明短信广播接收器 -- receiver android:name.SmsReceiver android:exportedtrue android:permissionandroid.permission.BROADCAST_SMS intent-filter android:priority1000 action android:nameandroid.provider.Telephony.SMS_RECEIVED / /intent-filter /receiver注意android:priority1000是最高优先级确保你的Receiver在系统短信APP之前接收到广播。第二步在SmsReceiver中执行双重拦截class SmsReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.action android.provider.Telephony.SMS_RECEIVED) { val pdus intent.getParcelableArrayExtra(pdus) as? Array* ?: return val messages pdus.mapNotNull { pdu - SmsMessage.createFromPdu(pdu as ByteArray) } for (message in messages) { val body message.messageBody val sender message.originatingAddress // 1. 本地模型预测毫秒级 val isFraud predictLocally(body) // 2. 若预测为诈骗立即abort并标记为已处理 if (isFraud) { abortBroadcast() // 阻止其他APP接收 // 3. 写入本地数据库标记防重复拦截 markAsBlocked(sender, body, System.currentTimeMillis()) // 4. 发送系统通知用户可见 sendBlockingNotification(context, body) return // 退出循环避免后续处理 } } } } }第三步针对MIUI/EMUI的兜底方案——主动轮询通知栏拦截当广播接收器失效时启用后台Service每30秒扫描一次新短信// 在Service中执行 private fun scanNewSms(context: Context) { val uri Uri.parse(content://sms/inbox) val cursor context.contentResolver.query( uri, arrayOf(_id, address, body, date), date ?, arrayOf(lastScanTime.toString()), date DESC ) cursor?.use { while (it.moveToNext()) { val body it.getString(it.getColumnIndexOrThrow(body)) val address it.getString(it.getColumnIndexOrThrow(address)) if (predictLocally(body) !isAlreadyBlocked(address, body)) { // 主动删除该短信需WRITE_SMS权限 context.contentResolver.delete( ContentUris.withAppendedId(uri, it.getLong(0)), null, null ) sendNotification(已拦截诈骗短信${body.take(15)}...) } } } lastScanTime System.currentTimeMillis() }4.3 用户授权引导的实操话术提升通过率不要弹出系统原生权限框后就干等必须前置教育在申请READ_SMS前显示卡片式说明“为识别诈骗短信需读取收件箱内容仅分析文字不上传服务器”对MIUI用户跳转至设置页的精确路径Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { data Uri.parse(package:$packageName) }提供“一键跳转自启动管理”按钮适配各厂商val intent Intent().apply { when { Build.MANUFACTURER.lowercase().contains(xiaomi) - { action miui.intent.action.APP_PERM_EDITOR setClassName(com.miui.securitycenter, com.miui.permcenter.permissions.PermissionsEditorActivity) } Build.MANUFACTURER.lowercase().contains(huawei) - { action huawei.intent.action.PROTECT_MANAGER setClassName(com.huawei.systemmanager, com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity) } else - returnapply } flags Intent.FLAG_ACTIVITY_NEW_TASK } startActivity(intent)5. 验证拦截效果用真实电信诈骗短信样本集做端到端压测5.1 构建不可绕过的测试用例集覆盖最新诈骗手法不要用公开数据集如SMSSpamCollection做最终验证——其样本过于陈旧无法检测2023年新型话术。必须采集真实样本重点覆盖伪装银行类【中国银行】您的ETC账户异常点击 t.cn/XYZ123 解锁冒充公检法类【XX市公安局】您涉嫌洗钱案号京公刑字[2023]第8866号请速回电010-88661234刷单返利类【淘宝联盟】恭喜您中奖¥888扫码添加客服微信taobao8866 领取贷款诈骗类【微粒贷】授信额度¥50000年化3.8%点击领取dwz.cn/ABC456。提示样本需包含正常短信对照组快递、水电费、会议提醒比例严格控制在1:4诈骗:正常模拟真实收件箱分布。5.2 在Android真机上执行端到端压测非模拟器使用ADB命令注入测试短信绕过UI操作# 向设备发送伪造短信需root或使用adb shell input text模拟 adb shell service call isms 7 i32 0 s16 com.android.mms s16 8613800138000 s16 【工商银行】您尾号8866账户支出¥9999.00 s16 null s16 null s16 null # 或更可靠的方式写入数据库需adb root adb root adb shell sqlite3 /data/data/com.android.providers.telephony/databases/mmssms.db \ INSERT INTO sms (address, body, date, read, type) VALUES (8613800138000, 【工商银行】您尾号8866账户支出¥9999.00, $(date %s%3N), 0, 1);5.3 关键指标监控与日志埋点在SmsReceiver.onReceive()中添加结构化日志Log.d(SMS_FILTER, IN:${sender}|BODY:${body.take(30)}|PREDICT:${isFraud}|TIME:${System.currentTimeMillis()}|ABORTED:${isOrderedBroadcast() isInitialStickyBroadcast().not()} )然后用ADB实时抓取并统计# 实时监控拦截成功率 adb logcat -s SMS_FILTER | grep ABORTED:true | wc -l # 统计误杀率正常短信被拦截 adb logcat -s SMS_FILTER | grep PREDICT:true | grep -v 【工商银行\|【支付宝\|【微信\|【京东】 | wc -l表大学生项目可接受的性能基线真机实测指标达标值测量方法优化方向单条短信处理耗时≤120msSystem.nanoTime()打点减少正则匹配次数预编译Pattern内存占用峰值≤28MBadb shell dumpsys meminfo com.example.smsfilter使用SparseArray替代HashMap存储特征向量拦截成功率诈骗类≥82%人工标注100条诈骗样本统计ABORTED:true占比增加“伪基站号段”黑名单170/171/167开头误杀率正常类≤3.5%同上统计正常短信被拦截比例在模型输出后增加规则校验层如含“验证码”“订单号”则放行后台存活时长MIUI≥48小时连续运行Service记录最后一次成功扫描时间启用AlarmManager定期唤醒避开MIUI省电策略最后在onReceive()中加入一个硬性熔断机制当连续5次预测耗时超过200ms自动降级为规则匹配模式关键词号段黑名单保证基础拦截能力不中断。本文还有配套的精品资源点击获取