基于Handler机制的Android主线程卡顿检测方案详解
1. 为什么卡顿检测要回到Handler机制本身找答案做过一段时间线上性能监控的人都有体会崩溃问题最好定位一条crash堆栈加现场日志就能还原大半。卡顿不一样它不像崩溃那样会在某一刻“砰”地停住它是UI线程长期处于繁忙、某次关键操作超过人眼耐受阈值时用户的主观体感。更麻烦的是线上卡顿往往没有对应日志你只知道用户在某个页面停留了5秒、滑动时掉帧但根本拿不到“主线程当时在干嘛”的证据。我试着从源码和实际设备两个角度去排查这类问题时逐渐确认了一个事实不管是触摸事件、生命周期调用、布局测量、绘制分发还是你写的网络回调、图片加载回调最终都会以一条Message的形式放进主线程的Looper消息队列再由主线程Looper循环取出执行。只要卡顿发生就必然意味着某条Message在主线程上执行太久或者消息队列里的任务在排队等待时被严重拖延。于是“检测卡顿”这个问题就被还原成一个更具体、更可量化的问题如何监听主线程消息的执行和排队情况。这个切入点就是Handler机制本身。主线程Looper的循环里隐藏着两个天然的“检查点”消息开始派发的那一刻和消息派发完成的那一刻。把这两个时间点之间的差值算出来就能知道当前这条Message在主线程干活花了多久如果把一个精心设计的“心跳消息”按固定节奏投递到主线程还能从它的执行时间差里估算出整个队列被阻塞了多久。这种方案的商业价值在于它不光能告诉你“卡了多久”还能通过抓取当时的主线程调用栈告诉你“到底哪一行代码把UI线程拖住了”。这也是很多开源卡顿监控组件的底层逻辑区别只在于它们把采样、堆栈抓取、上报逻辑包装得更好。这篇文章我就从原理到工程化把基于Handler机制的线上卡顿检测实现方案完整梳理一遍适合正在做性能监控、或者想给自家应用加一套线上异常感知能力的开发者参考。2. Handler消息边界的“秒表实验”先跑通一个最小可运行示例2.1 方案A拦截Looper消息派发日志测量单条消息耗时任何线程只要进入了Looper.loop()就会在一个无限循环里反复执行两个动作从MessageQueue里取出一张消息调用msg.target.dispatchMessage(msg)把消息交给对应的Handler执行。Android主线程的Looper本质上也只是一个普通的Looper区别在于它注册了UI相关的所有Handler。为了观察消息派发的起止Looper内部提供了一个调试钩子叫setMessageLogging(Printer)。设置了Printer后Looper会在派发消息前打印一条以“ Dispatching”开头的日志在消息执行完毕后打印一条以“ Finished”开头的日志。我们不管这些日志拿去干嘛直接拿它们当信号就能给每条消息掐秒表public final class JankMonitor { private static final long THRESHOLD_MS 300L; private static long sDispatchStart; public static void install() { Looper.getMainLooper().setMessageLogging(new Printer() { Override public void println(String x) { if (x null || x.length() 0) { return; } if (x.startsWith( Dispatching)) { sDispatchStart SystemClock.elapsedRealtime(); } else if (x.startsWith( Finished)) { long cost SystemClock.elapsedRealtime() - sDispatchStart; if (cost THRESHOLD_MS) { reportJank(cost); } } } }); } }这段代码的核心逻辑只有三行开始时间、结束时间、差值比较。reportJank里可以做堆栈抓取和上报。这里有两个细节值得注意第一必须用SystemClock.elapsedRealtime()不要用System.currentTimeMillis()因为后者是墙上时钟用户改系统时间或者网络校时都会导致差值计算失真第二阈值判定用的是“单条消息执行时间”不是“帧间隔时间”这两者语义不同后面会专门讲。这种写法的优点是真的简单一行setMessageLogging就能跑起来。但它有两个明显副作用一是每条消息都触发两次字符串拼接和前缀匹配长期开着对主线程有一点肉眼看不见但真实存在的开销二是它强行给系统日志增加了大量输出线上环境一般不建议直接这么干。所以我会把它当作原理验证工具而不是最终线上版本。2.2 方案B心跳消息探针把卡顿定位拉回主线程调度视角单条消息的耗时只能反映“执行得过久”的卡顿但线上还有另一类更隐蔽的卡顿每条消息单独看都不慢二三十毫秒就结束了可它们排着队连续执行把消息队列塞得满满当当本来该在16ms内完成的下一帧绘制被反复推迟。这种情况用方案A是测不到的因为它压根没有一条“超长消息”可以抓。针对这类问题更贴近线上场景的做法是主动制造一个“心跳探针”消息。思路很简单后台的监控线程每隔一段时间向主线程投递一条轻量消息消息的内容只是往某个内存标记里写时间戳。如果主线程空闲这条消息会很快被执行如果主线程被一堆任务占住消息的预期执行时间和实际执行时间之间就会出现一个差值这个差值就是“从发送到真正被执行”的排队延迟能如实反映主线程的繁忙程度。public class MainThreadProbe { private final Handler mainHandler new Handler(Looper.getMainLooper()); private final AtomicLong executeCostNanos new AtomicLong(0L); private volatile Runnable probeTask; public void install() { HandlerThread watchdog new HandlerThread(jank-watchdog); watchdog.start(); Handler watchdogHandler new Handler(watchdog.getLooper()); watchdogHandler.postDelayed(new Runnable() { Override public void run() { long postTime SystemClock.elapsedRealtimeNanos(); mainHandler.post(new Runnable() { Override public void run() { long execTime SystemClock.elapsedRealtimeNanos(); executeCostNanos.set(execTime - postTime); } }); watchdogHandler.postDelayed(this, 500L); } }, 500L); } }每次心跳回调里主线程执行写时间戳的成本可以忽略不计对线上性能影响很小。监控侧只需要维护一个后台HandlerThread周期性地投递、读取标记值即可。一旦发现某次心跳的执行延迟超过阈值就可以断定主线程在过去一段时间内处于忙碌或阻塞状态然后触发一次堆栈采集。这里有个容易被忽略的事实心跳探针测的是“消息排队延迟”而不是“单条消息执行耗时”。对于定位“主线程被某个未知任务长时间占用”这类问题它比方案A更接近用户的真实体感。很多应用最终的线上监控都是走这个思路再叠加线程堆栈采样来还原现场。2.3 阈值选多少才不冤枉主线程阈值设置是整个卡顿检测里争议最大的环节。Android渲染一帧的理论耗时是16.6ms但如果你真把阈值设成16ms线上监控后台会瞬间被刷爆因为由于GC、系统调度、低端机性能等因素单帧超过16ms是生活常态不代表用户能感知到卡顿。我的经验是把阈值分成两档。一档叫做“感知卡顿阈值”大概在50ms到100ms之间对应的是用户能明确感受到“有点不跟手”的临界区适合用来做页面维度的卡顿率统计但不适合单条上报。另一档叫做“严重卡顿阈值”建议设在300ms以上超过这个值基本意味着主线程被严重阻塞用户能清楚地看到界面静止这时候每条都值得抓堆栈、上报、归因。再往上一档就是ANR相关通常超过5秒系统会弹无响应对话框这属于另一个监控系统负责的范围。Handler机制的监控方案不需要覆盖那么长的时间尺度把300ms到5秒之间的区间打好就足够了。还要提醒一点低端机和高端机的阈值不能一刀切比较稳妥的做法是按设备性能分群配置我通常按CPU核心数和内存档位粗分三档高端机严一些低端机放宽一倍这样后台的卡顿时长分布才有参考性。3. 线上方案真正难的不是检测是“低成本采集与还原现场”3.1 卡顿堆栈抓取要抓到“卡在当时”的那一组调用栈检测到卡顿只是第一步真正有价值的是给每条卡顿附上主线程当时的调用栈。没有调用栈的卡顿数据充其量只能告诉你用户感受到了卡无法支撑后续的修复。最简单的做法是拿到主线程Thread对象调用它的getStackTrace()StackTraceElement[] stack Looper.getMainLooper().getThread().getStackTrace();这个API的语义很直接由另一个线程对一个正在运行的线程调getStackTrace()拿到的其实是目标线程在“调用瞬间”的Java调用栈快照。卡顿发生在UI线程触发堆栈采集的却在后台监控线程所以快照里能看到的是主线程还没从长任务里走出来时正在执行的代码路径恰好是我们想要的。这里要谨慎两点。一是不要每次心跳延迟都立刻抓堆栈因为短时间卡顿的堆栈抖动很快抓到的可能是卡顿已经结束、线程正在恢复的样子。比较成熟的策略是多采样几次把300ms作为触发线触发后每隔80ms抓一次主线程堆栈最多抓4到6次然后从这些堆栈里找出出现次数最多的那个调用点作为“根因疑似点”。这个方法简单但有奇效能过滤掉大量瞬时噪音。二是getStackTrace()本身有一定开销虽然一般不会到几十毫秒但它在后台线程执行不会直接卡UI线程真正风险在于频繁调用会造成短暂的线程锁竞争所以触发间隔必须限制住。3.2 采样、聚合与上报策略线上环境最忌讳“有卡顿就报”。一个日活几百万的应用哪怕只有万分之一的用户触发卡顿后台一天也能积攒几百上千条原始数据而且其中有大量重复。我的做法是分三层做削减。第一层是采样。卡顿事件本身不是高频事件但心跳探针是高频运行的所以先把“检测”和“上报”解耦。探针每500ms运行一次只更新内存标记只有标记超过阈值时才进入“疑似卡顿”状态再做堆栈采集。这一步就把99%的无效检测过滤掉了。第二层是聚合。同一个版本、同一个页面、主线程堆栈的前若干帧相同就认为这是同一种卡顿类型。聚合窗口可以用小时为单位相同类型只保留一条代表样本附上触发次数、时长分布、设备分布。这样后台看到的是“某版本某页面的某方法成为卡顿热点”而不是一行一行堆栈几乎相同的重复日志。第三层是控制上报时机。卡顿分析本来就有延迟容忍度完全可以把打点数据缓存在本地等网络空闲或者应用切后台时批量上传。这样既避免抢用户流量也避免在弱网下给应用增加额外负担。上报字段上除堆栈外我还会带页面名、卡顿时长、是否冷启动阶段、进程CPU占用率等几个维度这些信息对后续归因有很大帮助。3.3 兼容性与自身性能开销Handler方案看着简单落到线上就会碰到很多Android版本差异的细节。比如不同版本对Looper日志格式做过渡调setMessageLogging的日志前缀不是绝对稳定硬件加速开启时异步消息的派发时机也有微调。因此在实现时要做的不是硬编码一个字符串前缀而是把“消息派发阶段”抽象成独立模块用签名匹配或正则兼容多种格式。当然最好的方式是线上尽量少依赖日志解析这条路而是用心跳探针类方案它的兼容性要好得多。自身性能开销方面我会单独列一个开关和一个采样率配置。默认全量开启心跳探针因为它真的便宜堆栈采集则按百分比采样比如10%的触发事件做详细采集其余只记录一个触发时间戳。这样整套系统即便在低端机上额外开销也能控制在百分之一CPU占用以下不会反过来造成新的卡顿。4. 从Demo到线上的几道坎实际项目里踩过的坑记录4.1 LogPrinter格式变化与硬编码前缀的坑方案A的原理验证跑通以后我一度以为可以无缝上生产。结果在部分Android高版本上发现一个诡异的现象日志前缀有时候不是标准的 Dispatching而是多了一些额外的标记字符。原因是一些系统版本对Message做了重新格式化或者厂商在Looper实现里改了打印细节导致我的startsWith()判断在某些机型上一直命中不了监控直接失联。这类问题的排查路径不难但很折磨人先要确认setMessageLogging是否真的生效再打印一段原始日志看真实前缀。说穿了厂商定制ROM往往会有自己的Looper改动字符串解析这种办法本质上是在跟系统的实现细节赛跑。后来我把方案A降级成测试环境可用的调试工具线上主监控改用心跳探针文本解析的不稳定因素一下就消失了。如果你也选了基于Printer的方案务必在主流厂商的几款机型上回归验证。4.2 同步屏障消息与异步消息为什么有一类卡顿始终测不到Handler消息里存在一种特殊机制叫同步屏障。当MessageQueue插入了同步屏障时所有普通消息都必须暂停排队只有异步消息在屏障解除前能够被取出执行。系统在VSYNC调度、硬件渲染里大量使用这套机制来保证关键任务的优先性。这给监控带来的影响在于某些“看起来卡住”的时段主线程可能并不是在死循环或者干重活而是在正常地等待异步消息完成输入和绘制。基于心跳消息的探针在这种时段会被同步屏障拦住导致探针延迟升高监控层却无法区分“主线程真的在忙”还是“普通消息被暂时压住”。真实线上数据里这类情况出现在动画渲染密集的场景比较多会产生一定比例的误报。我的处理办法是给探针消息队列加一个特殊通道不用普通消息而用异步消息投递探针这样它不会被同步屏障拦住。从Android的Handler分发角度看异步消息的执行时机更接近系统关键任务拿它做心跳探针反而能避开屏障干扰。如果你的工程还在使用比较旧的SDK版本可以通过反射往消息对象上设置setAsynchronous(true)但这属于敏感反射代码要加严格的版本校验和降级逻辑。4.3 IdleHandler的“伪卡顿”与低优先级调度误导另一个容易误导判断的是MessageQueue.addIdleHandler注册的空闲任务。IdleHandler只在主线程消息队列暂时空闲时执行如果你的应用在主线程空闲时塞了大量轻量任务比如本地缓存整理、数据预加载它们堆在一起也可能让主线程连续执行几百毫秒。从检测结果看心跳探针会发现消息队列一直没怎么休息从而判定为卡顿。但站在用户视角这段时间往往伴随着快速滚动或者连续动画UI正在稳定输出新帧并没有明显卡顿体感。这就是典型的“伪卡顿”。要避免这类误报最直接的办法是在堆栈采集时增加一道分类过滤如果主线程堆栈里出现MessageQueue.next或者IdleHandler相关调用同时UI线程FrameCallback仍在正常回调就降低这条记录的优先级甚至直接丢弃。原理很简单IdleHandler导致的长耗时和真正阻塞消息导致的卡顿对用户的前台体验影响完全不是一个量级的。4.4 误报过滤长消息不等于真卡顿还有一类反过来被漏掉的案例某条消息执行了400ms听着很吓人但它可能是一次用户主动触发的图片加载或者数据库批量迁移在用户没操作的时候后台悄悄执行视觉上并没有卡顿。如果不加过滤这条数据照样会被推为严重卡顿导致后台热榜污染。后来我加入了一个辅助判断维度卡顿触发时检查最近120ms内是否有输入事件或者关键frame回调发生。如果用户没有操作、屏幕上没有动画那这条长耗时消息即使执行了很长时间也只记录到慢操作统计里不进入卡顿榜单。这个过滤逻辑看起来很简单却能把后台的卡顿准确率提升一大截。4.5 主线程上的堆栈获取注意性能副作用堆栈采集最直接的方式是Looper.getMainLooper().getThread().getStackTrace()但你要知道主线程Java层栈的获取本质上依赖虚拟机线程挂起和栈帧遍历调用方虽然不是UI线程却会让被采线程短暂停顿。如果你在单次卡顿里连续采样五六次主线程反而会因为被采样而变慢。所以我对采集频率做了硬限制单次卡顿事件最多采4次间隔不小于80ms全局每分钟最多处理一定数量的卡顿事件超出的只记录时间戳不抓栈。这样的话堆栈采集对UI线程的影响可以忽略不计。在线下压测时建议专门跑一趟基准对比确认开启监控前后的滑动帧率曲线基本重合再放开线上灰度。5. 同场对比Handler思路与Choreographer、ANR看护的配合关系5.1 Choreographer为什么更适合做帧率监控Handler机制的监控回答的是“主线程有没有被占住”但用户感受到的卡顿最终是“帧有没有按预期刷新出来”。这两个问题有关联却不完全等价。主线程在忙帧不一定掉主线程不忙也不一定没掉帧比如渲染管线被GPU拖住、SurfaceFlinger合成超时等。Choreographer是Android每一帧调度的大脑它在本帧开始前回调doFrame(long frameTimeNanos)。通过注册FrameCallback可以拿到连续两帧的时间戳从而精确计算实际帧间隔。如果某次doFrame回调的时间戳比预期晚了很多就说明渲染链路在时间维度上出问题了。这套方案对“帧掉没掉”的感知最直接也是很多帧率统计工具的底层实现。它的缺点也很明显只能告诉你“掉帧了”却不容易告诉你“为什么掉帧”。当掉帧由主线程代码引起时你要把Choreographer回调和主线程堆栈关联起来还需要额外做一层对齐。所以在真正的监控架构里我不会把两套方案对立起来反而将它们视为同一块拼图的两部分。5.2 两路方案如何配合才能覆盖完整卡顿链基于Handler的心跳探针负责回答“主线程是不是被卡住了卡了多久当时在执行什么代码”Choreographer负责回答“实际渲染帧间隔达到多少有没有掉帧掉帧集中在哪些页面”。两个指标交叉就能把卡顿根源进一步细分表格两路监控维度对比监控维度主线程占用实际渲染节奏数据来源主线程Looper消息调度Choreographer帧回调核心指标消息执行耗时/排队延迟相邻帧时间间隔适合判断的问题主线程长任务、死循环、锁等待渲染超时、动画掉帧堆栈归因能力强直接抓主线程调用栈弱需额外关联主线程状态线上开销低到中取决于采样频率低稳定线上出现一条“掉帧主线程占用”的案例基本可以直接断定是UI线程代码写崩了如果只有掉帧、主线程很空闲那就要去查渲染链路、GPU负载、甚至系统窗口管理逻辑。这种组合判断让监控系统的归因精度提升了一个档次。5.3 Handler方案在线上监控系统中的真实定位经过这几轮对比和反复踩坑我把Handler方案在线上监控体系中的定位总结得很清楚它是卡顿监控的第一道哨兵负责快速发现、快速采样、快速定位主线程问题。它不追求把每一次掉帧都记录下来更不会替代Choreographer做帧率统计。它的核心价值是在用户报告“某个页面很卡”之前就能先一步把“主线程在哪段代码里卡了多久”的证据沉淀下来。在实际项目中我通常把这个方案作为性能监控SDK的“主线程卡顿模块”来落地旁边再带上帧率统计模块和慢操作模块三份数据打通页面维度后统一上报。这套架构的好处是各模块相互独立升级和灰度都不互相影响出了问题也能快速关掉某个模块止血。这里还想提一句Handler方案本身也有它的天花板比如它无法感知到非主线程引起的渲染阻塞、无法覆盖NDK层面的重负载也不可能捕捉到已经导致系统完全无响应的极端场景。所以做线上监控不要指望一套方案解决所有问题而是要用最少的数据维度先把最常遇到的卡顿场景捞干净。最后分享一个实用小技巧开监控后第一次全量跑一周的原始数据别急着定阈值和过滤规则把每条卡顿时间轴和主线程堆栈拉出来人工看一遍。你会惊讶地发现自己以为合理的规则在真实流量下往往会漏掉一大堆、又误伤一大堆。把前一两周的监控数据当成训练集逐步调整过滤规则比一次性写复杂算法要实用得多。关于Choreographer掉帧监控和堆栈符号化的更细实践我后面在系列文章里继续展开。

