尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Jetpack ViewModel原理深析:跨过Activity重建,状态如何死里逃生
我前一阵子在做某个跨平台架构改造遇到了一个老生常谈但特别容易翻车的问题屏幕旋转后列表数据没了。几个人在群里讨论了半天最后把问题定位到了 Activity 重建那一刻的状态丢失上。当时的代码里还留着几年前的写法数据全部存在 Activity 成员变量里转个屏界面还在内容清空用户心态直接崩了。后来把这块用 Android Jetpack 里的 ViewModel 组件重构了一遍数据稳定了内存也没啥压力。这个系列的第二篇我就集中聊聊 ViewModel 组件的原理不光是 API 怎么调关键是它背后是怎么做到“死里逃生”的。如果你已经在项目里用上了 ViewModel但说不清楚为什么转屏后对象还在或者你正准备从 MVP 那套脏活累活里跳出来想搞懂 LiveData、ViewModel 这组组件到底怎么配合又或者只是好奇一个看似普通的类凭什么能跨过 Activity 的销毁重建继续存活——这篇文章就是冲你来的。我会把 ViewModel 的生命周期设计、ViewModelStore 的存在方式、Provider 的获取链路、Factory 的自定义场景、以及 Fragment 之间共享的底层逻辑都拆开来讲也会附上我在实际项目中踩过的坑和排查思路。1. ViewModel 到底解决的是什么样的痛点先别急着上代码我们得先把痛点钉死。ViewModel 不是 Android 官方拍脑袋想出来的一个类它是被活生生逼出来的。1.1 屏幕旋转那一夜我丢掉了所有数据Android 的应用界面是由 Activity 或 Fragment 承载的而这两个东西有一个非常特殊的行为当设备配置发生变化最常见的就是屏幕旋转系统会把当前 Activity 销毁然后重新创建一个新的实例。这个过程不是开发者主动调用finish()而是系统在底层完成的回收重建。听起来好像没什么但问题是如果你把业务数据直接存在 Activity 的字段里比如一个ArrayListUser一个 EditText 里输入到一半的草稿一个正在进行的网络请求回调在转屏的那一瞬旧 Activity 实例就被废弃了你存在旧实例上的所有内存数据全部跟着一起“陪葬”。新 Activity 是被重新 new 出来的字段都是初始值界面重新走onCreate你以为的“重新加载”对用户来说就是“刚才的东西呢”这还没完。转屏只是一个最典型的场景系统还会因为其他原因销毁 Activity比如切换到后台太久被系统回收、开启多窗口模式、语言切换、字体大小调整等。只要配置变了这套“销毁重来”的流程就会重新上演。如果每次都用 “重新请求一遍接口” 的方式补救体验差倒也罢了流量和性能也是白花花的成本。1.2 为什么说 onSaveInstanceState 不够用很多老开发的第一反应是用onSaveInstanceState存一下不就行了这确实是个方案但它有一个非常致命的天花板——Bundle 的容量和类型限制。onSaveInstanceState的本质是把数据序列化到一个 Bundle 里在 Activity 重建时再通过onCreate的savedInstanceState参数拿回来。但 Bundle 适合存的是基本类型、字符串、序列化对象这类轻量数据。如果你要把一个大的列表、一个 Bitmap、一个复杂的业务实体塞进去会立刻碰到两个问题一是跨进程传输的大小限制。系统在极端情况下需要把 Bundle 数据交给系统进程托管比如进程被系统杀死后的恢复场景过大的数据会导致TransactionTooLargeException这个异常是崩溃级别的我们在线上就遇到过几次日志里明确提到了Binder transaction failed。崩溃的原因说白了就是数据太大了Binder 的传输缓冲区装不下。二是序列化和反序列化的开销。就算数据没有大到触发异常每次转屏都把几十 KB 甚至几 MB 的数据序列化一遍、再反序列化一遍短时间内的 CPU 开销和 GC 压力都不小。对一个以流畅为目标的 App 来说这不是一个优雅的做法。ViewModel 解决的就是这个“非序列化的、需要存活的、和 UI 相关联的对象数据”问题。它不绑定 Activity 的销毁事件不在转屏时随 Activity 一起走而是由系统把它单独保存下来等新的 Activity 创建后你再通过ViewModelProvider把同一个对象取回来。对象还是那个对象引用没变数据自然还在。2. ViewModel 生命周期设计的底层逻辑ViewModel 能活过 Activity 重建靠的不是魔法而是一套明确的生命周期约定。2.1 生命周期容器ViewModelStore 是核心中的核心先看一个关键概念ViewModelStore。它的名字已经说明白了这是一个用来“存储 ViewModel 实例”的容器类。它的内部实现核心就是一个HashMapString, ViewModelkey 是 ViewModel 的类名默认情况下是DEFAULT_KEY 类名value 就是对应的 ViewModel 实例。我们的 Activity 在第一次创建时会有一个关联的 ViewModelStore 实例。每当你通过ViewModelProvider去获取一个 ViewModel 时流程是这样的Provider 先找到当前 Activity 或 Fragment 关联的 ViewModelStore。用类名作为 key 去HashMap里查。如果查到了直接返回已有的实例。如果没查到就通过 Factory 创建一个新的实例放进 ViewModelStore然后返回。记住最后一步“放进 ViewModelStore”。这个动作是后面所有机制的地基。因为 ViewModel 实例一旦进了 ViewModelStore它的生杀大权就交给了 ViewModelStore 所属的生命周期容器而不是某个具体的 Activity 实例。所以当屏幕旋转时旧 Activity 被销毁但系统会在销毁前把 ViewModelStore 这个容器“捞”出来传给新 Activity 使用。新 Activity 创建后再拿这个 ViewModelStore 去取 ViewModel因为 HashMap 里有现成的实例所以直接就取到了同一个对象。这里有个细节很多人没注意getViewModelStore()这个方法会检查当前 Activity 的NonConfigurationInstances对象里有没有遗留的 ViewModelStore。如果有就直接复用如果没有就 new 一个。整个思路跟老牌游戏里“存档/读档”非常像旧实例销毁前存档新实例启动后读档。2.2 onRetainNonConfigurationInstance 与数据中转站接下来是第二个关键机制onRetainNonConfigurationInstance()。这个方法在 Activity 因为配置变更而即将被销毁时被系统调用它的返回值会被系统保存下来并在新的 Activity 出来后通过getLastNonConfigurationInstance()方法取回。ViewModelStore 就是靠这个通道进行“跨实例交接”的。至于 Fragment情况类似但路径不同。Fragment 在重建时旧 Fragment 实例上的 ViewModelStore 会被系统转移到新的 Fragment 实例上所以 Fragment 里的 ViewModel 同样能在重建后活下来。那 Activity 进程被杀的情况呢这里就要谨慎了。如果是配置变更导致的销毁ViewModelStore 能保住但如果是进程被系统杀掉了那 Activity 和 ViewModelStore 都保不住因为内存都没了。这种场景下真正的兜底方案是SavedStateHandle我会在后面的排查实录里专门讲它。我刚开始学这块的时候也有一个困惑ViewModelStore 既然存的是 HashMap那 Activity 销毁时 ViewModelStore 会不会也跟着销毁答案是不会但前提是“配置变更导致的销毁”。系统内部对配置变更这条路径做了特殊处理把 ViewModelStore 的幸存通道单独摘出来了。如果是正常finish()退出那 ViewModelStore 就会在 Activity 销毁后调用所有 ViewModel 的onCleared()然后被回收。3. ViewModelProvider 是怎么把对象“递”给你的3.1 ViewModelProvider 的获取流程我们在代码里写new ViewModelProvider(this).get(XxxViewModel.class)看似只有一行背后的分工其实很清楚。第一步构造 ViewModelProvider 时它会接收四个关键参数ViewModelStore、Factory、一个默认的 key还有创建参数 Creator。如果你只传了一个this那么它会在内部做这些事如果this是ViewModelStoreOwner接口的实现类Activity 和 Fragment 都实现了这个接口就调用getViewModelStore()拿到 ViewModelStore。如果this实现了HasDefaultViewModelProviderFactory就拿开发者自定义的默认 Factory否则用SavedStateViewModelFactory作为默认工厂。在get(Class)方法内部会先通过 key 去 ViewModelStore 里取。key 的默认生成规则是DEFAULT_KEY : canonicalName但如果是自定义 Factory 生成的 ViewModel 且通过get(String, Class)传入自定义 key就会用你传的 key 查找。第二步如果 ViewModelStore 里没有这个 key就调用 Factory 的create(Class)或create(Class, CreationExtras)方法生成一个新实例然后调用viewModelStore.put(key, viewModel)存进去。第三部返回实例。整个过程既有“缓存命中”的快速路径也有“新建并缓存”的慢路径。这也是为什么同一个 Activity 里多次调用get(XxxViewModel.class)拿到的永远是同一个对象——因为它第一次创建后就被放进 HashMap 了后续的get只是查表而已。这里有一个让人错误的直觉ViewModelProvider 不是每次获取时都 new 一个新对象。它的核心设计是“先查后建”查到了就复用查不到才创建。这也是它能保证 UI 和 ViewModel 状态一致的关键。3.2 Factory 的作用与自定义场景Factory 是 ViewModelProvider 里的“工厂角色”它负责回答一个问题当 ViewModelStore 里没有当前要找的 ViewModel 时你该怎么创建它默认的SavedStateViewModelFactory已经能处理大多数情况如果你的 ViewModel 构造函数里只有一个参数或者没有参数它会尝试反射创建实例如果构造函数里有SavedStateHandle参数它会自动为你注入一个。换句话说默认 Factory 是官方为了让你少写样板代码而设计的。但在两类场景下你必须重写 Factory场景一你的 ViewModel 构造函数里有业务参数。比如需要传入一个Repository或者传入某个用户 ID。这时候默认 Factory 帮不了你因为它是靠反射调用构造函数遇到非默认类型的参数就直接挂掉。你需要自己写一个 Factory在里面拿到参数并创建 ViewModel。场景二你需要在 ViewModel 创建前做一些前置操作。比如打日志、统计、加载缓存等。Factory 的create方法会在创建实例时被调用你可以在这个方法里做这些事再把创建好的 ViewModel 返回。自定义 Factory 的标准写法以常见的AbstractSavedStateViewModelFactory或者ViewModelProvider.Factory接口为例实现create方法即可。需要注意的是新版 AndroidX 里更推荐用CreationExtras来传递参数比如SavedStateHandle的初始化数据可以通过createSavedStateHandle获取这样比手动塞 Bundle 更优雅。我个人的建议是不要把 Factory 写得太重。它本质上是提供创建依赖的工具如果每个 ViewModel 都要写一个 Factory代码量会变得很臃肿。优先用默认 Factory 构造函数注入的模式或者用官方推荐的SavedStateHandle方案只有遇到复杂依赖时才考虑为特定 ViewModel 单独定制 Factory。4. ViewModel 存活范围与跨组件共享4.1 Activity 与 Fragment 的共享逻辑ViewModel 有一个非常实用的特性Fragment 可以直接获取它所在 Activity 的 ViewModel。这个特性和 Fragment 的宿主切换有紧密关系。每当我们从 Fragment 里调用new ViewModelProvider(requireActivity()).get(XxxViewModel.class)时Provider 拿到的 ViewModelStore 是宿主 Activity 的而不是 Fragment 自己的。也就是说同一个 Activity 下的多个 Fragment只要共用这个 Activity 的 ViewModelStore就能拿到同一个 ViewModel 实例。这么做的好处很明显Fragment A 往 ViewModel 里写了一条数据Fragment B 不用通过接口回调也不用传参直接读同一个 ViewModel 就能拿到最新状态。这对“列表页 → 详情页”、“主内容区 → 侧边栏”这类交互非常合适。但要注意一个边界Fragment 使用 Activity 的 ViewModelStore意味着这个 ViewModel 的生命周期范围是 Activity 级别的。它不会因为某个 Fragment 销毁而onCleared只会跟着宿主 Activity 走。如果你把一些本应属于 Fragment 级别的状态硬塞进 Activity 级别的 ViewModel那么 Fragment 退出后这些数据依然存活有可能会造成不必要的内存占用和逻辑混乱。4.2 Fragment 之间如何拿到同一个 ViewModel具体到代码层面要让两个 Fragment 共享同一个 ViewModel通常有两种姿势。第一种直接共享 Activity 作用域两个 Fragment 都使用requireActivity()作为 owner 去拿同一个 class 的 ViewModel。这种情况下它们拿到的是同一个实例因为它们在同一个 Activity 的 ViewModelStore 里查找同一个 key。第二种父子 Fragment 共享如果 Fragment 之间有父子关系子 Fragment 可以通过requireParentFragment()作为 owner去获取父 Fragment 的 ViewModel。这样可以实现一个 Fragment 容器内部的状态共享。我个人更推荐第一种因为它的语义最简单凡是这个 Activity 页面范围内的组件都可以共享一个 ViewModel。这种模式下UI 状态集中存放在 ViewModel 里Fragment 只负责渲染和事件转发数据的流向单一且可控。还有一种很容易踩坑的情况你拿 Activity 作用域的 ViewModel想在新打开的 Fragment 里复用旧数据。假设 Fragment A 带着一个id字段往 ViewModel 里写了一次数据Fragment B 读的是同一个 ViewModel如果 ViewModel 里没有清理机制B 就会读到 A 留下的旧值。解决方案是要么通过SavedStateHandle的 key 区分要么在 Fragment 切换时手动重置部分状态要么把 id 这类“每次进入都可能不同”的数据设计成参数而不是 ViewModel 里的共享状态。4.3 作用域并不是越大越好很多初学者看到 ViewModel 的生命周期长就想把所有数据都塞到 Activity 级的 ViewModel 里结果 Activity 没退出一大坨数据就一直躺在内存里。不要这样用。ViewModel 适合存放“和界面状态强相关、在配置变更时需要保留”的数据。但如果是全 App 级别的全局状态应该考虑用专门的数据层方案或者组件化后的共享仓库而不是把 Activity 级 ViewModel 当全局存储用。否则你的 Activity 级别 ViewModel 一旦引用了 Application 里的大对象而不是轻量引用就很容易在页面栈堆积时放大内存压力。作用域的选择本质上是“数据存活范围”的选择。一句话总结你的数据需要活多久就把它放到匹配的生命周期容器里。如果你要的是“整个 App 运行期间都存活”直接放进 ViewModel 是不合适的要么放到 Application 级的数据仓库要么用单独的依赖注入容器管理。5. onCleared 与资源清理的门道5.1 onCleared 何时触发ViewModel 里有一个方法很像 Activity 的onDestroy叫做onCleared()。它是在 ViewModelStore 清除自己的时候被调用的。什么时候 ViewModelStore 会清除自己一般有两个场景场景一宿主 Activity 正常退出finish()系统在销毁流程里会遍历 ViewModelStore 的所有 value逐个调用onCleared()然后清空 HashMap。场景二宿主 Fragment 正常退出或被彻底移除。同样地它的 ViewModelStore 会清空并触发onCleared()。但一定要注意配置变更导致的 Activity 重建不会触发onCleared()。因为旧 Activity 销毁时ViewModelStore 被系统捞走并转交给了新 ActivityViewModel 根本没死自然也不会触发清理回调。这也是 ViewModel 和普通 View 生命周期最大的不同ViewModel 的销毁时机不是“界面销毁”而是“宿主完全终结”。我在项目里会在onCleared()里做这些事取消协程任务、关闭文件流、释放大对象引用。尤其是协程如果 ViewModel 里启动了viewModelScope.launch任务当onCleared()被调用时作用域会被自动取消。不手动挂起的话后台任务就会在宿主销毁后继续跑容易引发状态更新到已销毁 UI 的问题。5.2 与 LiveData、Flow 的关系ViewModel 本身不负责数据流动它更像是一个“容器”。数据怎么从 ViewModel 出来有两种主流方式LiveData 或者 Kotlin Flow。LiveData 是 AndroidX 里的一个可观察数据持有类它有一个特性只有处于STARTED或RESUMED状态的生命周期所有者才会收到数据更新。所以在 ViewModel 里暴露 LiveData 给 UI 层UI 层在销毁时自动解绑不会发生内存泄漏。但 LiveData 有个明显的问题它的转换操作符少组合能力弱。如果你要做复杂的流式处理比如 map、flatMapLatest、debounceLiveData 会变得很难看。这种情况下我建议 ViewModel 向外暴露 Flow比如StateFlowUI 层通过repeatOnLifecycle来收集既安全又灵活。Flow ViewModel 的方案里viewModelScope是一个自带生命周期的协程作用域它会在onCleared()时自动取消所以你在 ViewModel 里发起的网络请求、数据库读写都不会成为在宿主销毁后的“野任务”。这一点比直接在 Activity 里用lifecycleScope更稳妥因为你不需要担心 ViewModel 和 UI 的生命周期不匹配。还有一个常见的坑有些同学喜欢在 ViewModel 里持有 Activity 的 Context。这样做非常危险因为 ViewModel 的生命周期比 Activity 长如果它内部持有了 Activity 引用Activity 被销毁后依然无法被 GC 回收就会造成内存泄漏。正确的做法是ViewModel 里如果需要上下文信息应该用AndroidViewModel它持有的是 ApplicationApplication 的生命周期是全局的或者通过SavedStateHandle去拿Bundle里的数据而不是直接传 Activity。6. 常见问题与排查技巧实录说了这么多原理最后聊聊我在实际开发中遇到过的几个典型问题以及排查思路。6.1 从 ViewModel 拿不到数据问题出在 owner 不一致有一次同事跑来说Fragment A 里往 ViewModel 写的数据Fragment B 里死活读不到。我第一反应就是请他把ViewModelProvider(requireActivity())改成ViewModelProvider(this)试试。果然问题就出在这里。ViewModelProvider(this)里的this指的是 Fragment 自身系统会去找 Fragment 自己的 ViewModelStore。两个 Fragment 各有各的 ViewModelStore所以即使 ViewModel 类名一样它们拿到的也是两个不同的实例。要想共享必须统一 owner也就是都使用同一个 Activity 作为ViewModelStoreOwner。排查这类问题很简单打印 ViewModel 实例的 hashCode 或者toString()如果两个 Fragment 拿到的 hashCode 不一样说明 owner 不一致或者 key 不一致直接检查取用逻辑即可。6.2 OOM 与内存泄漏经常出在持有了不该持有的引用ViewModel 里的内存泄漏绝大多数是因为持有了 Activity / View / Context 引用。我见过一个典型案例ViewModel 里为了更新一个自定义 View把 View 的引用传进去了。结果页面关闭后 ViewModel 还活着View 也被 ViewModel 强引用着Activity 无法被回收内存一次比一次涨。解决思路有两个一是用LiveData/Flow做单向数据流UI 层观察数据自己更新ViewModel 不持有 View二是如果真的需要某种原生入口可以考虑用事件总线或者生命周期感知组件但要非常克制。如果需要 Application 级别的上下文可以直接继承AndroidViewModel它的构造函数里会传入 Application 引用。注意 Application 是全局单例生命周期比 ViewModel 长所以不会反过来把 ViewModel 拖死。6.3 进程被杀与 SavedStateHandle很多开发会误以为 ViewModel 万能就算进程被系统杀了只要 ViewModel 在数据就在。实际上不是这样。当进程级别被杀时比如用户从最近任务列表划掉 App或者系统在极度内存压力下杀进程整个 App 的内存都会被系统回收ViewModelStore 也不复存在。此时 Activity 重建时能拿到的只有系统可能保存下来的savedInstanceStateBundle也就是SavedStateHandle里存的那份数据。所以我的建议是对于必须恢复的关键状态比如当前编辑框的内容、当前页面的滚动位置标记不要只放在 ViewModel 的普通字段里要通过SavedStateHandle的 key 写入。SavedStateHandle 底层会把数据委托给 Bundle系统在适当的时候把它保存下来进程被杀后也能恢复。使用方式也很简单在 ViewModel 的构造函数里接收一个SavedStateHandle然后用getLiveData(key)或者getStateFlow读取用set(key, value)写入。这样即使进程被杀了下次进入时这些值仍然在。6.4 使用 viewModelScope 时的取消时机如果你在 ViewModel 里用了viewModelScope.launch启动任务注意它的取消时机是onCleared()。如果你的 ViewModel 是在配置变更时被转移而不是销毁那onCleared()不会被调用任务还会继续执行。这本身是符合预期的但如果你在任务里写死了某些 UI 逻辑比如获取到数据后直接更新某个 View在 Activity 重建期间旧 View 已经销毁更新逻辑会碰上一个脱锚的引用。解决方案不要在 ViewModel 里操作 ViewViewModel 只负责把状态暴露给 UI 层UI 层自己决定怎么响应该状态。这既符合单向数据流也能避免因生命周期不匹配导致的空指针。我处理过一个案例某个页面加载进度是放在 ViewModel 的MutableLiveDataBoolean里在 Activity 里 collect 这个状态来显隐加载框。转屏之后 Activity 重新创建因为 ViewModel 没死、状态还在新 Activity 一注册观察者就能立刻收到 isLoading 的状态界面进度条直接从“加载中”恢复。这种设计的好处就是UI 状态和 UI 实体解耦界面重建后状态不丢体验自然连贯。做这一堆复盘我个人最深的体会是ViewModel 这套机制的核心价值不是“少写几行代码”而是把“界面的临时状态”和“界面的载体”做了彻底分离。它让界面在无常的销毁重建中依然能稳定恢复状态也让开发者在设计数据流时有了一个清晰的锚点。哪怕你不是用 Jetpack 全家桶理解这套生命周期设计思路对以后处理任何平台上的“视图重建”问题都会有帮助。最后再提一个压箱底的小技巧如果你在自定义 Factory 里创建 ViewModel记得在ViewModelProvider.get前先确认 ViewModelStore 没有该 key 的旧实例。别每次进页面都 new那和直接 new 一个数据类没有区别生命周期管理也全废了。正确的姿势永远是让 Provider 先去查表查不到再创建。踩过几次坑之后我现在写页面数据流的固定套路已经变成了Activity 持有 Activity 级 ViewModelFragment 按需引用基础数据类型全部走 SavedStateHandle复杂业务数据进 ViewModelStore任何耗时任务必须挂在 viewModelScope 下。一套流程下来几乎没再遇到过转屏丢数据这种低级事故。ViewModel 的原理说复杂也复杂说简单也简单无非是搞明白它被存到了哪里、什么时候被创建、什么时候被清理把这几个关键节点搞清楚了你在任何项目里都能做到心里有数。
RELATED

