<?xml version="1.0" encoding="utf-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><title>Resmic's | BLOG</title><link>https://blog.resmic.cn/</link><description>个人博客</description><item><title>Jetpack Compose 动画系统深度解析：从基础到高性能实战</title><link>https://blog.resmic.cn/post/367.html</link><description>&lt;p&gt;&lt;img src=&quot;https://blog.resmic.cn/zb_users/upload/2026/08/20260827080402178778904273310.jpg&quot; alt=&quot;封面&quot; style=&quot;width:100%;&quot;&gt;&lt;/p&gt;

&lt;h2&gt;为什么 Jetpack Compose 动画值得深入学习&lt;/h2&gt;
&lt;p&gt;在传统 Android 开发中，实现一个流畅的动画往往需要繁琐的 ObjectAnimator、AnimatorSet 以及各种 XML 配置文件。而 Jetpack Compose 彻底改变了这一局面，它以声明式、函数式的方式重新定义了 Android 动画开发体验。&lt;/p&gt;
&lt;p&gt;Compose 动画系统的核心理念是：&lt;strong&gt;动画是状态变化的可视化表达&lt;/strong&gt;。当 UI 状态从 A 变化到 B 时，Compose 会自动在两个状态之间进行插值，产生平滑的过渡效果。开发者无需手动管理动画的开始、暂停和结束，只需关注状态本身。&lt;/p&gt;
&lt;p&gt;根据 Google 2025 年 Android 开发者调查，超过 78% 的新 Android 项目已采用 Jetpack Compose 作为主要 UI 框架，而其中动画功能的使用率也在逐年攀升。掌握 Compose 动画不仅能提升用户体验，还能显著减少代码量。&lt;/p&gt;

&lt;h2&gt;Compose 动画 API 全景：选对工具事半功倍&lt;/h2&gt;
&lt;p&gt;Compose 提供了从简单到复杂的完整动画 API 体系，理解它们各自的定位是高效开发的前提。&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;animate*AsState&lt;/strong&gt;：最简单的高级动画 API，适合单一值的动画过渡，如颜色、大小、偏移量等&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;AnimatedVisibility&lt;/strong&gt;：控制 Composable 的显示/隐藏动画，内置多种进出场效果&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;AnimatedContent&lt;/strong&gt;：在不同内容之间进行动画切换，适合内容替换场景&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Crossfade&lt;/strong&gt;：简化版的内容切换动画，仅支持淡入淡出&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;updateTransition&lt;/strong&gt;：管理多个动画值同步执行，适合复杂多状态动画&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;rememberInfiniteTransition&lt;/strong&gt;：创建无限循环动画，如加载指示器、呼吸灯效果&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Animatable&lt;/strong&gt;：底层动画 API，提供最大的控制灵活性，支持手势驱动动画&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;选择动画 API 的基本原则：&lt;em&gt;优先使用高级 API，仅在高级 API 无法满足需求时才降级到底层 API&lt;/em&gt;。这样可以减少样板代码并避免常见错误。&lt;/p&gt;