相关新闻

N_m3u8DL-RE 完整指南:m3u8/MPD 下载、选流与混流一次讲清

N_m3u8DL-RE 完整指南:m3u8/MPD 下载、选流与混流一次讲清

N_m3u8DL-RE 完整指南:m3u8/MPD 下载、选流与混流一次讲清 【免费下载链接】N_m3u8DL-RE Cross-Platform, modern and powerful stream downloader for MPD/M3U8/ISM. English/简体中文/繁體中文. 项目地址: https://gitcode.com/GitHub_Trending/nm3/N_m3u8DL-R…

2026/10/16 5:57:45 阅读更多 →
技术参考资源整理指南:从分层筛选到知识库搭建

技术参考资源整理指南:从分层筛选到知识库搭建

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 5:56:44 阅读更多 →
揭秘 vm2api 0 提示词注入机制:为什么不用再篡改 System 提示词

揭秘 vm2api 0 提示词注入机制:为什么不用再篡改 System 提示词

【免费下载链接】vm2api vm2api 使用虚拟机完全拟真方案的订阅转api 项目地址: https://gitcode.com/gh_mirrors/vm/vm2api 点击查看 免费下载 vm2api 是一个使用虚拟机完全拟真方案做 AI 订阅转 API 的开源网关,它的核心卖点之一,就是不用向…