相关推荐

从大龙虾到NanoClaw:HarmonyOS 6.0 上 OpenClaw AI Agent 本地化实战与 TaoToken 接入

从大龙虾到NanoClaw:HarmonyOS 6.0 上 OpenClaw AI Agent 本地化实战与 TaoToken 接入

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

📅 2026/10/12 17:50:13
token限额下AI全栈还可行吗?用TaoToken统一Key实测Python+pywin32+PyMuPDF自动化链路

token限额下AI全栈还可行吗?用TaoToken统一Key实测Python+pywin32+PyMuPDF自动化链路

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

📅 2026/10/12 17:50:13
AI Agent小白入门:绕开幻觉与工程断层的实战指南

AI Agent小白入门:绕开幻觉与工程断层的实战指南

简介:本资源是一份面向AI初学者的《小白入门AI Agent指南》PDF手册,聚焦大模型驱动的智能体(Agent)实践,特别适配DeepSeek等国产大模型生态下的延伸学习需求。内容系统解析AgentGPT这一轻量化AI代理平台的核心逻辑&…

📅 2026/10/12 17:50:13
MORE NEWS

更多资讯

📰

公共卫生数据采集实战:多源异构解析与可信校验

1. 项目概述:这不是一个“爬虫教程”,而是一次公共卫生数据响应的实操复盘2020肺炎数据收集统计——这八个字在当年不是技术选题,而是现实压力下的应急响应。我参与过多个类似场景的模拟项目X,也协助某高校公共卫生实验室搭建过疫…

📰

从零搭建代码评审自动化流程:open-code-review 分层拦截实战

1. 从零搭建一套代码评审自动化流程,我为什么盯上了 open-code-review代码评审这件事,做过的都懂。项目小的时候,两三个人互相看看代码,提两句意见,也就过去了。可一旦团队规模上来,分支多了,提…

📰

C++ Primer 练习解析:友元机制与类设计权限边界

1. 从一道练习看类设计的"权限边界"问题很多人第一次翻到《C Primer》第7章第3节第4小节这道练习时,会觉得它平平无奇——不就是定义一个屏幕类和一个窗口管理类,再让其中一个函数成为另一个类的友元吗?代码量撑死几十行&#xff0…

📰

从零搭建代码评审自动化流程:静态检查、提交钩子与机器人提醒实战

1. 从零搭建一套代码评审自动化流程的完整思路代码评审这件事,做过团队协作的人都有体会。项目一忙,评审就容易流于形式,要么是“看一眼就点通过”,要么是堆积几十个文件没人处理。我所在的团队大概在半年前开始尝试把评审流程做一…

📰

open-code-review:用轻量CLI自动化代码评审流程的实践

1. 项目缘起:周而复始的评审内耗,和一个朴素的判断先说个场景。某团队每周五下午固定做一轮集中代码评审,因为平时大家各自在分支上开发,合入主干前必须有人Review。听起来很正规,但实际走完几轮之后你会发现&#xff…

📰

从库存超卖到分布式锁:乐观锁与悲观锁的实战选型

1. 锁的分类坐标系:在选锁之前先搞清楚几件事几年前我在上线前突然撞上一个现实问题:商品详情页显示库存剩5件,用户下单买了3件,支付成功之后后台查库存却还是5件,下单通道继续开着,后台一会儿就欠了20多件…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