&lt;h2&gt;animate*AsState：从零开始的第一个动画&lt;/h2&gt;
&lt;p&gt;animate*AsState 是 Compose 中最直观的动画方式。它将普通的状态值包装成动画值，每当目标值变化时，动画值会平滑地过渡到新值。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@Composable
fun AnimatedBox() {
    var expanded by remember { mutableStateOf(false) }
    
    // 动画化 dp 值
    val size by animateDpAsState(
        targetValue = if (expanded) 200.dp else 100.dp,
        animationSpec = spring(
            dampingRatio = Spring.DampingRatioMediumBouncy,
            stiffness = Spring.StiffnessLow
        ),
        label = &quot;box_size&quot;
    )
    
    // 动画化颜色值
    val color by animateColorAsState(
        targetValue = if (expanded) Color(0xFF6200EE) else Color(0xFF03DAC5),
        animationSpec = tween(durationMillis = 300),
        label = &quot;box_color&quot;
    )
    
    Box(
        modifier = Modifier
            .size(size)
            .background(color, RoundedCornerShape(16.dp))
            .clickable { expanded = !expanded }
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意 &lt;code&gt;label&lt;/code&gt; 参数是 Compose 1.4+ 新增的，用于在 Android Studio 的动画预览工具中识别动画，强烈建议为每个动画指定有意义的 label。&lt;/p&gt;
&lt;p&gt;常见的 animate*AsState 变体包括：&lt;code&gt;animateFloatAsState&lt;/code&gt;、&lt;code&gt;animateIntAsState&lt;/code&gt;、&lt;code&gt;animateDpAsState&lt;/code&gt;、&lt;code&gt;animateColorAsState&lt;/code&gt;、&lt;code&gt;animateSizeAsState&lt;/code&gt;、&lt;code&gt;animateOffsetAsState&lt;/code&gt; 等。&lt;/p&gt;

&lt;h2&gt;AnimationSpec 深度解析：让动画更有灵魂&lt;/h2&gt;
&lt;p&gt;AnimationSpec 定义了动画的运动曲线，是决定动画&quot;感觉&quot;的关键参数。Compose 提供了多种内置 AnimationSpec：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;tween&lt;/strong&gt;：基于时间的动画，可自定义时长和缓动曲线（Easing）&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;spring&lt;/strong&gt;：基于物理弹簧模型的动画，更自然，参数为阻尼比和刚度&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;keyframes&lt;/strong&gt;：关键帧动画，可以在特定时间点指定特定值&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;snap&lt;/strong&gt;：瞬间跳转，没有过渡动画，适用于需要即时响应的场景&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;repeatable&lt;/strong&gt;：重复执行指定次数的动画&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;infiniteRepeatable&lt;/strong&gt;：无限重复动画&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// Spring 动画 - 更自然的物理感
val springSpec = spring&amp;lt;Float&amp;gt;(
    dampingRatio = Spring.DampingRatioLowBouncy,  // 低阻尼 = 更多弹跳
    stiffness = Spring.StiffnessMedium             // 中等刚度
)

// Tween 动画 - 精确控制时长
val tweenSpec = tween&amp;lt;Float&amp;gt;(
    durationMillis = 500,
    delayMillis = 100,
    easing = FastOutSlowInEasing  // Material Design 标准缓动
)

// Keyframes 动画 - 精细控制每个时间点
val keyframesSpec = keyframes&amp;lt;Float&amp;gt; {
    durationMillis = 1000
    0f at 0 with LinearEasing       // 起点
    0.8f at 600 with FastOutSlowInEasing  // 600ms 时到达 80%
    1f at 1000                       // 终点
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在实际开发中，&lt;strong&gt;Spring 动画通常比 Tween 更自然&lt;/strong&gt;，因为它模拟真实物理运动，会根据当前速度动态调整。Google Material Design 3 的大部分动画都基于弹簧模型。&lt;/p&gt;

&lt;h2&gt;updateTransition：协调多值同步动画&lt;/h2&gt;
&lt;p&gt;当需要同时动画化多个值，并且这些值都基于同一个状态时，updateTransition 是最佳选择。它确保所有动画值同步开始和结束，避免出现不协调的过渡效果。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;enum class CardState { Collapsed, Expanded }

@Composable
fun AnimatedCard() {
    var cardState by remember { mutableStateOf(CardState.Collapsed) }
    
    val transition = updateTransition(
        targetState = cardState,
        label = &quot;card_transition&quot;
    )
    
    val cardElevation by transition.animateDp(
        transitionSpec = { spring(stiffness = Spring.StiffnessLow) },
        label = &quot;card_elevation&quot;
    ) { state -&gt;
        when (state) {
            CardState.Collapsed -&gt; 2.dp
            CardState.Expanded -&gt; 8.dp
        }
    }
    
    val cardHeight by transition.animateDp(
        transitionSpec = { tween(durationMillis = 400) },
        label = &quot;card_height&quot;
    ) { state -&gt;
        when (state) {
            CardState.Collapsed -&gt; 80.dp
            CardState.Expanded -&gt; 240.dp
        }
    }
    
    val contentAlpha by transition.animateFloat(
        transitionSpec = { tween(durationMillis = 300, delayMillis = 100) },
        label = &quot;content_alpha&quot;
    ) { state -&gt;
        when (state) {
            CardState.Collapsed -&gt; 0f
            CardState.Expanded -&gt; 1f
        }
    }
    
    Card(
        elevation = CardDefaults.cardElevation(defaultElevation = cardElevation),
        modifier = Modifier
            .fillMaxWidth()
            .height(cardHeight)
            .clickable {
                cardState = if (cardState == CardState.Collapsed)
                    CardState.Expanded else CardState.Collapsed
            }
    ) {
        // 内容区域
        Box(modifier = Modifier.alpha(contentAlpha)) {
            // 展开后显示的详细内容
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;updateTransition 的另一个优势是对 Android Studio 动画预览工具的完整支持——你可以在 IDE 中实时预览所有状态间的过渡效果，极大提升开发效率。&lt;/p&gt;

&lt;h2&gt;手势驱动动画：用 Animatable 实现拖拽交互&lt;/h2&gt;
&lt;p&gt;Animatable 是 Compose 动画系统中最底层、最灵活的 API。它支持在动画执行过程中动态修改目标值，非常适合手势驱动的交互动画。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@Composable
fun DraggableCard() {
    val offsetX = remember { Animatable(0f) }
    val offsetY = remember { Animatable(0f) }
    val coroutineScope = rememberCoroutineScope()
    
    Box(
        modifier = Modifier
            .offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
            .pointerInput(Unit) {
                detectDragGestures(
                    onDragEnd = {
                        // 手势结束，弹回原位
                        coroutineScope.launch {
                            launch { offsetX.animateTo(0f, spring(dampingRatio = Spring.DampingRatioMediumBouncy)) }
                            launch { offsetY.animateTo(0f, spring(dampingRatio = Spring.DampingRatioMediumBouncy)) }
                        }
                    }
                ) { change, dragAmount -&gt;
                    change.consume()
                    coroutineScope.launch {
                        // 跟随手指移动（snap = 瞬间，无动画延迟）
                        offsetX.snapTo(offsetX.value + dragAmount.x)
                        offsetY.snapTo(offsetY.value + dragAmount.y)
                    }
                }
            }
            .size(120.dp)
            .background(Color(0xFF6200EE), RoundedCornerShape(16.dp))
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关键技巧：在手势跟踪阶段使用 &lt;code&gt;snapTo&lt;/code&gt;（无动画，即时响应），在手势结束阶段使用 &lt;code&gt;animateTo&lt;/code&gt;（有动画，自然回弹）。两者的配合是手势动画流畅的核心。&lt;/p&gt;

&lt;h2&gt;性能优化：让动画保持 60fps/120fps&lt;/h2&gt;
&lt;p&gt;再好的动画设计，如果执行时卡顿，都会带来糟糕的用户体验。以下是 Compose 动画性能优化的关键实践：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;使用 Modifier.graphicsLayer&lt;/strong&gt;：透明度、缩放、旋转等变换应通过 graphicsLayer 而非 Modifier.alpha/scale，前者在 RenderThread 执行，不阻塞主线程&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;避免在动画值中触发重组&lt;/strong&gt;：用 derivedStateOf 包裹从动画值派生的计算，减少不必要的重组&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;使用 Canvas 绘制复杂动画&lt;/strong&gt;：对于粒子系统等复杂动画，直接在 Canvas 中绘制比创建大量 Composable 更高效&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;启用 RenderEffect（Android 12+）&lt;/strong&gt;：BlurEffect 等视觉效果应使用 RenderEffect 而非模糊 Bitmap&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;合理设置动画时长&lt;/strong&gt;：移动端推荐 200-500ms，过短感觉突兀，过长影响操作流畅性&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// ✅ 正确：使用 graphicsLayer，动画在 RenderThread 执行
Box(
    modifier = Modifier.graphicsLayer {
        alpha = alphaValue
        scaleX = scaleValue
        scaleY = scaleValue
    }
)

// ❌ 避免：直接用 Modifier.alpha/scale 可能触发重组
Box(
    modifier = Modifier
        .alpha(alphaValue)
        .scale(scaleValue)
)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 Android Studio Profiler 的 Compose 重组追踪功能，可以可视化地找出动画执行时的性能瓶颈。在目标设备上测试时，开启 &lt;em&gt;Show GPU Overdraw&lt;/em&gt; 和 &lt;em&gt;Profile GPU Rendering&lt;/em&gt; 也是发现渲染问题的有效手段。&lt;/p&gt;

&lt;h2&gt;实战案例：打造 Material You 风格的底部导航栏动画&lt;/h2&gt;
&lt;p&gt;综合运用上述知识，我们来实现一个符合 Material Design 3 规范的底部导航栏动画效果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@Composable
fun AnimatedBottomNavItem(
    selected: Boolean,
    onClick: () -&gt; Unit,
    icon: @Composable () -&gt; Unit,
    label: String
) {
    val transition = updateTransition(selected, label = &quot;nav_item_$label&quot;)
    
    // 指示器宽度动画
    val indicatorWidth by transition.animateDp(
        transitionSpec = { spring(stiffness = Spring.StiffnessMediumLow) },
        label = &quot;indicator_width&quot;
    ) { isSelected -&gt; if (isSelected) 64.dp else 0.dp }
    
    // 图标偏移动画（选中时图标上移）
    val iconOffset by transition.animateDp(
        transitionSpec = { tween(200) },
        label = &quot;icon_offset&quot;
    ) { isSelected -&gt; if (isSelected) (-2).dp else 0.dp }
    
    // 标签透明度动画
    val labelAlpha by transition.animateFloat(
        transitionSpec = { tween(200, delayMillis = if (selected) 100 else 0) },
        label = &quot;label_alpha&quot;
    ) { isSelected -&gt; if (isSelected) 1f else 0.6f }
    
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier
            .clickable(onClick = onClick)
            .padding(vertical = 8.dp, horizontal = 16.dp)
    ) {
        // 指示器背景
        Box(
            contentAlignment = Alignment.Center,
            modifier = Modifier
                .width(indicatorWidth)
                .height(32.dp)
                .background(
                    if (selected) MaterialTheme.colorScheme.secondaryContainer
                    else Color.Transparent,
                    RoundedCornerShape(16.dp)
                )
        ) {
            Box(modifier = Modifier.offset(y = iconOffset)) {
                icon()
            }
        }
        
        Spacer(modifier = Modifier.height(4.dp))
        
        Text(
            text = label,
            style = MaterialTheme.typography.labelSmall,
            modifier = Modifier.graphicsLayer { alpha = labelAlpha }
        )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个实现涵盖了本文介绍的多个知识点：updateTransition 协调多值动画、spring/tween 混用、graphicsLayer 性能优化、以及符合 Material Design 3 的视觉规范。&lt;/p&gt;
&lt;p&gt;Jetpack Compose 动画系统还在持续演进，Compose 1.7 引入了更强大的共享元素动画（Shared Element Transition），Android 16 也带来了更多底层动画性能提升。持续关注官方更新，将是 Android 开发者保持竞争力的重要途径。&lt;/p&gt;
</description><pubDate>Thu, 27 Aug 2026 08:04:46 +0800</pubDate></item><item><title>生产级 AI Agent 构建实战：架构设计、工具调用与避坑指南</title><link>https://blog.resmic.cn/post/366.html</link><description>&lt;p&gt;&lt;img src=&quot;https://blog.resmic.cn/zb_users/upload/2026/08/20260826080340178770262048512.jpg&quot; alt=&quot;封面&quot; style=&quot;width:100%;&quot;/&gt;&lt;/p&gt;&lt;h2&gt;一、什么是 AI Agent？从概念到落地&lt;/h2&gt;&lt;p&gt;AI Agent（智能体）是一种能够感知环境、做出决策并采取行动以实现特定目标的 AI 系统。与传统的单轮问答不同，Agent 具备持续行动能力，可以通过调用工具、访问外部资源来完成复杂任务。&lt;/p&gt;&lt;p&gt;自 ChatGPT 发布以来，AI Agent 的热度持续攀升。从 AutoGPT、BabyAGI 的早期探索，到 LangChain、LlamaIndex 的生态繁荣，再到如今 OpenAI Agents SDK、Anthropic Claude 等原生 Agent 框架的成熟，构建 AI Agent 的门槛越来越低，但真正跑通生产环境的 Agent 仍然面临重重挑战。本文将结合实战经验，梳理核心技术点，帮助你少走弯路。&lt;/p&gt;&lt;h2&gt;二、Agent 架构设计：ReAct 与 Plan-and-Execute&lt;/h2&gt;&lt;p&gt;目前主流的 Agent 架构有两类：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;ReAct（Reasoning + Acting）&lt;/strong&gt;：大模型交替输出「思考」和「行动」，每次工具调用结果都会反馈到下一轮推理。优点是灵活，适合动态任务；缺点是长链路时容易偏离目标。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Plan-and-Execute&lt;/strong&gt;：先生成完整计划，再逐步执行。适合结构化任务，但计划生成质量要求高，容错能力弱。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;在实际工程中，推荐使用&lt;strong&gt;混合架构&lt;/strong&gt;：用 Plan-and-Execute 做顶层任务分解，每个子任务内部用 ReAct 循环。这样既保证了任务方向的稳定性，又允许执行阶段灵活应对异常情况。&lt;/p&gt;&lt;p&gt;核心伪代码逻辑：先调用 LLM 生成 plan，然后逐步执行每个 subtask，每个 subtask 内部用 react_loop 处理，最后汇总结果。react_loop 内部交替进行 think_and_act 和工具调用，直到输出 final_answer 或达到 max_iterations。&lt;/p&gt;&lt;h2&gt;三、工具调用（Function Calling）最佳实践&lt;/h2&gt;&lt;p&gt;工具调用是 AI Agent 的核心能力，但在生产环境中稳定运行并不简单。以下是几个关键实践：&lt;/p&gt;&lt;h3&gt;3.1 工具描述要精准&lt;/h3&gt;&lt;p&gt;工具的 JSON Schema 描述直接影响模型的调用准确率。参数描述要具体，包含示例值，明确说明边界条件。以搜索工具为例，description 字段需写明&amp;quot;适用于查询特定 SKU、商品名称或分类，不适用于库存实时数据&amp;quot;，示例值包含&amp;quot;iPhone 15 Pro&amp;quot;、&amp;quot;SKU-98765&amp;quot;等，limit 参数需标注默认值和最大值限制。&lt;/p&gt;&lt;h3&gt;3.2 工具调用的错误处理&lt;/h3&gt;&lt;p&gt;工具调用失败时，不要让 Agent 卡死，要设计清晰的降级策略。推荐采用指数退避重试机制：第一次失败等 1 秒，第二次等 2 秒，第三次等 4 秒后彻底放弃并返回明确的错误信息。TimeoutError、ToolNotFoundError 等不同异常需要分别处理，前者可重试，后者无需重试。&lt;/p&gt;&lt;h2&gt;四、记忆管理：让 Agent 不再「失忆」&lt;/h2&gt;&lt;p&gt;Agent 的记忆系统直接决定多轮交互体验的质量。工程上通常将记忆分为四个层次：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;工作记忆（Working Memory）&lt;/strong&gt;：当前对话上下文，存在 Context Window 里，受 Token 限制。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;情景记忆（Episodic Memory）&lt;/strong&gt;：历史对话摘要，定期压缩后存入数据库，按需召回。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;语义记忆（Semantic Memory）&lt;/strong&gt;：向量化的知识库，通过 RAG 检索。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;程序记忆（Procedural Memory）&lt;/strong&gt;：固化的操作流程，以 Prompt 模板或代码形式存储。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;关键实现要点：在每轮对话结束时，异步触发记忆压缩；对话开始时并行召回相关记忆，控制召回 Token 在总上下文的 20% 以内。MemoryManager 的核心方法包括 recall（从向量库按相似度召回）和 consolidate（异步压缩对话写入向量库）。&lt;/p&gt;&lt;h2&gt;五、多轮对话优化：防止上下文爆炸&lt;/h2&gt;&lt;p&gt;随着对话轮数增加，Context Window 会被填满，导致性能下降甚至报错。生产环境必须做好上下文管理：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;动态截断&lt;/strong&gt;：保留最近 N 轮对话 + 系统 Prompt + 关键记忆，其余压缩为摘要。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Token 预算管理&lt;/strong&gt;：为不同内容分配 Token 配额，工具输出超过阈值时自动截断并标注「内容已截断」。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;关键信息锚定&lt;/strong&gt;：用户明确的偏好、任务目标、已确认的事实，优先级最高，永远保留在上下文中。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;build_context 函数的核心逻辑：计算可用 Token 预算（总限额减去系统提示词和输出预留），然后从最近的消息逆序遍历，逐条累加 Token 数量，超过预算则停止，最终拼接系统提示词和筛选后的消息列表返回。&lt;/p&gt;&lt;h2&gt;六、生产环境的可观测性建设&lt;/h2&gt;&lt;p&gt;AI Agent 的调试难度远高于普通业务代码，完善的可观测性是稳定运行的基础。推荐的监控体系：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Trace 链路追踪&lt;/strong&gt;：每次 Agent 运行生成唯一 trace_id，记录每步思考、工具调用、耗时、Token 消耗。推荐使用 LangSmith、Phoenix 或自建 OpenTelemetry 方案。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;关键指标监控&lt;/strong&gt;：任务完成率、平均轮数、工具调用成功率、平均 Token 消耗、P99 延迟。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;异常告警&lt;/strong&gt;：工具调用失败率 &amp;gt;5% 触发告警；单次 Token 消耗超阈值触发告警；Agent 陷入死循环（超过 max_iterations）触发告警。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;使用 OpenTelemetry 时，通过 @tracer.start_as_current_span 装饰器包裹 Agent 运行函数，在 span 上记录 task、tool_calls、total_tokens、success 等属性，即可在 Jaeger 或 Grafana Tempo 中可视化完整调用链路。&lt;/p&gt;&lt;h2&gt;七、常见踩坑与解决方案&lt;/h2&gt;&lt;p&gt;以下是生产环境中最常遇到的问题及解决方案：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;问题1：Agent 陷入死循环&lt;/strong&gt;：设置 max_iterations，使用递减 Token Budget（越到后面越逼迫 Agent 输出结果）。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;问题2：工具调用参数幻觉&lt;/strong&gt;：加强工具描述，使用 Pydantic 严格验证参数，拒绝无效调用时返回清晰错误信息。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;问题3：长任务超时&lt;/strong&gt;：将任务分解为可检查点的子任务，支持断点续跑；前端使用 SSE 推送进度。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;问题4：并发冲突&lt;/strong&gt;：共享工具（如数据库写操作）加分布式锁；Agent 实例不共享状态。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;问题5：成本失控&lt;/strong&gt;：对每个 API Key 设置每日 Token 上限；复杂任务用强模型，简单子任务用小模型分级处理。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;AI Agent 的落地是一个持续迭代的过程。从 Demo 到生产，最大的鸿沟不在于模型能力，而在于工程基础设施的完善程度。希望本文的实战经验能帮助你更快走过这段路。&lt;/p&gt;</description><pubDate>Wed, 26 Aug 2026 08:09:08 +0800</pubDate></item><item><title>Android 16 核心新特性实战解析：你必须掌握的四大升级</title><link>https://blog.resmic.cn/post/365.html</link><description>&lt;p&gt;&lt;img src=&quot;https://blog.resmic.cn/zb_users/upload/2026/08/20260825080617178761637729142.jpg&quot; alt=&quot;封面&quot; style=&quot;width:100%;&quot;/&gt;&lt;/p&gt;&lt;h2&gt;Android 16 概览：为何值得关注&lt;/h2&gt;&lt;p&gt;2026年，Android 16 正式发布，这是 Google 近年来变化最大的一个版本。它不仅在视觉层面引入了 Material 3 Expressive 设计语言，更在底层系统架构、安全隐私机制、媒体处理能力等方面做出了重大升级。对于 Android 开发者而言，这是一次不可错过的技术红利窗口期。&lt;/p&gt;&lt;p&gt;本文基于 Android 16 正式 SDK，从实际开发场景出发，精选四大核心新特性进行深度解析，并附完整可运行代码示例，帮助你快速将新特性落地到项目中。&lt;/p&gt;&lt;h2&gt;一、预测性返回手势（Predictive Back Gesture）全面普及&lt;/h2&gt;&lt;p&gt;预测性返回手势（Predictive Back）在 Android 13 中首次引入，经过数个版本的迭代，在 Android 16 中已成为强制要求——&lt;strong&gt;所有 targetSdk=36 的应用必须适配&lt;/strong&gt;，否则无法上架 Google Play。&lt;/p&gt;&lt;p&gt;其核心价值在于：用户在向右滑动时，可以实时预览即将返回的目标界面（上一个 Activity 或桌面），大幅降低误操作率，提升交互确定性。&lt;/p&gt;&lt;p&gt;适配步骤如下：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;在 &lt;code&gt;AndroidManifest.xml&lt;/code&gt; 中为每个 Activity 声明 &lt;code&gt;android:enableOnBackInvokedCallback=&amp;quot;true&amp;quot;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;使用 &lt;code&gt;OnBackPressedDispatcher&lt;/code&gt; 替换过时的 &lt;code&gt;onBackPressed()&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;对于有拦截需求的页面（如表单保存确认），使用 &lt;code&gt;OnBackPressedCallback&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre&gt;//&amp;nbsp;在&amp;nbsp;Activity&amp;nbsp;中注册返回拦截
class&amp;nbsp;EditFormActivity&amp;nbsp;:&amp;nbsp;AppCompatActivity()&amp;nbsp;{

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;override&amp;nbsp;fun&amp;nbsp;onCreate(savedInstanceState:&amp;nbsp;Bundle?)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;super.onCreate(savedInstanceState)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setContentView(R.layout.activity_edit_form)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;注册返回拦截回调
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;onBackPressedDispatcher.addCallback(this,&amp;nbsp;object&amp;nbsp;:&amp;nbsp;OnBackPressedCallback(true)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;override&amp;nbsp;fun&amp;nbsp;handleOnBackPressed()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;有未保存内容时弹出确认对话框
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(hasUnsavedChanges())&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;showSaveConfirmDialog()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;else&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;isEnabled&amp;nbsp;=&amp;nbsp;false
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;onBackPressedDispatcher.onBackPressed()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;})
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;fun&amp;nbsp;hasUnsavedChanges():&amp;nbsp;Boolean&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;业务判断逻辑
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;true
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;fun&amp;nbsp;showSaveConfirmDialog()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MaterialAlertDialogBuilder(this)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.setTitle(&amp;quot;保存修改？&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.setMessage(&amp;quot;您有未保存的内容，确定要离开吗？&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.setPositiveButton(&amp;quot;保存&amp;quot;)&amp;nbsp;{&amp;nbsp;_,&amp;nbsp;_&amp;nbsp;-&amp;gt;&amp;nbsp;saveAndFinish()&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.setNegativeButton(&amp;quot;放弃&amp;quot;)&amp;nbsp;{&amp;nbsp;_,&amp;nbsp;_&amp;nbsp;-&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;onBackPressedDispatcher.onBackPressed()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.show()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;&lt;p&gt;对于 Fragment 栈管理，推荐使用 Navigation Component，它已经内置了对预测性返回手势的支持，无需额外适配。&lt;/p&gt;&lt;h2&gt;二、自适应刷新率（Adaptive Refresh Rate）开发者指南&lt;/h2&gt;&lt;p&gt;Android 16 引入了更细粒度的帧率控制 API，允许开发者为不同的 UI 场景精确指定目标帧率，配合高刷屏（90Hz/120Hz/144Hz）设备实现真正的性能与功耗平衡。&lt;/p&gt;&lt;p&gt;核心 API 是 &lt;code&gt;Window.setFrameRateBoostOnTouchEnabled()&lt;/code&gt; 和 Surface 的 &lt;code&gt;setFrameRate()&lt;/code&gt; 方法：&lt;/p&gt;&lt;pre&gt;//&amp;nbsp;在&amp;nbsp;SurfaceView&amp;nbsp;中设置目标帧率（适用于游戏/动画场景）
class&amp;nbsp;GameSurfaceView(context:&amp;nbsp;Context)&amp;nbsp;:&amp;nbsp;SurfaceView(context),&amp;nbsp;SurfaceHolder.Callback&amp;nbsp;{

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;init&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;holder.addCallback(this)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;override&amp;nbsp;fun&amp;nbsp;surfaceCreated(holder:&amp;nbsp;SurfaceHolder)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;设置帧率：60fps，固定模式（不允许系统降低）
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(Build.VERSION.SDK_INT&amp;nbsp;&amp;gt;=&amp;nbsp;Build.VERSION_CODES.VANILLA_ICE_CREAM)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;holder.surface.setFrameRate(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;60f,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Surface.CHANGE_FRAME_RATE_ALWAYS
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;override&amp;nbsp;fun&amp;nbsp;surfaceChanged(holder:&amp;nbsp;SurfaceHolder,&amp;nbsp;format:&amp;nbsp;Int,&amp;nbsp;width:&amp;nbsp;Int,&amp;nbsp;height:&amp;nbsp;Int)&amp;nbsp;{}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;override&amp;nbsp;fun&amp;nbsp;surfaceDestroyed(holder:&amp;nbsp;SurfaceHolder)&amp;nbsp;{}
}

//&amp;nbsp;在普通&amp;nbsp;View&amp;nbsp;动画中，使用&amp;nbsp;ValueAnimator&amp;nbsp;配合&amp;nbsp;setFrameDelay
fun&amp;nbsp;startSmoothAnimation(view:&amp;nbsp;View)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ValueAnimator.ofFloat(0f,&amp;nbsp;1f).apply&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;duration&amp;nbsp;=&amp;nbsp;300
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;interpolator&amp;nbsp;=&amp;nbsp;FastOutSlowInInterpolator()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;addUpdateListener&amp;nbsp;{&amp;nbsp;animator&amp;nbsp;-&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;view.alpha&amp;nbsp;=&amp;nbsp;animator.animatedValue&amp;nbsp;as&amp;nbsp;Float
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;start()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;&lt;p&gt;建议策略：静态内容（阅读页、列表停止滚动）维持 30-60fps 降低功耗；滚动、动画期间提升至设备最高支持帧率；游戏场景使用固定帧率避免撕裂。&lt;/p&gt;&lt;h2&gt;三、隐私沙盒升级：Photos &amp;amp; Videos Picker 强制化&lt;/h2&gt;&lt;p&gt;Android 16 进一步收紧了媒体文件访问权限。&lt;code&gt;READ_MEDIA_IMAGES&lt;/code&gt; 和 &lt;code&gt;READ_MEDIA_VIDEO&lt;/code&gt; 权限在 targetSdk=36 时行为发生变化：&lt;strong&gt;即使用户授予了权限，也只能通过 Photo Picker 访问完整相册&lt;/strong&gt;，直接路径访问受到限制。&lt;/p&gt;&lt;p&gt;迁移方案：使用官方 Photo Picker（&lt;code&gt;PickVisualMedia&lt;/code&gt;）替代自定义图库选择器：&lt;/p&gt;&lt;pre&gt;class&amp;nbsp;MainActivity&amp;nbsp;:&amp;nbsp;AppCompatActivity()&amp;nbsp;{

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;注册单图选择器
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;val&amp;nbsp;pickSingleMedia&amp;nbsp;=&amp;nbsp;registerForActivityResult(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ActivityResultContracts.PickVisualMedia()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)&amp;nbsp;{&amp;nbsp;uri&amp;nbsp;-&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;uri?.let&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;直接使用&amp;nbsp;uri，无需文件权限
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;imageView.setImageURI(it)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;processSelectedImage(it)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;注册多图选择器（最多选&amp;nbsp;9&amp;nbsp;张）
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;val&amp;nbsp;pickMultipleMedia&amp;nbsp;=&amp;nbsp;registerForActivityResult(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ActivityResultContracts.PickMultipleVisualMedia(maxItems&amp;nbsp;=&amp;nbsp;9)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)&amp;nbsp;{&amp;nbsp;uris&amp;nbsp;-&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;uris.forEach&amp;nbsp;{&amp;nbsp;uri&amp;nbsp;-&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Log.d(&amp;quot;Picker&amp;quot;,&amp;nbsp;&amp;quot;Selected:&amp;nbsp;$uri&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fun&amp;nbsp;openSinglePicker()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;只允许选图片
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pickSingleMedia.launch(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fun&amp;nbsp;openMultiPicker()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;允许选图片和视频
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pickMultipleMedia.launch(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageAndVideo)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;fun&amp;nbsp;processSelectedImage(uri:&amp;nbsp;Uri)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;使用&amp;nbsp;ContentResolver&amp;nbsp;读取，不需要&amp;nbsp;MANAGE_EXTERNAL_STORAGE
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;contentResolver.openInputStream(uri)?.use&amp;nbsp;{&amp;nbsp;stream&amp;nbsp;-&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;bitmap&amp;nbsp;=&amp;nbsp;BitmapFactory.decodeStream(stream)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;处理图片...
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;Photo Picker 无需任何运行时权限即可使用&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;系统级 UI，用户体验一致，且内置了云端相册（Google Photos）支持&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;返回的 Uri 在应用生命周期内有效，如需持久化需调用 &lt;code&gt;takePersistableUriPermission()&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;四、Health Connect 深度集成与健康数据开发&lt;/h2&gt;&lt;p&gt;Android 16 将 Health Connect 从独立应用升级为系统内置组件，这意味着开发者可以更可靠地访问健康数据，无需再引导用户安装额外应用。&lt;/p&gt;&lt;p&gt;以读取用户步数为例：&lt;/p&gt;&lt;pre&gt;//&amp;nbsp;build.gradle.kts
dependencies&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;implementation(&amp;quot;androidx.health.connect:connect-client:1.1.0&amp;quot;)
}&lt;/pre&gt;&lt;pre&gt;class&amp;nbsp;HealthViewModel(application:&amp;nbsp;Application)&amp;nbsp;:&amp;nbsp;AndroidViewModel(application)&amp;nbsp;{

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;val&amp;nbsp;healthConnectClient&amp;nbsp;by&amp;nbsp;lazy&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HealthConnectClient.getOrCreate(application)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;suspend&amp;nbsp;fun&amp;nbsp;requestPermissions(activity:&amp;nbsp;Activity)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;permissions&amp;nbsp;=&amp;nbsp;setOf(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HealthPermission.getReadPermission(StepsRecord::class),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;HealthPermission.getReadPermission(HeartRateRecord::class)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;granted&amp;nbsp;=&amp;nbsp;healthConnectClient.permissionController
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.getGrantedPermissions()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(!granted.containsAll(permissions))&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;请求权限（会跳转系统权限页面）
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;activity.startActivity(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;healthConnectClient.permissionController
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.createRequestPermissionResultContract()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.createIntent(activity,&amp;nbsp;permissions)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;suspend&amp;nbsp;fun&amp;nbsp;getTodaySteps():&amp;nbsp;Long&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;today&amp;nbsp;=&amp;nbsp;LocalDate.now()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;startTime&amp;nbsp;=&amp;nbsp;today.atStartOfDay().toInstant(ZoneOffset.UTC)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;endTime&amp;nbsp;=&amp;nbsp;today.plusDays(1).atStartOfDay().toInstant(ZoneOffset.UTC)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;response&amp;nbsp;=&amp;nbsp;healthConnectClient.readRecords(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ReadRecordsRequest(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;recordType&amp;nbsp;=&amp;nbsp;StepsRecord::class,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;timeRangeFilter&amp;nbsp;=&amp;nbsp;TimeRangeFilter.between(startTime,&amp;nbsp;endTime)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;response.records.sumOf&amp;nbsp;{&amp;nbsp;it.count&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;&lt;p&gt;Health Connect 的权限模型非常严格：每次读写访问都需要独立授权，且用户可以随时在系统设置中撤销。建议在关键数据读取前先检查权限状态，优雅降级展示。&lt;/p&gt;&lt;h2&gt;总结与升级建议&lt;/h2&gt;&lt;p&gt;Android 16 的这四大特性，分别对应了交互体验（预测性返回）、性能优化（自适应帧率）、隐私合规（Photo Picker）和生态扩展（Health Connect）四个维度。建议按以下优先级逐步适配：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;P0（必须）&lt;/strong&gt;：预测性返回手势适配，targetSdk=36 强制要求&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;P1（强烈建议）&lt;/strong&gt;：Photo Picker 迁移，未来版本将移除旧权限&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;P2（按需）&lt;/strong&gt;：自适应帧率，对动画/游戏类应用收益显著&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;P3（扩展）&lt;/strong&gt;：Health Connect，健康类应用必选&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Android 16 的发布周期也有所调整，Google 计划将主版本发布时间从秋季提前到春季（Q2），这意味着开发者需要更早关注 Beta 版本，预留充足的适配时间。建议现在就将项目的 compileSdk 升级至 36，在不强制 targetSdk 的前提下提前测试兼容性。&lt;/p&gt;</description><pubDate>Tue, 25 Aug 2026 08:06:33 +0800</pubDate></item><item><title>大模型推理加速全攻略：vLLM、SGLang与TensorRT-LLM深度实战</title><link>https://blog.resmic.cn/post/364.html</link><description>&lt;p&gt;&lt;img src=&quot;https://blog.resmic.cn/zb_users/upload/2026/08/20260824080452178752989264443.jpg&quot; alt=&quot;封面&quot; style=&quot;width:100%;&quot;&gt;&lt;/p&gt;

&lt;h2&gt;大模型推理加速：2025年工程师必须掌握的核心技能&lt;/h2&gt;
&lt;p&gt;随着GPT-4、Claude、Llama-3等大语言模型的广泛落地，推理成本与延迟成为制约商业化的核心瓶颈。训练一次、推理千万次——推理服务的吞吐量和响应速度直接影响产品体验与运营成本。&lt;/p&gt;
&lt;p&gt;本文将系统梳理目前最主流的推理加速技术方向，并结合vLLM、SGLang、TensorRT-LLM等开源框架的实现，给出可落地的工程实践建议。&lt;/p&gt;

&lt;h2&gt;PagedAttention与KV Cache管理&lt;/h2&gt;
&lt;p&gt;KV Cache是Transformer推理加速的基础机制，将历史token的K/V矩阵缓存起来避免重复计算。传统实现存在严重的显存碎片问题：为每个请求预分配固定长度的显存块，导致大量浪费。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PagedAttention&lt;/strong&gt;（vLLM提出）借鉴操作系统的虚拟内存分页思想，将KV Cache切分为固定大小的&quot;物理页&quot;，按需动态分配，显著降低显存碎片率。&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;物理页大小&lt;/strong&gt;：通常设置为16或32个token，可通过block-size参数调整&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;逻辑-物理映射&lt;/strong&gt;：维护页表，实现多请求间的KV Cache共享（如prefix caching）&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;显存利用率提升&lt;/strong&gt;：实测相比naive实现可提升2-4倍吞吐量&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SGLang在此基础上实现了&lt;strong&gt;RadixAttention&lt;/strong&gt;，通过Radix树结构管理KV Cache，对于有大量共同前缀的请求（如few-shot prompt）可实现更高效的缓存命中。&lt;/p&gt;

&lt;h2&gt;Continuous Batching连续批处理&lt;/h2&gt;
&lt;p&gt;传统静态批处理需要等待一批请求全部生成完毕才能开始下一批，GPU在等待慢请求时大量空转。&lt;strong&gt;Continuous Batching&lt;/strong&gt;（iteration-level scheduling）彻底改变了这一模式：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;每个推理步骤后，检查哪些序列已生成结束符EOS&lt;/li&gt;
  &lt;li&gt;立即将完成的序列替换为等待队列中的新请求&lt;/li&gt;
  &lt;li&gt;GPU始终保持高负载，吞吐量提升显著（实测3-5倍）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;关键参数调优建议：最大并发请求数根据显存容量设置，通常为GPU显存GB数乘以8到12；prefill阶段的最大token数影响首token延迟（TTFT）。&lt;/p&gt;

&lt;h2&gt;Speculative Decoding推测解码&lt;/h2&gt;
&lt;p&gt;大模型的autoregressive生成本质上是串行的——每次只能生成一个token。&lt;strong&gt;Speculative Decoding&lt;/strong&gt;通过引入&quot;草稿模型&quot;来突破这一限制：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;用一个小模型快速生成K个候选token&lt;/li&gt;
  &lt;li&gt;用大模型并行验证这K个token&lt;/li&gt;
  &lt;li&gt;接受验证通过的token，拒绝的token重新采样&lt;/li&gt;
  &lt;li&gt;平均每次大模型调用可接受2-4个token，相当于加速2-4倍&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;实测数据（A100 80GB）：Llama-3-70B启用Speculative Decoding后吞吐量从42 tokens/s提升至118 tokens/s，提升约2.8倍。推测解码在temperature较高时加速效果下降，最适合确定性生成场景。&lt;/p&gt;

&lt;h2&gt;量化推理Quantization方案对比&lt;/h2&gt;
&lt;p&gt;量化是降低推理成本最直接的手段，通过降低模型权重的数值精度来减少显存占用和计算量。主流方案对比：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;GPTQ（4-bit）&lt;/strong&gt;：离线量化，精度损失小，适合权重密集型任务，llama.cpp和AutoGPTQ均支持&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;AWQ（4-bit）&lt;/strong&gt;：关注激活值的量化感知，比GPTQ在困难任务上精度更优&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FP8（8-bit浮点）&lt;/strong&gt;：H100和H800原生支持，几乎无精度损失，推荐生产环境优先选择&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;GGUF/Q4_K_M&lt;/strong&gt;：llama.cpp格式，适合CPU推理或边缘设备部署&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Llama-3-8B-Instruct量化前后对比：FP16需要16GB显存，AWQ INT4仅需5.5GB显存，推理速度提升约1.8倍，MMLU基准分数下降不足0.5%。&lt;/p&gt;

&lt;h2&gt;模型并行策略选择&lt;/h2&gt;
&lt;p&gt;当单卡显存不足以容纳整个模型时，需要采用模型并行策略。两种主流方案各有侧重：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tensor Parallelism（张量并行）&lt;/strong&gt;：将每一层的权重矩阵按列/行切分到多卡，每张卡负责部分矩阵乘法，最后AllReduce汇总结果。延迟最低，但需要高带宽NVLink互联。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pipeline Parallelism（流水线并行）&lt;/strong&gt;：将模型的不同层分配到不同GPU，形成流水线。适合跨机器部署，但会引入流水线气泡导致效率损失。&lt;/p&gt;
&lt;p&gt;实际部署中，推荐优先使用张量并行（TP），在多机场景下组合使用张量并行加流水线并行（TP+PP）策略。&lt;/p&gt;

&lt;h2&gt;主流框架横向对比与选型建议&lt;/h2&gt;
&lt;p&gt;面对vLLM、SGLang、TensorRT-LLM、LMDeploy等众多框架，如何选型？以下是综合评估：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;vLLM&lt;/strong&gt;：生态最成熟，OpenAI兼容API，支持模型最广，推荐作为默认选择&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SGLang&lt;/strong&gt;：在长上下文、复杂prompt结构场景下吞吐量优于vLLM，适合RAG/Agent场景&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;TensorRT-LLM&lt;/strong&gt;：NVIDIA官方优化，在A100/H100上性能天花板最高，但部署复杂度高&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;LMDeploy&lt;/strong&gt;：上海AI Lab出品，对InternLM系列优化最好，支持量化部署便捷&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;llama.cpp&lt;/strong&gt;：CPU推理首选，适合本地开发和资源受限环境&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;选型原则：&lt;strong&gt;开发测试用vLLM，生产高性能选TensorRT-LLM，长上下文场景考虑SGLang，预算有限或边缘部署选llama.cpp&lt;/strong&gt;。&lt;/p&gt;

&lt;h2&gt;实战优化效果总结&lt;/h2&gt;
&lt;p&gt;综合应用以上技术，以Llama-3-8B加单张A10 24GB为例，实测结果如下：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;启用AWQ INT4量化：显存从16GB降至5.5GB，推理速度提升1.8倍&lt;/li&gt;
  &lt;li&gt;启用prefix caching：重复前缀请求的响应时间降低60%以上&lt;/li&gt;
  &lt;li&gt;启用Continuous Batching：吞吐量提升3到5倍&lt;/li&gt;
  &lt;li&gt;综合优化后：可稳定支持32并发，P99延迟控制在3秒内，吞吐量达到820 tokens/s&lt;/li&gt;
  &lt;li&gt;相比完全未优化版本：总体提升约6.2倍&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;大模型推理加速是一个系统工程，需要从模型量化、内存管理、调度策略、硬件选型等多个维度协同优化。希望本文的技术梳理和选型建议，能帮助工程师在实际项目中快速决策并落地高性能AI推理服务。&lt;/p&gt;
</description><pubDate>Mon, 24 Aug 2026 08:09:45 +0800</pubDate></item><item><title>PHP 8.4 新特性全解析：Property Hooks 与 Asymmetric Visibility 实战指南</title><link>https://blog.resmic.cn/post/363.html</link><description>&lt;p&gt;&lt;img src=&quot;https://blog.resmic.cn/zb_users/upload/2026/08/20260823080239178744335914463.jpg&quot; alt=&quot;封面&quot; style=&quot;width:100%;&quot;&gt;&lt;/p&gt;

&lt;h2&gt;PHP 8.4 带来了什么变化？&lt;/h2&gt;
&lt;p&gt;PHP 8.4 于 2024 年 11 月正式发布，这是 PHP 近年来最具突破性的版本之一。它引入了多项激动人心的语言特性，包括 Property Hooks（属性钩子）、Asymmetric Visibility（非对称可见性）、新的数组操作函数，以及对 HTML5 解析器的支持。这些特性不仅大幅提升了代码的表达力，也让开发者在编写面向对象代码时更加灵活和优雅。&lt;/p&gt;
&lt;p&gt;本文将深入解析 PHP 8.4 的核心新特性，并结合实际应用场景演示如何在项目中充分利用这些功能。&lt;/p&gt;

&lt;h2&gt;Property Hooks：告别冗余的 Getter/Setter&lt;/h2&gt;
&lt;p&gt;Property Hooks 是 PHP 8.4 最令人期待的特性之一。它允许开发者直接在属性定义上添加 &lt;code&gt;get&lt;/code&gt; 和 &lt;code&gt;set&lt;/code&gt; 钩子，彻底消除传统 Getter/Setter 方法的样板代码。&lt;/p&gt;
&lt;p&gt;传统写法需要定义私有属性并手动编写访问方法，而 Property Hooks 让这一切更加简洁：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
class User {
    // 带 hooks 的属性
    public string $fullName {
        get =&amp;gt; &quot;{$this-&amp;gt;firstName} {$this-&amp;gt;lastName}&quot;;
        set {
            $parts = explode(' ', $value, 2);
            $this-&amp;gt;firstName = $parts[0];
            $this-&amp;gt;lastName = $parts[1] ?? '';
        }
    }

    public string $email {
        set {
            if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
                throw new \InvalidArgumentException(&quot;无效的邮箱地址: $value&quot;);
            }
            $this-&amp;gt;email = strtolower($value);
        }
    }

    public function __construct(
        private string $firstName,
        private string $lastName,
    ) {}
}

$user = new User('张', '三');
echo $user-&amp;gt;fullName;      // 输出: 张 三
$user-&amp;gt;fullName = '李 四'; // 自动拆分到 firstName/lastName
$user-&amp;gt;email = 'Test@Example.COM'; // 自动转小写并验证
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Property Hooks 的优势在于：代码量减少约 60%，逻辑与属性定义紧密结合，IDE 可以更好地进行类型推断，并且接口中也可以声明带钩子的属性。&lt;/p&gt;

&lt;h2&gt;Asymmetric Visibility：精细化属性访问控制&lt;/h2&gt;
&lt;p&gt;非对称可见性允许对属性的读操作和写操作设置不同的访问级别，这在构建不可变值对象和领域模型时非常实用。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
class Order {
    // 公开读取，但只能在类内部修改
    public private(set) int $id;
    
    // 公开读取，子类可以修改
    public protected(set) string $status = 'pending';
    
    // 构造器提升同样支持非对称可见性
    public function __construct(
        public private(set) float $total,
        public private(set) \DateTimeImmutable $createdAt,
    ) {
        $this-&amp;gt;id = random_int(100000, 999999);
        $this-&amp;gt;createdAt = new \DateTimeImmutable();
    }
    
    public function confirm(): void {
        $this-&amp;gt;status = 'confirmed'; // 类内部可以修改
    }
}

$order = new Order(total: 299.99, createdAt: new \DateTimeImmutable());
echo $order-&amp;gt;total;   // 可以读取: 299.99
echo $order-&amp;gt;status;  // 可以读取: pending
// $order-&amp;gt;total = 100; // 错误！外部无法修改
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个特性让我们可以更精准地表达设计意图，无需通过复杂的接口或只读 DTO 来实现类似效果。&lt;/p&gt;

&lt;h2&gt;新增数组操作函数：array_find 系列&lt;/h2&gt;
&lt;p&gt;PHP 8.4 新增了四个实用的数组操作函数，弥补了长期以来 PHP 数组函数库的空缺：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;array_find()&lt;/strong&gt;：返回第一个满足回调条件的元素值&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;array_find_key()&lt;/strong&gt;：返回第一个满足条件的元素键名&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;array_any()&lt;/strong&gt;：检查是否有任意元素满足条件（相当于 JavaScript 的 Array.some）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;array_all()&lt;/strong&gt;：检查是否所有元素都满足条件（相当于 JavaScript 的 Array.every）&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
$users = [
    ['name' =&amp;gt; '张三', 'age' =&amp;gt; 25, 'active' =&amp;gt; true],
    ['name' =&amp;gt; '李四', 'age' =&amp;gt; 17, 'active' =&amp;gt; false],
    ['name' =&amp;gt; '王五', 'age' =&amp;gt; 30, 'active' =&amp;gt; true],
];

// 找到第一个活跃用户
$activeUser = array_find($users, fn($u) =&amp;gt; $u['active']);
// ['name' =&amp;gt; '张三', 'age' =&amp;gt; 25, 'active' =&amp;gt; true]

// 是否有未成年用户
$hasMinor = array_any($users, fn($u) =&amp;gt; $u['age'] &amp;lt; 18);
// true

// 是否所有用户都已激活
$allActive = array_all($users, fn($u) =&amp;gt; $u['active']);
// false

// 找到未成年用户的键
$minorKey = array_find_key($users, fn($u) =&amp;gt; $u['age'] &amp;lt; 18);
// 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有了这些函数，很多原本需要 &lt;code&gt;array_filter&lt;/code&gt; + &lt;code&gt;array_pop&lt;/code&gt; 组合才能实现的逻辑，现在一行代码即可完成。&lt;/p&gt;

&lt;h2&gt;新 HTML5 解析器（DOM\HTMLDocument）&lt;/h2&gt;
&lt;p&gt;PHP 8.4 引入了全新的符合 HTML5 标准的 DOM 解析器，解决了旧版 &lt;code&gt;DOMDocument&lt;/code&gt; 无法正确处理现代 HTML5 文档的痛点。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
// 旧方式：需要压制警告，且无法正确处理 HTML5
libxml_use_internal_errors(true);
$dom = new DOMDocument();
$dom-&amp;gt;loadHTML($html);

// PHP 8.4 新方式：标准的 HTML5 解析
$dom = \DOM\HTMLDocument::createFromString($html);
$dom = \DOM\HTMLDocument::createFromFile('/path/to/file.html');

// 使用新的选择器 API
$articles = $dom-&amp;gt;querySelectorAll('article.post');
$firstTitle = $dom-&amp;gt;querySelector('h1.title')-&amp;gt;textContent;

// 支持完整的 HTML5 实体和元素
foreach ($dom-&amp;gt;getElementsByTagName('figure') as $figure) {
    $caption = $figure-&amp;gt;querySelector('figcaption');
    echo $caption?-&amp;gt;textContent . &quot;\n&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;新解析器完全符合 WHATWG HTML5 规范，支持 &lt;code&gt;querySelector&lt;/code&gt; / &lt;code&gt;querySelectorAll&lt;/code&gt;，正确处理 HTML5 特有元素（如 &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt;、&lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt;、&lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; 等），是爬虫、内容提取等场景的理想选择。&lt;/p&gt;

&lt;h2&gt;其他实用改进&lt;/h2&gt;
&lt;p&gt;除了上述主要特性，PHP 8.4 还带来了许多值得关注的小改进：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;new 不需要括号即可链式调用&lt;/strong&gt;：&lt;code&gt;new MyClass()-&amp;gt;method()&lt;/code&gt; 现在直接有效，无需再写 &lt;code&gt;(new MyClass())-&amp;gt;method()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多字节字符串函数新增&lt;/strong&gt;：&lt;code&gt;mb_trim()&lt;/code&gt;、&lt;code&gt;mb_ltrim()&lt;/code&gt;、&lt;code&gt;mb_rtrim()&lt;/code&gt; 正确处理 Unicode 空白字符&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BCMath 对象 API&lt;/strong&gt;：支持运算符重载，&lt;code&gt;$a + $b&lt;/code&gt; 直接用于 &lt;code&gt;BcMath\Number&lt;/code&gt; 对象&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JIT 改进&lt;/strong&gt;：ARM64 架构下性能进一步提升，某些场景下比 PHP 8.3 快约 15%&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;废弃隐式可空类型&lt;/strong&gt;：&lt;code&gt;function foo(string $x = null)&lt;/code&gt; 需显式写成 &lt;code&gt;?string&lt;/code&gt;，否则触发 Deprecated 警告&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
// 链式调用无需额外括号
$result = new Request('GET', '/api/users')
    -&amp;gt;withHeader('Authorization', 'Bearer ' . $token)
    -&amp;gt;withHeader('Accept', 'application/json');

// mb_trim 正确处理全角空格等 Unicode 空白
$cleaned = mb_trim(&quot;　 Hello World 　&quot;); // &quot;Hello World&quot;

// BcMath 运算符重载
use BcMath\Number;
$a = new Number('10.5');
$b = new Number('3.2');
$sum = $a + $b;   // Number('13.7')
$prod = $a * $b;  // Number('33.60')
echo $sum;        // 13.7
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;升级建议与兼容性注意事项&lt;/h2&gt;
&lt;p&gt;准备升级到 PHP 8.4 时，需要注意以下几点兼容性问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;隐式可空类型已废弃&lt;/strong&gt;：全局检索 &lt;code&gt;= null&lt;/code&gt; 参数并补全 &lt;code&gt;?&lt;/code&gt; 前缀，可用 PHP CS Fixer 自动修复&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;部分 LDAP 函数被移除&lt;/strong&gt;：使用 &lt;code&gt;ldap_connect()&lt;/code&gt; 时需检查参数变化&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GMP 对象序列化变更&lt;/strong&gt;：如果代码中有 GMP 对象的序列化/反序列化，需要重新测试&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ext-dom 变更&lt;/strong&gt;：旧版 &lt;code&gt;DOMDocument&lt;/code&gt; 的部分行为调整，建议回归测试爬虫相关代码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;升级步骤建议：首先在开发环境运行 &lt;code&gt;php -d error_reporting=E_ALL your_app.php&lt;/code&gt; 检查 Deprecated 警告，然后借助 &lt;a href=&quot;https://github.com/rectorphp/rector&quot; target=&quot;_blank&quot;&gt;Rector&lt;/a&gt; 工具自动迁移代码，最后在 CI 流水线中增加 PHP 8.4 的并行测试矩阵。&lt;/p&gt;
&lt;p&gt;PHP 8.4 是一个值得积极拥抱的版本，Property Hooks 和非对称可见性这两大特性将从根本上改变我们编写 PHP 类的方式，建议提前在新项目中试用，感受现代 PHP 的魅力。&lt;/p&gt;
</description><pubDate>Sun, 23 Aug 2026 08:02:56 +0800</pubDate></item><item><title>PHP 8.4 新特性全解析：属性钩子、惰性对象与数组函数实战</title><link>https://blog.resmic.cn/post/362.html</link><description></description><pubDate>Sat, 22 Aug 2026 08:09:16 +0800</pubDate></item><item><title>Laravel Reverb 实战：PHP 原生 WebSocket 服务器从入门到生产部署</title><link>https://blog.resmic.cn/post/361.html</link><description>&lt;p&gt;&lt;img src=&quot;https://blog.resmic.cn/zb_users/upload/2026/08/20260821080253178727057319295.jpg&quot; alt=&quot;封面&quot; style=&quot;width:100%;&quot;/&gt;&lt;/p&gt;&lt;h2&gt;什么是 Laravel Reverb？&lt;/h2&gt;&lt;p&gt;Laravel Reverb 是 Laravel 官方在 2024 年推出的第一方 WebSocket 服务器，完全基于 PHP 构建，无需 Node.js 或任何外部服务即可实现实时通信功能。它与 Laravel Echo、Pusher 协议高度兼容，可无缝替换原有的第三方 WebSocket 服务。&lt;/p&gt;&lt;p&gt;在 Reverb 发布之前，Laravel 开发者想要实现实时功能通常需要借助 Pusher、Ably 等付费服务，或者自行部署 Soketi、Laravel WebSockets 等开源替代方案，配置复杂度较高。Reverb 的出现彻底改变了这一局面，让纯 PHP 技术栈也能轻松支撑大规模实时应用。&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;官方出品，与 Laravel 生态深度集成&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;基于 ReactPHP 构建，高性能异步 I/O&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;兼容 Pusher 协议，前端零改动迁移&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;内置水平扩展支持（Redis Pub/Sub）&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;开箱即用，一条命令即可启动&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;快速安装与配置&lt;/h2&gt;&lt;p&gt;Reverb 的安装极为简单，通过 Composer 引入包并运行官方安装命令即可完成全部初始化工作。安装命令会自动发布配置文件、生成 APP_ID/APP_KEY/APP_SECRET 环境变量，并配置好广播驱动。&lt;/p&gt;&lt;p&gt;安装完成后，只需在 &lt;code&gt;.env&lt;/code&gt; 文件中确认以下关键配置：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;BROADCAST_CONNECTION=reverb&lt;/code&gt;：指定广播驱动为 Reverb&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;REVERB_APP_ID&lt;/code&gt;、&lt;code&gt;REVERB_APP_KEY&lt;/code&gt;、&lt;code&gt;REVERB_APP_SECRET&lt;/code&gt;：应用标识与鉴权凭证&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;REVERB_HOST&lt;/code&gt;：Reverb 服务监听地址，本地开发填 &lt;code&gt;localhost&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;REVERB_PORT&lt;/code&gt;：监听端口，默认 8080&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;前端方面，只需将 Laravel Echo 的 broadcaster 从 &lt;code&gt;pusher&lt;/code&gt; 改为 &lt;code&gt;reverb&lt;/code&gt;，其余代码完全不变，迁移成本几乎为零。使用 &lt;code&gt;php artisan reverb:start&lt;/code&gt; 命令即可启动 WebSocket 服务器，支持 &lt;code&gt;--debug&lt;/code&gt; 参数查看实时连接日志。&lt;/p&gt;&lt;h2&gt;核心功能：广播事件与频道&lt;/h2&gt;&lt;p&gt;Reverb 完全复用 Laravel 的广播系统，支持以下三种频道类型：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;公共频道（Public Channels）&lt;/strong&gt;：任何人均可订阅，无需身份验证，适合公告、实时行情等场景&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;私有频道（Private Channels）&lt;/strong&gt;：需要登录验证才能订阅，适合用户专属通知、私信等&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;临场频道（Presence Channels）&lt;/strong&gt;：在私有频道基础上额外暴露在线用户列表，适合聊天室、协同编辑等需要感知&amp;quot;谁在线&amp;quot;的场景&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;广播事件只需实现 &lt;code&gt;ShouldBroadcast&lt;/code&gt; 接口，在 &lt;code&gt;broadcastOn()&lt;/code&gt; 方法中指定目标频道，在 &lt;code&gt;broadcastWith()&lt;/code&gt; 中定义推送的数据结构。事件触发后，Reverb 会自动将消息推送到所有已连接的订阅者，延迟通常在毫秒级别。&lt;/p&gt;&lt;p&gt;前端通过 Laravel Echo 监听：订阅频道后，用 &lt;code&gt;.listen(&amp;#39;.EventName&amp;#39;, callback)&lt;/code&gt; 即可接收服务端广播的实时数据，写法与 Pusher 完全相同。&lt;/p&gt;&lt;h2&gt;水平扩展：Redis Pub/Sub 集群方案&lt;/h2&gt;&lt;p&gt;单个 Reverb 进程可以处理数千个并发 WebSocket 连接，但对于更大规模的应用，需要运行多个 Reverb 实例并通过 Redis 进行消息同步。&lt;/p&gt;&lt;p&gt;水平扩展的配置非常简单：在 &lt;code&gt;config/reverb.php&lt;/code&gt; 中将 scaling 驱动设置为 &lt;code&gt;redis&lt;/code&gt;，并配置好 Redis 连接信息。此后，多个 Reverb 实例之间会通过 Redis Pub/Sub 自动同步消息，负载均衡器（如 Nginx）将 WebSocket 连接分发到各实例即可。&lt;/p&gt;&lt;p&gt;在生产环境中，推荐使用 Supervisor 管理 Reverb 进程，确保服务崩溃后自动重启。同时，建议在 Nginx 中为 WebSocket 连接单独配置反向代理，设置 &lt;code&gt;proxy_read_timeout&lt;/code&gt; 为较大值（如 3600s），避免长连接被误判为超时而断开。&lt;/p&gt;&lt;h2&gt;实战案例：实时消息通知系统&lt;/h2&gt;&lt;p&gt;以一个典型的实时消息通知场景为例，完整的实现链路如下：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;事件定义&lt;/strong&gt;：创建 &lt;code&gt;NewMessageReceived&lt;/code&gt; 事件，实现 &lt;code&gt;ShouldBroadcast&lt;/code&gt; 接口，在 &lt;code&gt;broadcastOn()&lt;/code&gt; 返回私有频道 &lt;code&gt;private-user.{userId}&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;触发广播&lt;/strong&gt;：在消息发送逻辑中调用 &lt;code&gt;broadcast(new NewMessageReceived($message))&lt;/code&gt;，可加 &lt;code&gt;.toOthers()&lt;/code&gt; 排除发送者自身&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;前端监听&lt;/strong&gt;：用户登录后，Echo 订阅 &lt;code&gt;private-user.{id}&lt;/code&gt; 频道，监听 &lt;code&gt;NewMessageReceived&lt;/code&gt; 事件，收到后更新 UI 显示通知红点&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;鉴权路由&lt;/strong&gt;：在 &lt;code&gt;routes/channels.php&lt;/code&gt; 中定义私有频道鉴权逻辑，确保用户只能订阅自己的频道&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;整个实现无需引入任何外部服务，代码量极少，与 Laravel 的 Queue、Notification 系统也可以无缝结合，实现消息既写入数据库又实时推送的双重效果。&lt;/p&gt;&lt;h2&gt;Reverb vs 传统方案对比&lt;/h2&gt;&lt;p&gt;选择 Reverb 的核心优势在于：&lt;strong&gt;零第三方依赖、与 Laravel 深度集成、运维成本极低&lt;/strong&gt;。与各方案的横向对比如下：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pusher / Ably（付费 SaaS）&lt;/strong&gt;：无需自托管，但有连接数/消息量限制，规模大时费用显著；Reverb 自托管无此限制&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Soketi（开源 Pusher 替代）&lt;/strong&gt;：基于 Node.js，需要额外维护 Node 服务；Reverb 纯 PHP，与 Laravel 进程统一管理&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Laravel WebSockets（旧官方包）&lt;/strong&gt;：已停止维护，性能不及 Reverb；官方建议迁移至 Reverb&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;原生 Swoole/Workerman&lt;/strong&gt;：性能极强，但配置复杂，需要深入了解异步编程模型；Reverb 开箱即用，学习曲线平缓&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;对于大多数 Laravel 项目而言，Reverb 是目前实现 WebSocket 实时通信的最优解，在简洁性和性能之间取得了极佳的平衡。&lt;/p&gt;&lt;h2&gt;生产部署建议与性能调优&lt;/h2&gt;&lt;p&gt;在将 Reverb 部署到生产环境时，有几个关键点值得关注：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;进程管理&lt;/strong&gt;：使用 Supervisor 配置 &lt;code&gt;php artisan reverb:start&lt;/code&gt; 命令，设置 &lt;code&gt;autostart=true&lt;/code&gt; 和 &lt;code&gt;autorestart=true&lt;/code&gt;，确保服务高可用&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;SSL/TLS 终止&lt;/strong&gt;：建议在 Nginx 层完成 SSL 终止，Reverb 内部走 HTTP，减少证书管理复杂度；配置 &lt;code&gt;wss://&lt;/code&gt; 时 Nginx 代理到 &lt;code&gt;ws://localhost:8080&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;连接数调优&lt;/strong&gt;：修改系统文件描述符限制（&lt;code&gt;ulimit -n&lt;/code&gt;）以支持更多并发连接；Reverb 配置中的 &lt;code&gt;max_connections&lt;/code&gt; 参数按实际内存调整&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;心跳配置&lt;/strong&gt;：合理设置 &lt;code&gt;timeout&lt;/code&gt; 和 &lt;code&gt;ping_interval&lt;/code&gt;，平衡连接活跃检测与服务器开销&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;监控接入&lt;/strong&gt;：利用 Laravel Telescope 或 Pulse 监控 WebSocket 事件流量，及时发现异常广播或连接泄漏&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Laravel Reverb 代表了 PHP 在实时通信领域的重大进步，让开发者无需跨越技术栈边界就能构建生产级别的 WebSocket 应用。随着 Laravel 11 将其列为官方推荐方案，掌握 Reverb 已经成为现代 PHP 全栈开发者的必备技能。&lt;/p&gt;</description><pubDate>Fri, 21 Aug 2026 08:08:37 +0800</pubDate></item><item><title>Jetpack Compose 动画系统全攻略：从基础到高性能实战</title><link>https://blog.resmic.cn/post/360.html</link><description>&lt;p&gt;&lt;img src=&quot;https://blog.resmic.cn/zb_users/upload/2026/08/20260820080431178718427112469.jpg&quot; alt=&quot;封面&quot; style=&quot;width:100%;&quot;/&gt;&lt;/p&gt;&lt;h2&gt;前言：为什么 Jetpack Compose 动画值得深入掌握&lt;/h2&gt;&lt;p&gt;随着 Jetpack Compose 在 Android 开发中逐渐成为主流 UI 框架，越来越多的开发者开始将项目迁移至 Compose。然而，很多人在迁移时会发现：传统 View 体系里的 Animator、ObjectAnimator 等动画 API 在 Compose 中已无法直接使用，取而代之的是一套全新的、基于状态驱动的动画系统。&lt;/p&gt;&lt;p&gt;Compose 动画系统不仅 API 简洁，更与 Compose 的声明式编程模型完美契合。但要真正用好它，需要理解其背后的原理和最佳实践。本文将从基础到进阶，系统性地介绍 Jetpack Compose 动画系统的核心概念与实战技巧，帮助你打造流畅、自然的用户体验。&lt;/p&gt;&lt;h2&gt;一、Compose 动画核心概念：状态驱动&lt;/h2&gt;&lt;p&gt;Compose 动画的本质是：&lt;strong&gt;当 UI 状态发生变化时，Compose 自动在新旧状态之间进行插值过渡&lt;/strong&gt;。这与命令式动画（&amp;quot;从 A 点移动到 B 点&amp;quot;）的思维方式截然不同。&lt;/p&gt;&lt;p&gt;理解这一点，是掌握 Compose 动画的关键。你只需要关心&amp;quot;当前状态是什么&amp;quot;，Compose 会帮你处理过渡过程。&lt;/p&gt;&lt;p&gt;Compose 提供了几类核心动画 API，按使用场景分类如下：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;animate*AsState&lt;/strong&gt;：最简单的动画，监听单个值的变化&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;updateTransition&lt;/strong&gt;：管理多个值同时变化的动画状态机&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;AnimatedVisibility&lt;/strong&gt;：内容显示/隐藏的过渡动画&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;AnimatedContent&lt;/strong&gt;：内容切换时的过渡动画&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Crossfade&lt;/strong&gt;：交叉淡入淡出切换&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;rememberInfiniteTransition&lt;/strong&gt;：无限循环动画&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;二、animate*AsState：最简单的起点&lt;/h2&gt;&lt;p&gt;&lt;code&gt;animate*AsState&lt;/code&gt; 系列 API 是 Compose 动画中最易上手的部分。只要目标值改变，它就会自动触发动画过渡。&lt;/p&gt;&lt;p&gt;常见的类型包括：&lt;code&gt;animateFloatAsState&lt;/code&gt;、&lt;code&gt;animateDpAsState&lt;/code&gt;、&lt;code&gt;animateColorAsState&lt;/code&gt;、&lt;code&gt;animateIntAsState&lt;/code&gt; 等。&lt;/p&gt;&lt;pre&gt;@Composable
fun&amp;nbsp;AnimatedBox()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;expanded&amp;nbsp;by&amp;nbsp;remember&amp;nbsp;{&amp;nbsp;mutableStateOf(false)&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;当&amp;nbsp;expanded&amp;nbsp;状态变化时，size&amp;nbsp;自动动画过渡
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;size&amp;nbsp;by&amp;nbsp;animateDpAsState(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;targetValue&amp;nbsp;=&amp;nbsp;if&amp;nbsp;(expanded)&amp;nbsp;200.dp&amp;nbsp;else&amp;nbsp;100.dp,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;animationSpec&amp;nbsp;=&amp;nbsp;spring(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dampingRatio&amp;nbsp;=&amp;nbsp;Spring.DampingRatioMediumBouncy,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stiffness&amp;nbsp;=&amp;nbsp;Spring.StiffnessLow
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;label&amp;nbsp;=&amp;nbsp;&amp;quot;box_size&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;color&amp;nbsp;by&amp;nbsp;animateColorAsState(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;targetValue&amp;nbsp;=&amp;nbsp;if&amp;nbsp;(expanded)&amp;nbsp;Color(0xFF6200EE)&amp;nbsp;else&amp;nbsp;Color(0xFF03DAC5),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;animationSpec&amp;nbsp;=&amp;nbsp;tween(durationMillis&amp;nbsp;=&amp;nbsp;400),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;label&amp;nbsp;=&amp;nbsp;&amp;quot;box_color&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Box(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;modifier&amp;nbsp;=&amp;nbsp;Modifier
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.size(size)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.background(color,&amp;nbsp;RoundedCornerShape(12.dp))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.clickable&amp;nbsp;{&amp;nbsp;expanded&amp;nbsp;=&amp;nbsp;!expanded&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
}&lt;/pre&gt;&lt;p&gt;注意：从 Compose 1.5 起，建议给每个动画加上 &lt;code&gt;label&lt;/code&gt; 参数，方便在 Android Studio 动画检查器中识别。&lt;/p&gt;&lt;h2&gt;三、updateTransition：协调多状态动画&lt;/h2&gt;&lt;p&gt;当一次状态切换需要同时驱动多个属性的动画时，&lt;code&gt;updateTransition&lt;/code&gt; 是更合适的选择。它可以确保所有属性动画同步启动，并支持为不同状态间的过渡单独设置动画规格。&lt;/p&gt;&lt;pre&gt;enum&amp;nbsp;class&amp;nbsp;BoxState&amp;nbsp;{&amp;nbsp;Small,&amp;nbsp;Large&amp;nbsp;}

@Composable
fun&amp;nbsp;TransitionDemo()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;currentState&amp;nbsp;by&amp;nbsp;remember&amp;nbsp;{&amp;nbsp;mutableStateOf(BoxState.Small)&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;transition&amp;nbsp;=&amp;nbsp;updateTransition(currentState,&amp;nbsp;label&amp;nbsp;=&amp;nbsp;&amp;quot;box_transition&amp;quot;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;size&amp;nbsp;by&amp;nbsp;transition.animateDp(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;transitionSpec&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;when&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;BoxState.Small&amp;nbsp;isTransitioningTo&amp;nbsp;BoxState.Large&amp;nbsp;-&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;spring(stiffness&amp;nbsp;=&amp;nbsp;Spring.StiffnessLow)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&amp;nbsp;-&amp;gt;&amp;nbsp;tween(durationMillis&amp;nbsp;=&amp;nbsp;300)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;},
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;label&amp;nbsp;=&amp;nbsp;&amp;quot;size&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)&amp;nbsp;{&amp;nbsp;state&amp;nbsp;-&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;when&amp;nbsp;(state)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;BoxState.Small&amp;nbsp;-&amp;gt;&amp;nbsp;80.dp
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;BoxState.Large&amp;nbsp;-&amp;gt;&amp;nbsp;200.dp
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;borderWidth&amp;nbsp;by&amp;nbsp;transition.animateDp(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;label&amp;nbsp;=&amp;nbsp;&amp;quot;border_width&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)&amp;nbsp;{&amp;nbsp;state&amp;nbsp;-&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;when&amp;nbsp;(state)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;BoxState.Small&amp;nbsp;-&amp;gt;&amp;nbsp;1.dp
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;BoxState.Large&amp;nbsp;-&amp;gt;&amp;nbsp;4.dp
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Box(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;modifier&amp;nbsp;=&amp;nbsp;Modifier
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.size(size)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.border(borderWidth,&amp;nbsp;Color.Blue,&amp;nbsp;RoundedCornerShape(8.dp))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.clickable&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;currentState&amp;nbsp;=&amp;nbsp;if&amp;nbsp;(currentState&amp;nbsp;==&amp;nbsp;BoxState.Small)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;BoxState.Large&amp;nbsp;else&amp;nbsp;BoxState.Small
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
}&lt;/pre&gt;&lt;p&gt;&lt;code&gt;updateTransition&lt;/code&gt; 的另一个优势是：它是可组合的。你可以将子 Composable 中的动画也附加到同一个 Transition 上，实现跨组件的协调动画。&lt;/p&gt;&lt;h2&gt;四、AnimatedVisibility 与 AnimatedContent 深度解析&lt;/h2&gt;&lt;p&gt;&lt;code&gt;AnimatedVisibility&lt;/code&gt; 用于处理内容的显示与隐藏，支持自定义进入和退出动画：&lt;/p&gt;&lt;pre&gt;@Composable
fun&amp;nbsp;VisibilityDemo()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;visible&amp;nbsp;by&amp;nbsp;remember&amp;nbsp;{&amp;nbsp;mutableStateOf(true)&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Column&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Button(onClick&amp;nbsp;=&amp;nbsp;{&amp;nbsp;visible&amp;nbsp;=&amp;nbsp;!visible&amp;nbsp;})&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Text(if&amp;nbsp;(visible)&amp;nbsp;&amp;quot;隐藏&amp;quot;&amp;nbsp;else&amp;nbsp;&amp;quot;显示&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AnimatedVisibility(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;visible&amp;nbsp;=&amp;nbsp;visible,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;enter&amp;nbsp;=&amp;nbsp;slideInVertically(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;initialOffsetY&amp;nbsp;=&amp;nbsp;{&amp;nbsp;-it&amp;nbsp;},
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;animationSpec&amp;nbsp;=&amp;nbsp;tween(300)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)&amp;nbsp;+&amp;nbsp;fadeIn(animationSpec&amp;nbsp;=&amp;nbsp;tween(300)),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;exit&amp;nbsp;=&amp;nbsp;slideOutVertically(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;targetOffsetY&amp;nbsp;=&amp;nbsp;{&amp;nbsp;-it&amp;nbsp;},
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;animationSpec&amp;nbsp;=&amp;nbsp;tween(300)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)&amp;nbsp;+&amp;nbsp;fadeOut(animationSpec&amp;nbsp;=&amp;nbsp;tween(300))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Card(modifier&amp;nbsp;=&amp;nbsp;Modifier.fillMaxWidth().padding(8.dp))&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Text(&amp;quot;这是一段可以显示/隐藏的内容&amp;quot;,&amp;nbsp;modifier&amp;nbsp;=&amp;nbsp;Modifier.padding(16.dp))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;&lt;p&gt;&lt;code&gt;AnimatedContent&lt;/code&gt; 则用于内容切换场景，比如 Tab 切换、数字变化等：&lt;/p&gt;&lt;pre&gt;@Composable
fun&amp;nbsp;CounterDemo()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;count&amp;nbsp;by&amp;nbsp;remember&amp;nbsp;{&amp;nbsp;mutableIntStateOf(0)&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Column(horizontalAlignment&amp;nbsp;=&amp;nbsp;Alignment.CenterHorizontally)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AnimatedContent(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;targetState&amp;nbsp;=&amp;nbsp;count,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;transitionSpec&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(targetState&amp;nbsp;&amp;gt;&amp;nbsp;initialState)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;slideInVertically&amp;nbsp;{&amp;nbsp;-it&amp;nbsp;}&amp;nbsp;+&amp;nbsp;fadeIn()&amp;nbsp;togetherWith
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;slideOutVertically&amp;nbsp;{&amp;nbsp;it&amp;nbsp;}&amp;nbsp;+&amp;nbsp;fadeOut()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;else&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;slideInVertically&amp;nbsp;{&amp;nbsp;it&amp;nbsp;}&amp;nbsp;+&amp;nbsp;fadeIn()&amp;nbsp;togetherWith
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;slideOutVertically&amp;nbsp;{&amp;nbsp;-it&amp;nbsp;}&amp;nbsp;+&amp;nbsp;fadeOut()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}.using(SizeTransform(clip&amp;nbsp;=&amp;nbsp;false))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;},
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;label&amp;nbsp;=&amp;nbsp;&amp;quot;counter&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)&amp;nbsp;{&amp;nbsp;targetCount&amp;nbsp;-&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Text(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;text&amp;nbsp;=&amp;nbsp;&amp;quot;$targetCount&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;style&amp;nbsp;=&amp;nbsp;MaterialTheme.typography.headlineLarge
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Row&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Button(onClick&amp;nbsp;=&amp;nbsp;{&amp;nbsp;count--&amp;nbsp;})&amp;nbsp;{&amp;nbsp;Text(&amp;quot;-&amp;quot;)&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Spacer(modifier&amp;nbsp;=&amp;nbsp;Modifier.width(16.dp))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Button(onClick&amp;nbsp;=&amp;nbsp;{&amp;nbsp;count++&amp;nbsp;})&amp;nbsp;{&amp;nbsp;Text(&amp;quot;+&amp;quot;)&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;&lt;h2&gt;五、动画规格（AnimationSpec）详解&lt;/h2&gt;&lt;p&gt;AnimationSpec 决定了动画的运动曲线，是调优动画质感的关键。Compose 提供了多种内置规格：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;spring&lt;/strong&gt;：弹性动画，模拟物理弹簧，无需指定时长，参数包括 dampingRatio（阻尼比）和 stiffness（刚性）&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;tween&lt;/strong&gt;：时间插值动画，可指定时长和 Easing 曲线&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;keyframes&lt;/strong&gt;：关键帧动画，可在指定时刻设置特定值&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;repeatable / infiniteRepeatable&lt;/strong&gt;：循环动画&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;snap&lt;/strong&gt;：无动画，立即跳变&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre&gt;//&amp;nbsp;spring&amp;nbsp;示例：有弹性的按压效果
val&amp;nbsp;scale&amp;nbsp;by&amp;nbsp;animateFloatAsState(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;targetValue&amp;nbsp;=&amp;nbsp;if&amp;nbsp;(pressed)&amp;nbsp;0.92f&amp;nbsp;else&amp;nbsp;1f,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;animationSpec&amp;nbsp;=&amp;nbsp;spring(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dampingRatio&amp;nbsp;=&amp;nbsp;Spring.DampingRatioMediumBouncy,&amp;nbsp;&amp;nbsp;//&amp;nbsp;阻尼：0.5（中等弹性）
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stiffness&amp;nbsp;=&amp;nbsp;Spring.StiffnessMedium&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;刚性：中等
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;label&amp;nbsp;=&amp;nbsp;&amp;quot;press_scale&amp;quot;
)

//&amp;nbsp;keyframes&amp;nbsp;示例：抖动效果
val&amp;nbsp;offsetX&amp;nbsp;by&amp;nbsp;animateFloatAsState(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;targetValue&amp;nbsp;=&amp;nbsp;if&amp;nbsp;(error)&amp;nbsp;0f&amp;nbsp;else&amp;nbsp;0f,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;animationSpec&amp;nbsp;=&amp;nbsp;keyframes&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;durationMillis&amp;nbsp;=&amp;nbsp;500
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0f&amp;nbsp;at&amp;nbsp;0
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-20f&amp;nbsp;at&amp;nbsp;100
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;20f&amp;nbsp;at&amp;nbsp;200
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-10f&amp;nbsp;at&amp;nbsp;300
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;10f&amp;nbsp;at&amp;nbsp;400
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0f&amp;nbsp;at&amp;nbsp;500
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;},
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;label&amp;nbsp;=&amp;nbsp;&amp;quot;shake&amp;quot;
)&lt;/pre&gt;&lt;p&gt;经验之谈：&lt;strong&gt;优先使用 spring 动画&lt;/strong&gt;。Spring 动画基于物理模拟，在不同设备帧率下表现更自然一致；而 tween 动画在低帧率设备上可能显得不够流畅。&lt;/p&gt;&lt;h2&gt;六、rememberInfiniteTransition：循环动画实战&lt;/h2&gt;&lt;p&gt;加载指示器、呼吸灯效果、骨架屏闪烁……这些场景都需要无限循环动画。&lt;code&gt;rememberInfiniteTransition&lt;/code&gt; 专为此设计：&lt;/p&gt;&lt;pre&gt;@Composable
fun&amp;nbsp;ShimmerEffect()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;infiniteTransition&amp;nbsp;=&amp;nbsp;rememberInfiniteTransition(label&amp;nbsp;=&amp;nbsp;&amp;quot;shimmer&amp;quot;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;shimmerTranslate&amp;nbsp;by&amp;nbsp;infiniteTransition.animateFloat(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;initialValue&amp;nbsp;=&amp;nbsp;0f,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;targetValue&amp;nbsp;=&amp;nbsp;1000f,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;animationSpec&amp;nbsp;=&amp;nbsp;infiniteRepeatable(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;animation&amp;nbsp;=&amp;nbsp;tween(1200,&amp;nbsp;easing&amp;nbsp;=&amp;nbsp;LinearEasing),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;repeatMode&amp;nbsp;=&amp;nbsp;RepeatMode.Restart
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;label&amp;nbsp;=&amp;nbsp;&amp;quot;shimmer_translate&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;shimmerBrush&amp;nbsp;=&amp;nbsp;Brush.linearGradient(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;colors&amp;nbsp;=&amp;nbsp;listOf(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Color.LightGray.copy(alpha&amp;nbsp;=&amp;nbsp;0.6f),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Color.LightGray.copy(alpha&amp;nbsp;=&amp;nbsp;0.2f),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Color.LightGray.copy(alpha&amp;nbsp;=&amp;nbsp;0.6f)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;start&amp;nbsp;=&amp;nbsp;Offset(shimmerTranslate&amp;nbsp;-&amp;nbsp;500f,&amp;nbsp;0f),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;end&amp;nbsp;=&amp;nbsp;Offset(shimmerTranslate,&amp;nbsp;0f)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Column(modifier&amp;nbsp;=&amp;nbsp;Modifier.padding(16.dp))&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;repeat(5)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Spacer(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;modifier&amp;nbsp;=&amp;nbsp;Modifier
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.fillMaxWidth()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.height(20.dp)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.padding(vertical&amp;nbsp;=&amp;nbsp;4.dp)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.background(shimmerBrush,&amp;nbsp;RoundedCornerShape(4.dp))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;&lt;h2&gt;七、性能优化：让动画不掉帧&lt;/h2&gt;&lt;p&gt;动画性能优化是 Compose 进阶不可绕过的话题。以下是几个关键原则：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;使用 Modifier.graphicsLayer{}&lt;/strong&gt;：平移、旋转、缩放、透明度等变换操作，优先用 graphicsLayer 而非直接修改 layout 属性，因为 graphicsLayer 只触发 Draw 阶段而不触发 Layout&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;避免在动画中触发重组&lt;/strong&gt;：动画值的读取应尽量推迟到 Modifier 的 lambda 内部，减少上层 Composable 的重组&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;使用 derivedStateOf&lt;/strong&gt;：当动画值需要参与复杂计算时，用 derivedStateOf 缓存计算结果&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;开启 Android Studio 动画检查器&lt;/strong&gt;：在 Layout Inspector 中可以实时预览和调试动画，大幅提升调试效率&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;pre&gt;//&amp;nbsp;✅&amp;nbsp;推荐：用&amp;nbsp;graphicsLayer&amp;nbsp;做变换，只触发&amp;nbsp;Draw
Box(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;modifier&amp;nbsp;=&amp;nbsp;Modifier
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.graphicsLayer&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;scaleX&amp;nbsp;=&amp;nbsp;scale
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;scaleY&amp;nbsp;=&amp;nbsp;scale
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alpha&amp;nbsp;=&amp;nbsp;alpha
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
)

//&amp;nbsp;❌&amp;nbsp;避免：直接用&amp;nbsp;size/offset&amp;nbsp;做变换，会触发&amp;nbsp;Layout+Draw
Box(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;modifier&amp;nbsp;=&amp;nbsp;Modifier
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.size(size)&amp;nbsp;&amp;nbsp;//&amp;nbsp;触发&amp;nbsp;Layout
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;.offset(offsetX,&amp;nbsp;offsetY)&amp;nbsp;&amp;nbsp;//&amp;nbsp;触发&amp;nbsp;Layout
)&lt;/pre&gt;&lt;h2&gt;八、实战案例：底部导航栏动画&lt;/h2&gt;&lt;p&gt;综合运用上述知识，实现一个带动画的底部导航栏：&lt;/p&gt;&lt;pre&gt;@Composable
fun&amp;nbsp;AnimatedBottomNav()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;selectedIndex&amp;nbsp;by&amp;nbsp;remember&amp;nbsp;{&amp;nbsp;mutableIntStateOf(0)&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;items&amp;nbsp;=&amp;nbsp;listOf(&amp;quot;首页&amp;quot;&amp;nbsp;to&amp;nbsp;Icons.Default.Home,&amp;nbsp;&amp;quot;搜索&amp;quot;&amp;nbsp;to&amp;nbsp;Icons.Default.Search,&amp;nbsp;&amp;quot;我的&amp;quot;&amp;nbsp;to&amp;nbsp;Icons.Default.Person)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NavigationBar&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;items.forEachIndexed&amp;nbsp;{&amp;nbsp;index,&amp;nbsp;(label,&amp;nbsp;icon)&amp;nbsp;-&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;selected&amp;nbsp;=&amp;nbsp;index&amp;nbsp;==&amp;nbsp;selectedIndex
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;transition&amp;nbsp;=&amp;nbsp;updateTransition(selected,&amp;nbsp;label&amp;nbsp;=&amp;nbsp;&amp;quot;nav_$index&amp;quot;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;iconScale&amp;nbsp;by&amp;nbsp;transition.animateFloat(label&amp;nbsp;=&amp;nbsp;&amp;quot;icon_scale&amp;quot;)&amp;nbsp;{&amp;nbsp;if&amp;nbsp;(it)&amp;nbsp;1.2f&amp;nbsp;else&amp;nbsp;1f&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;labelAlpha&amp;nbsp;by&amp;nbsp;transition.animateFloat(label&amp;nbsp;=&amp;nbsp;&amp;quot;label_alpha&amp;quot;)&amp;nbsp;{&amp;nbsp;if&amp;nbsp;(it)&amp;nbsp;1f&amp;nbsp;else&amp;nbsp;0.6f&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NavigationBarItem(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;selected&amp;nbsp;=&amp;nbsp;selected,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;onClick&amp;nbsp;=&amp;nbsp;{&amp;nbsp;selectedIndex&amp;nbsp;=&amp;nbsp;index&amp;nbsp;},
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;icon&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Icon(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;icon,&amp;nbsp;contentDescription&amp;nbsp;=&amp;nbsp;label,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;modifier&amp;nbsp;=&amp;nbsp;Modifier.graphicsLayer&amp;nbsp;{&amp;nbsp;scaleX&amp;nbsp;=&amp;nbsp;iconScale;&amp;nbsp;scaleY&amp;nbsp;=&amp;nbsp;iconScale&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;},
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;label&amp;nbsp;=&amp;nbsp;{&amp;nbsp;Text(label,&amp;nbsp;modifier&amp;nbsp;=&amp;nbsp;Modifier.graphicsLayer&amp;nbsp;{&amp;nbsp;alpha&amp;nbsp;=&amp;nbsp;labelAlpha&amp;nbsp;})&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;&lt;h2&gt;总结&lt;/h2&gt;&lt;p&gt;Jetpack Compose 的动画系统设计哲学是：&lt;strong&gt;简单场景零门槛，复杂场景有路径&lt;/strong&gt;。从最简单的 &lt;code&gt;animateFloatAsState&lt;/code&gt;，到协调多状态的 &lt;code&gt;updateTransition&lt;/code&gt;，再到完全自定义的 &lt;code&gt;Animatable&lt;/code&gt;，每种 API 都有其最适合的使用场景。&lt;/p&gt;&lt;p&gt;掌握 Compose 动画的关键，不在于记住所有 API，而在于理解&amp;quot;状态驱动动画&amp;quot;的核心思想，以及如何在性能和效果之间做出合理的权衡。希望本文能帮助你在项目中打造出丝滑流畅的 Android 用户体验。&lt;/p&gt;</description><pubDate>Thu, 20 Aug 2026 08:04:56 +0800</pubDate></item><item><title>LLM推理加速全攻略：2026年主流技术与框架选型实战</title><link>https://blog.resmic.cn/post/359.html</link><description>&lt;p&gt;&lt;img src=&quot;https://blog.resmic.cn/zb_users/upload/2026/08/20260819080349178709782918074.jpg&quot; alt=&quot;封面&quot; style=&quot;width:100%;&quot;&gt;&lt;/p&gt;

&lt;h2&gt;为什么大模型推理加速是 2026 年的核心议题？&lt;/h2&gt;
&lt;p&gt;随着 GPT-4、Claude、Llama 3 等大模型在生产环境大规模落地，推理成本已成为 AI 业务的核心开销。一个百亿参数模型的单次推理往往需要数百毫秒，在高并发场景下 GPU 利用率低、吞吐量不足的问题尤为突出。2026 年，随着模型体积持续增大，推理加速不再是锦上添花，而是 AI 产品能否盈利的关键所在。&lt;/p&gt;

&lt;h2&gt;量化压缩：用精度换速度&lt;/h2&gt;
&lt;p&gt;量化是最普遍的推理加速手段，核心思路是将模型权重从 FP32/FP16 压缩到 INT8、INT4 甚至 INT2，显著减少显存占用与计算量。主流方案包括 GPTQ（逐层量化，精度损失小，适合 4-bit 推理）、AWQ（根据激活值分布保留关键权重，4-bit 下效果优于 GPTQ）、GGUF（llama.cpp 格式，支持 CPU 推理，适合本地部署）以及 FP8 量化（NVIDIA H100 原生支持，几乎无精度损失，吞吐量提升约 1.5 至 2 倍）。&lt;/p&gt;
&lt;p&gt;实际工程中，AWQ 4-bit 是在线服务的首选方案，可在精度损失极小的前提下将模型显存占用压缩 75%，使单卡 A100 能部署 70B 参数模型。&lt;/p&gt;

&lt;h2&gt;KV Cache 优化：解决显存瓶颈&lt;/h2&gt;
&lt;p&gt;KV Cache 是 Transformer 推理中存储历史注意力键值对的缓存结构，长上下文场景下会占用大量显存，成为推理效率的主要瓶颈之一。&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;PagedAttention（vLLM）&lt;/strong&gt;：借鉴操作系统虚拟内存分页思想，将 KV Cache 分块管理，显存利用率提升 3 至 5 倍，是 vLLM 的核心技术&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Sliding Window Attention&lt;/strong&gt;：限制每个 token 的注意力范围，将 KV Cache 从 O(n) 降至 O(window)，Mistral 系列模型广泛采用&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Grouped-Query Attention（GQA）&lt;/strong&gt;：减少 KV head 数量，降低 KV Cache 体积，Llama 3 全系列使用&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;KV Cache 量化&lt;/strong&gt;：对 KV Cache 本身进行 INT8 或 FP8 量化，可进一步节省约 50% 显存&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;投机采样：小模型辅助大模型&lt;/h2&gt;
&lt;p&gt;投机采样（Speculative Decoding）是近两年兴起的加速技术，核心思想是用小模型（Draft Model）快速生成候选 token，再由大模型（Target Model）批量验证，接受率高时可将生成速度提升 2 至 3 倍。&lt;/p&gt;
&lt;p&gt;主要变体包括：标准投机采样（Draft Model 生成若干候选，Target Model 并行验证）、Medusa（在主模型上添加多个预测头，无需独立 Draft Model）以及 EAGLE（利用特征层预测，接受率比标准方案高 20% 以上）。vLLM 0.5 以上版本和 TGI 均已内置支持。&lt;/p&gt;

&lt;h2&gt;主流推理框架横向对比&lt;/h2&gt;
&lt;p&gt;选择合适的推理框架对生产落地至关重要。以下是 2026 年主流框架的核心特性对比：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;vLLM&lt;/strong&gt;：PagedAttention 加连续批处理，吞吐量业界领先，Python 生态友好，适合在线服务&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;TensorRT-LLM（NVIDIA）&lt;/strong&gt;：深度优化 CUDA kernel，A100 和 H100 上延迟最低，适合 NVIDIA 独占部署&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SGLang&lt;/strong&gt;：RadixAttention 前缀缓存命中率高，适合长系统提示加短用户输入的 RAG 场景&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Ollama&lt;/strong&gt;：一键本地部署，底层 llama.cpp，适合开发调试和资源受限环境&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;MLC-LLM&lt;/strong&gt;：支持 Apple Silicon、Android 和 WebGPU，端侧部署首选&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实测数据（Llama-3.1-8B，A100 80GB，batch=32）：vLLM 0.6 约 3200 tokens/s，TensorRT-LLM 约 4100 tokens/s，SGLang（前缀命中率 80%）约 3800 tokens/s。&lt;/p&gt;

&lt;h2&gt;工程落地建议&lt;/h2&gt;
&lt;p&gt;综合以上技术，给出不同场景下的推理加速方案推荐：在线服务（低延迟优先）推荐 TensorRT-LLM 加 FP8 量化加投机采样，延迟可控制在 200ms 以内；高并发服务（吞吐优先）推荐 vLLM 加 AWQ 4-bit 加 PagedAttention，成本降低 60% 以上；RAG 和 Agent 场景推荐 SGLang 加 Prefix Caching，系统提示复用率高时效果显著；本地部署和边缘计算推荐 llama.cpp 加 Ollama 加 GGUF Q4_K_M，MacBook M3 可流畅运行 8B 模型。&lt;/p&gt;
&lt;p&gt;无论选择哪种方案，建议先用专业基准测试工具对目标场景做充分测试，再针对性优化，避免过度工程化带来的维护负担。&lt;/p&gt;</description><pubDate>Wed, 19 Aug 2026 08:13:38 +0800</pubDate></item><item><title>Test</title><link>https://blog.resmic.cn/post/358.html</link><description>TestBody</description><pubDate>Wed, 19 Aug 2026 08:11:16 +0800</pubDate></item></channel></rss>