2026/10/16 5:56:44 阅读更多 →

最新新闻

AI赛道,最缺接盘人

AI赛道,最缺接盘人

AI算力绕不开的卡点与机会。 最近,字节跳动通过银团贷款296亿美元,阿里巴巴800亿港元配售,再往前几个月,谷歌和亚马逊融了800多亿美元,NVIDIA抛出5000亿美元“大计划”,全球所有大厂都在疯狂找钱&#xff…

2026/10/16 6:32:54 阅读更多 →
98家企业AI应用现状:规模化落地还有多远?

98家企业AI应用现状:规模化落地还有多远?

企业生成式AI广覆盖浅应用,投入积极治理滞后 调研概览 目前,企业生成式AI正从早期的工具尝试和技术验证,逐步进入规模化落地阶段。随着大模型能力从通用问答向企业知识库、数据分析、智能体(Agent)以及业务流程自动化…

2026/10/16 6:32:54 阅读更多 →
Hermes DeepSeek 接入教程:4 步跑通自我进化 AI Agent

Hermes DeepSeek 接入教程:4 步跑通自我进化 AI Agent

Hermes DeepSeek 接入教程:4 步跑通自我进化 AI Agent 【免费下载链接】awesome-deepseek-agent 项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-deepseek-agent Hermes 是 Nous Research 出品的开源 AI Agent,能从使用中自动沉淀技…

2026/10/16 6:32:54 阅读更多 →
马斯克亲口承认:全球只有3家拥有足够数据,自己不在其中

马斯克亲口承认:全球只有3家拥有足够数据,自己不在其中

马斯克600亿美元买Cursor,入局超级智能竞赛。 600 亿美元,买一张补票。 10 月 10 日,两千多人的 X Takeover 活动现场,马斯克远程连线。主持人直接问他:你为什么要买 Cursor? 马斯克的回答只用了 61 秒&a…

2026/10/16 6:32:54 阅读更多 →
微信JSAPI支付V3升级:原生支付方式二核心原理与落地实践

微信JSAPI支付V3升级:原生支付方式二核心原理与落地实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 6:32:54 阅读更多 →
企业级数据安全实战:基于透明加密与零信任的多通道数据防护解析

企业级数据安全实战:基于透明加密与零信任的多通道数据防护解析

摘要 在政企内部业务场景中,员工通过 Web 业务系统、FTP 服务、邮件等渠道收发业务文档是常态。传统终端文档加密方案,往往在文件上传业务平台、从平台下载、邮件外发、FTP 传输环节出现使用断层:文件上传后变成明文,或者下载到本…

2026/10/16 6:31:54 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →