<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[技术小黑屋]]></title>
  <link href="https://droidyue.com/atom.xml" rel="self"/>
  <link href="https://droidyue.com/"/>
  <updated>2026-01-04T19:51:14+08:00</updated>
  <id>https://droidyue.com/</id>
  <author>
    <name><![CDATA[androidyue]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Vibe Coding 的安全风险与应对策略]]></title>
    <link href="https://droidyue.com/blog/2025/11/02/vibe-coding-de-an-quan-feng-xian-yu-ying-dui-ce-lue/"/>
    <updated>2025-11-02T14:30:00+08:00</updated>
    <id>https://droidyue.com/blog/2025/11/02/vibe-coding-de-an-quan-feng-xian-yu-ying-dui-ce-lue</id>
    <content type="html"><![CDATA[<p>AI 驱动的代码生成正在改变软件开发方式，但快速开发背后隐藏着机器规模的安全风险。</p>

<!--more-->

<h2 id="vibe-coding-的兴起">Vibe Coding 的兴起</h2>

<p>Vibe Coding 是 OpenAI 联合创始人 Andrej Karpathy 在今年早些时候创造的术语，描述了一种全新的开发模式：开发者不再逐行编写代码，而是通过提示词指导 AI 生成代码，自己退居到更具战略性的”导演”角色。</p>

<p>这种方式比主流的 AI 辅助编程更进一步。GitHub Copilot 和 Claude 等工具让开发者能够以”爆发式”的速度编写代码，AI 负责处理细节。对企业而言，吸引力显而易见：开发者可以快速实验想法，团队生产力可能大幅提升。</p>

<p>Checkmarx 的全球研究显示，50% 的受访者使用某种形式的 AI 编码助手，三分之一的组织通过这种方式生成多达 60% 的代码。AI 的价值主张太强，无法忽视。</p>

<hr />

<h2 id="当-vibe-出错时">当 Vibe 出错时</h2>

<h3 id="llm-的概率特性">LLM 的概率特性</h3>

<p>任何使用过大语言模型的人都知道，它们的概率特性就像老虎机——用同一个提示词拉五次杠杆，会得到五个不同的结果。可能中大奖，可能什么都没有，也可能得到介于两者之间、勉强可用的东西。</p>

<h3 id="常见问题">常见问题</h3>

<p>将这种特性应用到编码中，各种问题都会出现：</p>

<ul>
  <li><strong>幻觉 API</strong>：AI 工具可能生成不存在的 API</li>
  <li><strong>过时依赖</strong>：插入已弃用的库</li>
  <li><strong>脆弱代码</strong>：生成脆弱或不透明的代码，悄悄侵蚀可维护性</li>
  <li><strong>架构破坏</strong>：AI 代理可能通过非授权接口粘合组件，绕过模块化设计</li>
  <li><strong>意外删除</strong>：关键代码可能因意外删除和其他错误而丢失</li>
</ul>

<p>一个在网上流传的警示故事中，一个失控的 AI 代理在代码冻结期间删除了整个数据库。</p>

<h3 id="供应链风险">供应链风险</h3>

<p>风险还延伸到供应链：AI 工具可能引入未经审查的依赖项、包含已知漏洞的包，甚至完全幻觉它们的存在。</p>

<p>如果这些问题得不到解决，组织面临将更多低质量代码引入环境的风险，包括完整的架构和运营威胁。更令人担忧的是，研究发现只有 18% 的组织目前制定了 AI 使用政策。</p>

<h3 id="循环中的谎言litl">“循环中的谎言”（LITL）</h3>

<p>即使开发者有人工干预（HITL）步骤来验证 AI 创建的代码，Checkmarx 研究团队最近发现了一种被称为”循环中的谎言”（lies in the loop, LITL）的技术，可以欺骗 Claude Code 等 AI 编码助手执行更危险的活动，同时在人工监督下显得安全。</p>

<hr />

<h2 id="风险预防从架构和思维开始">风险预防从架构和思维开始</h2>

<h3 id="模块化架构">模块化架构</h3>

<p>缓解 AI 生成代码的风险要在漏洞引入之前就开始。模块化架构至关重要：当有明确定义的边界和授权 API 时，一个幻觉函数或失控依赖的潜在破坏范围会自然受到限制。</p>

<p>这些原则并不新鲜，但在 AI 代理可能不尊重抽象的环境中变得更加紧迫，除非明确引导。</p>

<h3 id="开发者思维转变">开发者思维转变</h3>

<p>开发者思维同样重要。Vibe Coding 将开发者定位为系统架构师，而不是逐行作者——编写提示词、审查输出、决定发布什么。</p>

<p>这种转变需要技能提升。研究发现，理解架构和提示策略的开发者比那些将传统工作流应用于生成工具的开发者有效得多。</p>

<h3 id="当前状态">当前状态</h3>

<p>在目前状态下，Vibe 方法更适合实验和原型设计，而不是将完成的代码投入生产环境。组织应该以此为出发点进行探索。至关重要的是，一切都要经过同样严格的安全流程，使用左移思维尽早引入这些检查。</p>

<p>预防现在取决于智能设计和一个知道如何与 AI 协作（而不仅仅是围绕它工作）的团队。</p>

<hr />

<h2 id="快速反馈循环与-devsecops">快速反馈循环与 DevSecOps</h2>

<h3 id="实时检测与修正">实时检测与修正</h3>

<p>在 AI 驱动的开发中，错误可能快速引入，因此必须同样快速地发现和修复。依赖传统检查点是不够的。检测和修正需要嵌入到整个开发工作流程中。</p>

<p>这意味着将实时安全扫描直接集成到开发者环境中——在 IDE 和 pull request 中，而不仅仅是 CI/CD 管道中。</p>

<h3 id="ai-驱动的安全代理">AI 驱动的安全代理</h3>

<p>安全和质量需要人类专业知识，新兴的 AI 驱动安全代理可以支持开发者大规模应用这一点——标记问题、建议修复，甚至在代码离开本地环境之前指导修复。</p>

<h3 id="devsecops-的角色">DevSecOps 的角色</h3>

<p>这正是 DevSecOps 大显身手的地方。这些团队处于独特位置，可以闭合创建和修正之间的循环，嵌入防护栏，加速反馈，构建预期 AI 波动性（而不仅仅是对其做出反应）的系统。</p>

<p>当风险被及早并在上下文中检测到时，它是可管理的。当在高速、AI 生成的系统中积累时，遏制它的难度和成本会高得多。</p>

<hr />

<h2 id="总结">总结</h2>

<p>Vibe Coding 目前是一个有争议的话题，被一些团队接受，被另一些团队视为一时风尚而拒绝。无论它是否成为软件开发的新基准，组织都必须为 AI 时代准备好严格的流程和安全防护。</p>

<p><strong>关键要点：</strong></p>

<ul>
  <li>模块化架构是控制 AI 代码风险的基础</li>
  <li>开发者需要从编码者转变为架构师思维</li>
  <li>实时安全扫描必须嵌入开发环境</li>
  <li>DevSecOps 在 AI 时代的重要性空前</li>
  <li>目前 Vibe Coding 更适合原型而非生产环境</li>
</ul>

<hr />

<p><strong>原文链接：</strong> <a href="https://www.business-reporter.co.uk/ai--automation/the-risks-of-vibe-coding">The risks of vibe coding - Business Reporter</a></p>

<p><strong>作者：</strong> Eran Kinsbruner, Checkmarx VP Portfolio Marketing</p>
<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Vibe Coding 最佳实践：10 个让开发效率提升 10 倍的关键技巧]]></title>
    <link href="https://droidyue.com/blog/2025/10/15/vibe-coding-zui-jia-shi-jian-10-ge-rang-kai-fa-xiao-lu-ti-sheng-10-bei-de-guan-jian-ji-qiao/"/>
    <updated>2025-10-15T14:30:00+08:00</updated>
    <id>https://droidyue.com/blog/2025/10/15/vibe-coding-zui-jia-shi-jian-10-ge-rang-kai-fa-xiao-lu-ti-sheng-10-bei-de-guan-jian-ji-qiao</id>
    <content type="html"><![CDATA[<p>本文整理 Vibe Coding（AI 辅助编程）的 10 个核心最佳实践，帮助提升开发效率和代码质量。</p>

<!--more-->

<h2 id="1-根据任务选择模型">1. 根据任务选择模型</h2>

<p><strong>Claude Haiku 4.5</strong>：速度最快、成本最低（$0.25/MTok 输入，$1.25/MTok 输出），适合简单快速任务（代码格式化、简单 bug 修复、基础代码补全、文档注释生成）。响应速度快，适合高频调用场景。</p>

<p><strong>Claude Sonnet 4.5</strong>：日常开发首选，用于 70-80% 编程任务（代码生成、重构、测试），性价比最高（$3/MTok 输入，$15/MTok 输出）。</p>

<p><strong>Claude Opus 4.1</strong>：复杂推理任务（多步骤工作流、架构决策），价格是 Sonnet 5 倍，支持 7 小时以上自主编程。</p>

<p><strong>OpenAI Codex / GPT-4</strong>：擅长代码补全和快速生成，GitHub Copilot 基于此技术。适合 IDE 内实时代码提示、函数级补全、单元测试生成。</p>

<p><strong>决策框架</strong>：</p>
<ul>
  <li>简单快速任务 → Claude Haiku 4.5</li>
  <li>实时代码补全 → Codex</li>
  <li>日常代码生成、重构 → Claude Sonnet 4.5</li>
  <li>复杂架构设计、多文件重构 → Claude Opus 4.1</li>
</ul>

<hr />

<h2 id="2-使用四要素-prompt-框架">2. 使用四要素 Prompt 框架</h2>

<p><strong>核心框架</strong>：</p>
<ul>
  <li><strong>上下文/角色</strong>：设定专业背景（”你是精通 Kotlin 协程的 Android 性能优化专家”）</li>
  <li><strong>指令</strong>：清晰的单一任务命令（”重构此 ViewModel 以减少数据库查询次数”）</li>
  <li><strong>内容</strong>：使用代码块标记实际代码</li>
  <li><strong>格式</strong>：明确输出结构（”提供重构后的代码并添加注释解释变更”）</li>
</ul>

<p><strong>实用模式</strong>：</p>
<ul>
  <li>逐步说明：”创建 Kotlin 函数：1. 接收用户 ID 列表；2. 从 Room 获取数据；3. 过滤活跃用户；4. 返回 Flow 并处理异常”</li>
  <li>示例驱动：”参考以下 ViewModel 写法，转换这个 Activity”</li>
  <li>角色扮演：”作为 Android 安全专家，审查此登录代码，重点关注 SharedPreferences 加密、网络请求安全、WebView 配置”</li>
</ul>

<p><strong>避免</strong>：</p>
<ul>
  <li>模糊请求（”让这个更好”）</li>
  <li>一次多个无关任务</li>
  <li>缺少错误堆栈的调试请求</li>
</ul>

<hr />

<h2 id="3-善用代码上下文和文件引用">3. 善用代码上下文和文件引用</h2>

<p><strong>核心问题</strong>：AI 不了解项目结构，生成代码可能与项目风格不一致。</p>

<p><strong>提供上下文的方法</strong>：</p>

<p><strong>引用文件</strong>：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class=""><span class="line">请参考 UserRepository.kt 的写法，为 ProductRepository 实现相同的缓存逻辑</span></code></pre></td></tr></table></div></figure></notextile></div>

<p><strong>粘贴关键代码</strong>：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="kotlin"><span class="line"><span class="c1">// 现有的 BaseViewModel 实现</span>
</span><span class="line"><span class="k">abstract</span> <span class="kd">class</span> <span class="nc">BaseViewModel</span> <span class="p">:</span> <span class="nc">ViewModel</span><span class="p">()</span> <span class="p">{</span>
</span><span class="line">    <span class="k">protected</span> <span class="kd">val</span> <span class="py">_loading</span> <span class="p">=</span> <span class="nc">MutableStateFlow</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
</span><span class="line"><span class="p">}</span>
</span><span class="line"><span class="c1">// 请按此模式实现 UserViewModel</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p><strong>说明架构</strong>：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="kotlin"><span class="line"><span class="err">项目使用</span> <span class="nc">MVVM</span> <span class="err">架构：</span>
</span><span class="line"><span class="p">-</span> <span class="n">data</span><span class="p">/</span> <span class="err">层：</span><span class="nc">Repository</span> <span class="p">+</span> <span class="nc">DataSource</span>
</span><span class="line"><span class="p">-</span> <span class="n">domain</span><span class="p">/</span> <span class="err">层：</span><span class="nc">UseCase</span> <span class="p">+</span> <span class="nc">Model</span>  
</span><span class="line"><span class="p">-</span> <span class="n">presentation</span><span class="p">/</span> <span class="err">层：</span><span class="nc">ViewModel</span> <span class="p">+</span> <span class="nc">Activity</span><span class="p">/</span><span class="nc">Fragment</span>
</span><span class="line"><span class="err">使用</span> <span class="nc">Hilt</span><span class="err">、</span><span class="nc">Room</span><span class="err">、</span><span class="nc">Retrofit</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p><strong>使用 @文件 语法</strong>：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="kotlin"><span class="line"><span class="nd">@MainActivity</span><span class="p">.</span><span class="n">kt</span> <span class="err">这个</span> <span class="nc">Activity</span> <span class="err">的内存泄漏在哪里？</span>
</span><span class="line"><span class="err">@</span><span class="n">app</span><span class="p">/</span><span class="n">build</span><span class="p">.</span><span class="n">gradle</span> <span class="err">添加</span> <span class="nc">Coil</span> <span class="err">图片加载库</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>首次对话说明技术栈和架构，涉及多文件时列出文件名，生成代码时提供参考示例。</p>

<hr />

<h2 id="4-针对复杂问题开启扩展思考模式">4. 针对复杂问题开启扩展思考模式</h2>

<p><strong>适用场景</strong>：复杂算法（图片压缩、列表优化）、不明确原因的 bug（ANR、内存泄漏）、架构决策（MVVM vs MVI）、跨文件重构、性能优化。</p>

<p><strong>不推荐</strong>：简单补全、语法修复、基本 CRUD、简单 UI 布局。</p>

<p><strong>Claude Code 魔法词</strong>：</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">think</code>：4,000 token</li>
  <li><code class="language-plaintext highlighter-rouge">think hard</code> / <code class="language-plaintext highlighter-rouge">megathink</code>：10,000 token</li>
  <li><code class="language-plaintext highlighter-rouge">think harder</code> / <code class="language-plaintext highlighter-rouge">ultrathink</code>：31,999 token</li>
</ul>

<p><strong>性能提升</strong>：SWE-bench Verified 从 62.3% 提升至 70.3%，数学问题达 96.2%。</p>

<p>从最小预算（1,024 token）开始，根据问题复杂度逐步增加。</p>

<hr />

<h2 id="5-采用小步迭代开发">5. 采用小步迭代开发</h2>

<p><strong>黄金法则</strong>：一次生成太多代码会导致混乱和 bug，使用最小有意义增量。</p>

<p><strong>增量流程</strong>：定义最小增量 → 编写失败测试 → AI 编写通过测试的代码 → 立即运行测试 → 失败则让 AI 诊断修复 → 重复。</p>

<p><strong>架构先行</strong>：编码前先绘制模块图（Repository-ViewModel-View）、定义数据流（LiveData/StateFlow）、确定组件职责。</p>

<p><strong>多轮精炼</strong>：第一轮生成基本结构 → 第二轮添加错误处理 → 第三轮优化性能 → 第四轮添加完整文档。每一轮都小而专注、可测试。</p>

<hr />

<h2 id="6-审查代码并建立测试防线">6. 审查代码并建立测试防线</h2>

<p><strong>核心原则</strong>：永远不要盲目接受 AI 输出。GitClear 研究发现粗心使用 AI 导致 bug 增加 41%。</p>

<p><strong>重点审查</strong>：潜在 bug、安全漏洞（未加密 SharedPreferences、不安全 WebView、Intent 劫持）、性能瓶颈（主线程阻塞、内存泄漏、过度绘制）、缺失错误处理、生命周期管理问题。</p>

<p><strong>测试生成</strong>：使用 AI 生成单元测试和 UI 测试，但必须人工验证测试是否真实有效、边界情况有意义（空列表、网络错误、权限拒绝）、使用合适框架（JUnit、Mockito、Espresso、Robolectric）。</p>

<p><strong>覆盖率目标</strong>：ViewModel/Repository 层、复杂业务逻辑、数据转换工具类追求 80%+ 代码覆盖率。</p>

<hr />

<h2 id="7-与版本控制系统深度集成">7. 与版本控制系统深度集成</h2>

<p><strong>核心原则</strong>：Git 是安全网，每个 AI 生成的代码都必须提交，便于快速回退错误修改。</p>

<p><strong>快速回退命令</strong>：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">git checkout <span class="nb">.</span>              <span class="c"># 放弃所有修改</span>
</span><span class="line">git checkout <span class="nt">--</span> file.kt     <span class="c"># 放弃指定文件修改</span>
</span><span class="line">git reset HEAD &lt;file&gt;       <span class="c"># 回退暂存区</span>
</span><span class="line">git reset <span class="nt">--hard</span> HEAD^      <span class="c"># 完全回退到上一次提交</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p><strong>原子提交</strong>：每次提交一个逻辑变更，使用祈使语气，消息正文解释原因。</p>

<p><strong>分支管理</strong>：使用清晰命名（<code class="language-plaintext highlighter-rouge">feat/add-retrofit-api</code>、<code class="language-plaintext highlighter-rouge">fix/memory-leak-viewmodel</code>）、所有 AI 实验都使用分支、出问题直接删除分支。</p>

<hr />

<h2 id="8-建立项目规则和指南文件">8. 建立项目规则和指南文件</h2>

<p><strong>规则文件</strong>：创建 <code class="language-plaintext highlighter-rouge">.editorconfig</code>、<code class="language-plaintext highlighter-rouge">detekt.yml</code> 或 <code class="language-plaintext highlighter-rouge">docs/coding-standards.md</code>，定义编码规范（优先使用 Kotlin、Activity/Fragment 最大 500 行、使用 ViewBinding）、测试要求（80% 覆盖率、JUnit 和 Espresso、ViewModel 必须有单元测试）、文档标准（public 方法使用 KDoc、每个模块需 README）、安全策略（永不硬编码 API 密钥、使用 EncryptedSharedPreferences）。</p>

<p><strong>Android 常用配置</strong>：</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">.editorconfig</code> - 统一代码格式</li>
  <li><code class="language-plaintext highlighter-rouge">detekt.yml</code> - Kotlin 代码质量检查</li>
  <li><code class="language-plaintext highlighter-rouge">lint.xml</code> - Android Lint 配置</li>
  <li><code class="language-plaintext highlighter-rouge">docs/android-conventions.md</code> - 团队开发规范</li>
</ul>

<p><strong>配置方法</strong>：Cursor IDE 在设置中添加用户规则，Claude Projects 在自定义指令中添加规则。</p>

<p><strong>核心收益</strong>：AI 自动遵循项目规范，团队代码生成一致，保持质量和可维护性。</p>

<hr />

<h2 id="9-及时会话管理">9. 及时会话管理</h2>

<p><strong>三种策略</strong>：</p>

<p><strong>清理会话（Clear）</strong>：任务切换或 AI 出现混乱时使用 <code class="language-plaintext highlighter-rouge">/clear</code> 或 Cmd/Ctrl + Shift + N。</p>

<p><strong>新建会话（New Chat）</strong>：开始完全不同的任务时，避免上下文混淆。</p>

<p><strong>压缩会话（Compact）</strong>：长会话超过 50 轮对话时使用 <code class="language-plaintext highlighter-rouge">/compact</code>，保留关键信息，释放上下文空间。</p>

<p><strong>时长建议</strong>：</p>
<ul>
  <li>短任务：15-30 分钟，10-20 轮</li>
  <li>中等任务：1-2 小时，30-50 轮</li>
  <li>复杂项目：单次不超过 3-4 小时</li>
</ul>

<p>超过 50-70 轮对话后性能明显下降，及时管理会话。</p>

<hr />

<h2 id="10-使用-mcp-扩展-ai-能力">10. 使用 MCP 扩展 AI 能力</h2>

<p><strong>什么是 MCP</strong>：Model Context Protocol 让 AI 直接执行 ADB 命令、查询设备状态，无需手动复制日志。</p>

<h3 id="android-adb-mcp-server">Android ADB MCP Server</h3>

<p>创建 <code class="language-plaintext highlighter-rouge">adb-mcp-server.js</code>：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
</pre></td><td class="code"><pre><code class="javascript"><span class="line"><span class="cp">#!/usr/bin/env node
</span><span class="line"></span><span class="kd">const</span> <span class="p">{</span> <span class="nx">Server</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">@modelcontextprotocol/sdk/server/index.js</span><span class="dl">'</span><span class="p">);</span>
</span><span class="line"><span class="kd">const</span> <span class="p">{</span> <span class="nx">exec</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">child_process</span><span class="dl">'</span><span class="p">);</span>
</span><span class="line"><span class="kd">const</span> <span class="p">{</span> <span class="nx">promisify</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">util</span><span class="dl">'</span><span class="p">);</span>
</span><span class="line"><span class="kd">const</span> <span class="nx">execPromise</span> <span class="o">=</span> <span class="nx">promisify</span><span class="p">(</span><span class="nx">exec</span><span class="p">);</span>
</span><span class="line">
</span><span class="line"><span class="kd">const</span> <span class="nx">server</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Server</span><span class="p">({</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">adb-server</span><span class="dl">'</span><span class="p">,</span> <span class="na">version</span><span class="p">:</span> <span class="dl">'</span><span class="s1">1.0.0</span><span class="dl">'</span> <span class="p">});</span>
</span><span class="line">
</span><span class="line"><span class="nx">server</span><span class="p">.</span><span class="nx">tool</span><span class="p">(</span><span class="dl">'</span><span class="s1">adb-logcat</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Get device logs</span><span class="dl">'</span><span class="p">,</span> <span class="k">async</span> <span class="p">({</span> <span class="nx">lines</span> <span class="o">=</span> <span class="mi">100</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span><span class="line">  <span class="kd">const</span> <span class="p">{</span> <span class="nx">stdout</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">execPromise</span><span class="p">(</span><span class="s2">`adb logcat -d -t </span><span class="p">${</span><span class="nx">lines</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
</span><span class="line">  <span class="k">return</span> <span class="p">{</span> <span class="na">content</span><span class="p">:</span> <span class="nx">stdout</span> <span class="p">};</span>
</span><span class="line"><span class="p">});</span>
</span><span class="line">
</span><span class="line"><span class="nx">server</span><span class="p">.</span><span class="nx">tool</span><span class="p">(</span><span class="dl">'</span><span class="s1">adb-meminfo</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Get memory usage</span><span class="dl">'</span><span class="p">,</span> <span class="k">async</span> <span class="p">({</span> <span class="nx">packageName</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span><span class="line">  <span class="kd">const</span> <span class="p">{</span> <span class="nx">stdout</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">execPromise</span><span class="p">(</span><span class="s2">`adb shell dumpsys meminfo </span><span class="p">${</span><span class="nx">packageName</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
</span><span class="line">  <span class="k">return</span> <span class="p">{</span> <span class="na">content</span><span class="p">:</span> <span class="nx">stdout</span> <span class="p">};</span>
</span><span class="line"><span class="p">});</span>
</span><span class="line">
</span><span class="line"><span class="nx">server</span><span class="p">.</span><span class="nx">tool</span><span class="p">(</span><span class="dl">'</span><span class="s1">adb-install</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">Install APK</span><span class="dl">'</span><span class="p">,</span> <span class="k">async</span> <span class="p">({</span> <span class="nx">apkPath</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span><span class="line">  <span class="kd">const</span> <span class="p">{</span> <span class="nx">stdout</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">execPromise</span><span class="p">(</span><span class="s2">`adb install -r </span><span class="p">${</span><span class="nx">apkPath</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
</span><span class="line">  <span class="k">return</span> <span class="p">{</span> <span class="na">content</span><span class="p">:</span> <span class="nx">stdout</span> <span class="p">};</span>
</span><span class="line"><span class="p">});</span>
</span><span class="line">
</span><span class="line"><span class="nx">server</span><span class="p">.</span><span class="nx">start</span><span class="p">();</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p><strong>配置 Claude Desktop</strong>（<code class="language-plaintext highlighter-rouge">~/Library/Application Support/Claude/claude_desktop_config.json</code>）：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="p">{</span><span class="w">
</span><span class="line">  </span><span class="nl">"mcpServers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="line">    </span><span class="nl">"adb"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="line">      </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node"</span><span class="p">,</span><span class="w">
</span><span class="line">      </span><span class="nl">"args"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"/path/to/adb-mcp-server.js"</span><span class="p">]</span><span class="w">
</span><span class="line">    </span><span class="p">}</span><span class="w">
</span><span class="line">  </span><span class="p">}</span><span class="w">
</span><span class="line"></span><span class="p">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="实战场景">实战场景</h3>

<p><strong>分析崩溃</strong>：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="err">用户：应用启动时崩溃</span><span class="w">
</span><span class="line"></span><span class="err">AI：</span><span class="p">[</span><span class="err">调用</span><span class="w"> </span><span class="err">adb-logcat</span><span class="p">]</span><span class="w"> </span><span class="err">发现</span><span class="w"> </span><span class="err">NullPointerException</span><span class="w"> </span><span class="err">in</span><span class="w"> </span><span class="err">MainActivity:</span><span class="mi">45</span><span class="w">
</span><span class="line">     </span><span class="err">user</span><span class="w"> </span><span class="err">对象为</span><span class="w"> </span><span class="kc">null</span><span class="err">，建议使用</span><span class="w"> </span><span class="err">safe</span><span class="w"> </span><span class="err">call</span><span class="w"> </span><span class="err">(?.)</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p><strong>性能排查</strong>：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="err">用户：列表滑动很卡</span><span class="w">
</span><span class="line"></span><span class="err">AI：</span><span class="p">[</span><span class="err">调用</span><span class="w"> </span><span class="err">adb-meminfo</span><span class="p">]</span><span class="w"> </span><span class="err">内存</span><span class="w"> </span><span class="mi">450</span><span class="err">MB，ImageLoader</span><span class="w"> </span><span class="err">持有</span><span class="w"> </span><span class="err">Activity</span><span class="w"> </span><span class="err">引用</span><span class="w">
</span><span class="line">     </span><span class="err">建议使用</span><span class="w"> </span><span class="err">WeakReference</span><span class="w"> </span><span class="err">和</span><span class="w"> </span><span class="err">Glide</span><span class="w"> </span><span class="err">生命周期管理</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="配置建议">配置建议</h3>

<p><strong>安全限制</strong>：添加包名白名单，避免误操作。
<strong>错误处理</strong>：捕获异常，提示检查 USB 调试。
<strong>性能优化</strong>：限制 logcat 行数（默认 100），使用 <code class="language-plaintext highlighter-rouge">-d</code> 参数。</p>

<hr />

<h2 id="总结">总结</h2>

<p>掌握这十个最佳实践后，原本需要 3 天完成的功能模块可以压缩到半小时。关键在于将 AI 视为超级助手而非普通工具，快速试错、频繁提交、大胆回退，让看似不可能的开发效率成为现实。</p>
<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[一看就会，为 AI 编程 Agent 撸一个 MCP 服务]]></title>
    <link href="https://droidyue.com/blog/2025/10/15/kan-jiu-hui-wei-ai-bian-cheng-agent-lu-ge-mcp-fu-wu/"/>
    <updated>2025-10-15T14:30:00+08:00</updated>
    <id>https://droidyue.com/blog/2025/10/15/kan-jiu-hui-wei-ai-bian-cheng-agent-lu-ge-mcp-fu-wu</id>
    <content type="html"><![CDATA[<p>通过 MCP (Model Context Protocol) 让 AI 助手直接调用 ADB 命令操作 Android 设备，实现日志查看、应用安装、性能分析等自动化操作。</p>

<!--more-->

<h2 id="mcp-协议说明">MCP 协议说明</h2>

<p>MCP 是 Anthropic 推出的开放协议，用于连接 AI 助手与外部工具。MCP Server 将特定工具包装成标准化接口，让 AI 能够理解和调用。</p>

<p>架构如下：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class=""><span class="line">Claude Desktop/API → MCP Server → ADB Commands → Android Device</span></code></pre></td></tr></table></div></figure></notextile></div>

<hr />

<h2 id="实现步骤">实现步骤</h2>

<h3 id="初始化项目">初始化项目</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line"><span class="nb">mkdir </span>adb_mcp
</span><span class="line"><span class="nb">cd </span>adb_mcp
</span><span class="line">npm init <span class="nt">-y</span>
</span><span class="line">npm <span class="nb">install</span> @modelcontextprotocol/sdk</span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="配置-packagejson">配置 package.json</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="p">{</span><span class="w">
</span><span class="line">  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"adb-mcp-server"</span><span class="p">,</span><span class="w">
</span><span class="line">  </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0.0"</span><span class="p">,</span><span class="w">
</span><span class="line">  </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MCP Server for Android Debug Bridge (ADB) operations"</span><span class="p">,</span><span class="w">
</span><span class="line">  </span><span class="nl">"main"</span><span class="p">:</span><span class="w"> </span><span class="s2">"adb-mcp-server.js"</span><span class="p">,</span><span class="w">
</span><span class="line">  </span><span class="nl">"bin"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="line">    </span><span class="nl">"adb-mcp-server"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./adb-mcp-server.js"</span><span class="w">
</span><span class="line">  </span><span class="p">},</span><span class="w">
</span><span class="line">  </span><span class="nl">"scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="line">    </span><span class="nl">"start"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node adb-mcp-server.js"</span><span class="w">
</span><span class="line">  </span><span class="p">},</span><span class="w">
</span><span class="line">  </span><span class="nl">"keywords"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"mcp"</span><span class="p">,</span><span class="w"> </span><span class="s2">"adb"</span><span class="p">,</span><span class="w"> </span><span class="s2">"android"</span><span class="p">,</span><span class="w"> </span><span class="s2">"debug"</span><span class="p">],</span><span class="w">
</span><span class="line">  </span><span class="nl">"author"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
</span><span class="line">  </span><span class="nl">"license"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MIT"</span><span class="p">,</span><span class="w">
</span><span class="line">  </span><span class="nl">"dependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="line">    </span><span class="nl">"@modelcontextprotocol/sdk"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^1.0.0"</span><span class="w">
</span><span class="line">  </span><span class="p">}</span><span class="w">
</span><span class="line"></span><span class="p">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="核心代码实现">核心代码实现</h3>

<p>创建 <code class="language-plaintext highlighter-rouge">adb-mcp-server.js</code>：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
<span class="line-number">28</span>
<span class="line-number">29</span>
<span class="line-number">30</span>
<span class="line-number">31</span>
<span class="line-number">32</span>
<span class="line-number">33</span>
<span class="line-number">34</span>
<span class="line-number">35</span>
<span class="line-number">36</span>
<span class="line-number">37</span>
<span class="line-number">38</span>
<span class="line-number">39</span>
<span class="line-number">40</span>
<span class="line-number">41</span>
<span class="line-number">42</span>
<span class="line-number">43</span>
<span class="line-number">44</span>
<span class="line-number">45</span>
<span class="line-number">46</span>
<span class="line-number">47</span>
<span class="line-number">48</span>
<span class="line-number">49</span>
<span class="line-number">50</span>
<span class="line-number">51</span>
<span class="line-number">52</span>
<span class="line-number">53</span>
<span class="line-number">54</span>
<span class="line-number">55</span>
<span class="line-number">56</span>
<span class="line-number">57</span>
<span class="line-number">58</span>
<span class="line-number">59</span>
<span class="line-number">60</span>
<span class="line-number">61</span>
<span class="line-number">62</span>
<span class="line-number">63</span>
<span class="line-number">64</span>
<span class="line-number">65</span>
<span class="line-number">66</span>
<span class="line-number">67</span>
<span class="line-number">68</span>
<span class="line-number">69</span>
<span class="line-number">70</span>
<span class="line-number">71</span>
<span class="line-number">72</span>
<span class="line-number">73</span>
<span class="line-number">74</span>
<span class="line-number">75</span>
<span class="line-number">76</span>
<span class="line-number">77</span>
<span class="line-number">78</span>
<span class="line-number">79</span>
<span class="line-number">80</span>
<span class="line-number">81</span>
<span class="line-number">82</span>
<span class="line-number">83</span>
<span class="line-number">84</span>
<span class="line-number">85</span>
<span class="line-number">86</span>
<span class="line-number">87</span>
<span class="line-number">88</span>
<span class="line-number">89</span>
<span class="line-number">90</span>
<span class="line-number">91</span>
<span class="line-number">92</span>
<span class="line-number">93</span>
<span class="line-number">94</span>
<span class="line-number">95</span>
<span class="line-number">96</span>
<span class="line-number">97</span>
<span class="line-number">98</span>
<span class="line-number">99</span>
<span class="line-number">100</span>
<span class="line-number">101</span>
<span class="line-number">102</span>
<span class="line-number">103</span>
<span class="line-number">104</span>
<span class="line-number">105</span>
<span class="line-number">106</span>
<span class="line-number">107</span>
<span class="line-number">108</span>
<span class="line-number">109</span>
<span class="line-number">110</span>
<span class="line-number">111</span>
<span class="line-number">112</span>
<span class="line-number">113</span>
<span class="line-number">114</span>
<span class="line-number">115</span>
<span class="line-number">116</span>
<span class="line-number">117</span>
<span class="line-number">118</span>
<span class="line-number">119</span>
<span class="line-number">120</span>
<span class="line-number">121</span>
<span class="line-number">122</span>
<span class="line-number">123</span>
<span class="line-number">124</span>
<span class="line-number">125</span>
<span class="line-number">126</span>
<span class="line-number">127</span>
<span class="line-number">128</span>
<span class="line-number">129</span>
<span class="line-number">130</span>
<span class="line-number">131</span>
<span class="line-number">132</span>
<span class="line-number">133</span>
<span class="line-number">134</span>
<span class="line-number">135</span>
<span class="line-number">136</span>
<span class="line-number">137</span>
<span class="line-number">138</span>
<span class="line-number">139</span>
<span class="line-number">140</span>
<span class="line-number">141</span>
<span class="line-number">142</span>
<span class="line-number">143</span>
<span class="line-number">144</span>
<span class="line-number">145</span>
<span class="line-number">146</span>
<span class="line-number">147</span>
<span class="line-number">148</span>
<span class="line-number">149</span>
<span class="line-number">150</span>
<span class="line-number">151</span>
<span class="line-number">152</span>
<span class="line-number">153</span>
<span class="line-number">154</span>
<span class="line-number">155</span>
<span class="line-number">156</span>
<span class="line-number">157</span>
<span class="line-number">158</span>
<span class="line-number">159</span>
<span class="line-number">160</span>
<span class="line-number">161</span>
<span class="line-number">162</span>
<span class="line-number">163</span>
<span class="line-number">164</span>
<span class="line-number">165</span>
<span class="line-number">166</span>
<span class="line-number">167</span>
<span class="line-number">168</span>
<span class="line-number">169</span>
<span class="line-number">170</span>
<span class="line-number">171</span>
<span class="line-number">172</span>
<span class="line-number">173</span>
<span class="line-number">174</span>
<span class="line-number">175</span>
<span class="line-number">176</span>
<span class="line-number">177</span>
<span class="line-number">178</span>
<span class="line-number">179</span>
<span class="line-number">180</span>
<span class="line-number">181</span>
<span class="line-number">182</span>
<span class="line-number">183</span>
<span class="line-number">184</span>
<span class="line-number">185</span>
<span class="line-number">186</span>
<span class="line-number">187</span>
<span class="line-number">188</span>
<span class="line-number">189</span>
<span class="line-number">190</span>
<span class="line-number">191</span>
<span class="line-number">192</span>
<span class="line-number">193</span>
<span class="line-number">194</span>
<span class="line-number">195</span>
<span class="line-number">196</span>
<span class="line-number">197</span>
<span class="line-number">198</span>
<span class="line-number">199</span>
<span class="line-number">200</span>
<span class="line-number">201</span>
<span class="line-number">202</span>
</pre></td><td class="code"><pre><code class="javascript"><span class="line"><span class="cp">#!/usr/bin/env node
</span><span class="line"></span><span class="kd">const</span> <span class="p">{</span> <span class="nx">Server</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">@modelcontextprotocol/sdk/server/index.js</span><span class="dl">'</span><span class="p">);</span>
</span><span class="line"><span class="kd">const</span> <span class="p">{</span> <span class="nx">StdioServerTransport</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">@modelcontextprotocol/sdk/server/stdio.js</span><span class="dl">'</span><span class="p">);</span>
</span><span class="line"><span class="kd">const</span> <span class="p">{</span> <span class="nx">CallToolRequestSchema</span><span class="p">,</span> <span class="nx">ListToolsRequestSchema</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">@modelcontextprotocol/sdk/types.js</span><span class="dl">'</span><span class="p">);</span>
</span><span class="line"><span class="kd">const</span> <span class="p">{</span> <span class="nx">exec</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">child_process</span><span class="dl">'</span><span class="p">);</span>
</span><span class="line"><span class="kd">const</span> <span class="p">{</span> <span class="nx">promisify</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">util</span><span class="dl">'</span><span class="p">);</span>
</span><span class="line">
</span><span class="line"><span class="kd">const</span> <span class="nx">execPromise</span> <span class="o">=</span> <span class="nx">promisify</span><span class="p">(</span><span class="nx">exec</span><span class="p">);</span>
</span><span class="line">
</span><span class="line"><span class="c1">// 创建 MCP Server 实例</span>
</span><span class="line"><span class="kd">const</span> <span class="nx">server</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Server</span><span class="p">(</span>
</span><span class="line">  <span class="p">{</span>
</span><span class="line">    <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">adb-server</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">    <span class="na">version</span><span class="p">:</span> <span class="dl">'</span><span class="s1">1.0.0</span><span class="dl">'</span>
</span><span class="line">  <span class="p">},</span>
</span><span class="line">  <span class="p">{</span>
</span><span class="line">    <span class="na">capabilities</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">      <span class="na">tools</span><span class="p">:</span> <span class="p">{}</span>
</span><span class="line">    <span class="p">}</span>
</span><span class="line">  <span class="p">}</span>
</span><span class="line"><span class="p">);</span>
</span><span class="line">
</span><span class="line"><span class="c1">// 定义工具列表</span>
</span><span class="line"><span class="kd">const</span> <span class="nx">tools</span> <span class="o">=</span> <span class="p">[</span>
</span><span class="line">  <span class="p">{</span>
</span><span class="line">    <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">adb-logcat</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">    <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Get Android device logs</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">    <span class="na">inputSchema</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">      <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">      <span class="na">properties</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">        <span class="na">lines</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">          <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">number</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">          <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Number of log lines to retrieve</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">          <span class="na">default</span><span class="p">:</span> <span class="mi">100</span>
</span><span class="line">        <span class="p">}</span>
</span><span class="line">      <span class="p">}</span>
</span><span class="line">    <span class="p">}</span>
</span><span class="line">  <span class="p">},</span>
</span><span class="line">  <span class="p">{</span>
</span><span class="line">    <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">adb-devices</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">    <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">List connected Android devices</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">    <span class="na">inputSchema</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">      <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">      <span class="na">properties</span><span class="p">:</span> <span class="p">{}</span>
</span><span class="line">    <span class="p">}</span>
</span><span class="line">  <span class="p">},</span>
</span><span class="line">  <span class="p">{</span>
</span><span class="line">    <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">adb-install</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">    <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Install APK on connected device</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">    <span class="na">inputSchema</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">      <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">      <span class="na">properties</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">        <span class="na">apkPath</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">          <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">          <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Path to the APK file to install</span><span class="dl">'</span>
</span><span class="line">        <span class="p">}</span>
</span><span class="line">      <span class="p">},</span>
</span><span class="line">      <span class="na">required</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">apkPath</span><span class="dl">'</span><span class="p">]</span>
</span><span class="line">    <span class="p">}</span>
</span><span class="line">  <span class="p">},</span>
</span><span class="line">  <span class="p">{</span>
</span><span class="line">    <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">adb-app-info</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">    <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Get app package information</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">    <span class="na">inputSchema</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">      <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">      <span class="na">properties</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">        <span class="na">packageName</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">          <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">          <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Android package name (e.g., com.example.app)</span><span class="dl">'</span>
</span><span class="line">        <span class="p">}</span>
</span><span class="line">      <span class="p">},</span>
</span><span class="line">      <span class="na">required</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">packageName</span><span class="dl">'</span><span class="p">]</span>
</span><span class="line">    <span class="p">}</span>
</span><span class="line">  <span class="p">},</span>
</span><span class="line">  <span class="p">{</span>
</span><span class="line">    <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">adb-meminfo</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">    <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Get app memory usage information</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">    <span class="na">inputSchema</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">      <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">      <span class="na">properties</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">        <span class="na">packageName</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">          <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">          <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Android package name (e.g., com.example.app)</span><span class="dl">'</span>
</span><span class="line">        <span class="p">}</span>
</span><span class="line">      <span class="p">},</span>
</span><span class="line">      <span class="na">required</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">packageName</span><span class="dl">'</span><span class="p">]</span>
</span><span class="line">    <span class="p">}</span>
</span><span class="line">  <span class="p">},</span>
</span><span class="line">  <span class="p">{</span>
</span><span class="line">    <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">adb-clear-data</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">    <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Clear app data and cache</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">    <span class="na">inputSchema</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">      <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">      <span class="na">properties</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">        <span class="na">packageName</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">          <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">          <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Android package name (e.g., com.example.app)</span><span class="dl">'</span>
</span><span class="line">        <span class="p">}</span>
</span><span class="line">      <span class="p">},</span>
</span><span class="line">      <span class="na">required</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">packageName</span><span class="dl">'</span><span class="p">]</span>
</span><span class="line">    <span class="p">}</span>
</span><span class="line">  <span class="p">}</span>
</span><span class="line"><span class="p">];</span>
</span><span class="line">
</span><span class="line"><span class="c1">// 处理工具列表请求</span>
</span><span class="line"><span class="nx">server</span><span class="p">.</span><span class="nx">setRequestHandler</span><span class="p">(</span><span class="nx">ListToolsRequestSchema</span><span class="p">,</span> <span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span><span class="line">  <span class="k">return</span> <span class="p">{</span> <span class="nx">tools</span> <span class="p">};</span>
</span><span class="line"><span class="p">});</span>
</span><span class="line">
</span><span class="line"><span class="c1">// 处理工具执行请求</span>
</span><span class="line"><span class="nx">server</span><span class="p">.</span><span class="nx">setRequestHandler</span><span class="p">(</span><span class="nx">CallToolRequestSchema</span><span class="p">,</span> <span class="k">async</span> <span class="p">(</span><span class="nx">request</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span><span class="line">  <span class="kd">const</span> <span class="p">{</span> <span class="nx">name</span><span class="p">,</span> <span class="na">arguments</span><span class="p">:</span> <span class="nx">args</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">request</span><span class="p">.</span><span class="nx">params</span><span class="p">;</span>
</span><span class="line">
</span><span class="line">  <span class="k">try</span> <span class="p">{</span>
</span><span class="line">    <span class="kd">let</span> <span class="nx">result</span><span class="p">;</span>
</span><span class="line">
</span><span class="line">    <span class="k">switch</span> <span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="p">{</span>
</span><span class="line">      <span class="k">case</span> <span class="dl">'</span><span class="s1">adb-logcat</span><span class="dl">'</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">        <span class="kd">const</span> <span class="nx">lines</span> <span class="o">=</span> <span class="nx">args</span><span class="p">.</span><span class="nx">lines</span> <span class="o">||</span> <span class="mi">100</span><span class="p">;</span>
</span><span class="line">        <span class="kd">const</span> <span class="p">{</span> <span class="nx">stdout</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">execPromise</span><span class="p">(</span><span class="s2">`adb logcat -d -t </span><span class="p">${</span><span class="nx">lines</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
</span><span class="line">        <span class="nx">result</span> <span class="o">=</span> <span class="nx">stdout</span><span class="p">;</span>
</span><span class="line">        <span class="k">break</span><span class="p">;</span>
</span><span class="line">      <span class="p">}</span>
</span><span class="line">
</span><span class="line">      <span class="k">case</span> <span class="dl">'</span><span class="s1">adb-devices</span><span class="dl">'</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">        <span class="kd">const</span> <span class="p">{</span> <span class="nx">stdout</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">execPromise</span><span class="p">(</span><span class="dl">'</span><span class="s1">adb devices -l</span><span class="dl">'</span><span class="p">);</span>
</span><span class="line">        <span class="nx">result</span> <span class="o">=</span> <span class="nx">stdout</span><span class="p">;</span>
</span><span class="line">        <span class="k">break</span><span class="p">;</span>
</span><span class="line">      <span class="p">}</span>
</span><span class="line">
</span><span class="line">      <span class="k">case</span> <span class="dl">'</span><span class="s1">adb-install</span><span class="dl">'</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">args</span><span class="p">.</span><span class="nx">apkPath</span><span class="p">)</span> <span class="p">{</span>
</span><span class="line">          <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">apkPath is required</span><span class="dl">'</span><span class="p">);</span>
</span><span class="line">        <span class="p">}</span>
</span><span class="line">        <span class="kd">const</span> <span class="p">{</span> <span class="nx">stdout</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">execPromise</span><span class="p">(</span><span class="s2">`adb install -r "</span><span class="p">${</span><span class="nx">args</span><span class="p">.</span><span class="nx">apkPath</span><span class="p">}</span><span class="s2">"`</span><span class="p">);</span>
</span><span class="line">        <span class="nx">result</span> <span class="o">=</span> <span class="nx">stdout</span><span class="p">;</span>
</span><span class="line">        <span class="k">break</span><span class="p">;</span>
</span><span class="line">      <span class="p">}</span>
</span><span class="line">
</span><span class="line">      <span class="k">case</span> <span class="dl">'</span><span class="s1">adb-app-info</span><span class="dl">'</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">args</span><span class="p">.</span><span class="nx">packageName</span><span class="p">)</span> <span class="p">{</span>
</span><span class="line">          <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">packageName is required</span><span class="dl">'</span><span class="p">);</span>
</span><span class="line">        <span class="p">}</span>
</span><span class="line">        <span class="kd">const</span> <span class="p">{</span> <span class="nx">stdout</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">execPromise</span><span class="p">(</span><span class="s2">`adb shell dumpsys package </span><span class="p">${</span><span class="nx">args</span><span class="p">.</span><span class="nx">packageName</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
</span><span class="line">        <span class="nx">result</span> <span class="o">=</span> <span class="nx">stdout</span><span class="p">;</span>
</span><span class="line">        <span class="k">break</span><span class="p">;</span>
</span><span class="line">      <span class="p">}</span>
</span><span class="line">
</span><span class="line">      <span class="k">case</span> <span class="dl">'</span><span class="s1">adb-meminfo</span><span class="dl">'</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">args</span><span class="p">.</span><span class="nx">packageName</span><span class="p">)</span> <span class="p">{</span>
</span><span class="line">          <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">packageName is required</span><span class="dl">'</span><span class="p">);</span>
</span><span class="line">        <span class="p">}</span>
</span><span class="line">        <span class="kd">const</span> <span class="p">{</span> <span class="nx">stdout</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">execPromise</span><span class="p">(</span><span class="s2">`adb shell dumpsys meminfo </span><span class="p">${</span><span class="nx">args</span><span class="p">.</span><span class="nx">packageName</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
</span><span class="line">        <span class="nx">result</span> <span class="o">=</span> <span class="nx">stdout</span><span class="p">;</span>
</span><span class="line">        <span class="k">break</span><span class="p">;</span>
</span><span class="line">      <span class="p">}</span>
</span><span class="line">
</span><span class="line">      <span class="k">case</span> <span class="dl">'</span><span class="s1">adb-clear-data</span><span class="dl">'</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">        <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">args</span><span class="p">.</span><span class="nx">packageName</span><span class="p">)</span> <span class="p">{</span>
</span><span class="line">          <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">packageName is required</span><span class="dl">'</span><span class="p">);</span>
</span><span class="line">        <span class="p">}</span>
</span><span class="line">        <span class="kd">const</span> <span class="p">{</span> <span class="nx">stdout</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">execPromise</span><span class="p">(</span><span class="s2">`adb shell pm clear </span><span class="p">${</span><span class="nx">args</span><span class="p">.</span><span class="nx">packageName</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
</span><span class="line">        <span class="nx">result</span> <span class="o">=</span> <span class="nx">stdout</span><span class="p">;</span>
</span><span class="line">        <span class="k">break</span><span class="p">;</span>
</span><span class="line">      <span class="p">}</span>
</span><span class="line">
</span><span class="line">      <span class="nl">default</span><span class="p">:</span>
</span><span class="line">        <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">`Unknown tool: </span><span class="p">${</span><span class="nx">name</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
</span><span class="line">    <span class="p">}</span>
</span><span class="line">
</span><span class="line">    <span class="k">return</span> <span class="p">{</span>
</span><span class="line">      <span class="na">content</span><span class="p">:</span> <span class="p">[</span>
</span><span class="line">        <span class="p">{</span>
</span><span class="line">          <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">text</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">          <span class="na">text</span><span class="p">:</span> <span class="nx">result</span>
</span><span class="line">        <span class="p">}</span>
</span><span class="line">      <span class="p">]</span>
</span><span class="line">    <span class="p">};</span>
</span><span class="line">  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span><span class="line">    <span class="k">return</span> <span class="p">{</span>
</span><span class="line">      <span class="na">content</span><span class="p">:</span> <span class="p">[</span>
</span><span class="line">        <span class="p">{</span>
</span><span class="line">          <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">text</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">          <span class="na">text</span><span class="p">:</span> <span class="s2">`Error executing </span><span class="p">${</span><span class="nx">name</span><span class="p">}</span><span class="s2">: </span><span class="p">${</span><span class="nx">error</span><span class="p">.</span><span class="nx">message</span><span class="p">}</span><span class="s2">`</span>
</span><span class="line">        <span class="p">}</span>
</span><span class="line">      <span class="p">],</span>
</span><span class="line">      <span class="na">isError</span><span class="p">:</span> <span class="kc">true</span>
</span><span class="line">    <span class="p">};</span>
</span><span class="line">  <span class="p">}</span>
</span><span class="line"><span class="p">});</span>
</span><span class="line">
</span><span class="line"><span class="c1">// 启动服务器</span>
</span><span class="line"><span class="k">async</span> <span class="kd">function</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{</span>
</span><span class="line">  <span class="kd">const</span> <span class="nx">transport</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">StdioServerTransport</span><span class="p">();</span>
</span><span class="line">  <span class="k">await</span> <span class="nx">server</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">transport</span><span class="p">);</span>
</span><span class="line">  <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">'</span><span class="s1">ADB MCP Server running on stdio</span><span class="dl">'</span><span class="p">);</span>
</span><span class="line"><span class="p">}</span>
</span><span class="line">
</span><span class="line"><span class="nx">main</span><span class="p">().</span><span class="k">catch</span><span class="p">((</span><span class="nx">error</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span><span class="line">  <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Server error:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span>
</span><span class="line">  <span class="nx">process</span><span class="p">.</span><span class="nx">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span><span class="line"><span class="p">});</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>赋予执行权限：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line"><span class="nb">chmod</span> +x adb-mcp-server.js</span></code></pre></td></tr></table></div></figure></notextile></div>

<hr />

<h2 id="关键实现说明">关键实现说明</h2>

<h3 id="server-初始化">Server 初始化</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
</pre></td><td class="code"><pre><code class="javascript"><span class="line"><span class="kd">const</span> <span class="nx">server</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Server</span><span class="p">(</span>
</span><span class="line">  <span class="p">{</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">adb-server</span><span class="dl">'</span><span class="p">,</span> <span class="na">version</span><span class="p">:</span> <span class="dl">'</span><span class="s1">1.0.0</span><span class="dl">'</span> <span class="p">},</span>
</span><span class="line">  <span class="p">{</span> <span class="na">capabilities</span><span class="p">:</span> <span class="p">{</span> <span class="na">tools</span><span class="p">:</span> <span class="p">{}</span> <span class="p">}</span> <span class="p">}</span>
</span><span class="line"><span class="p">);</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>声明服务器名称、版本和支持的能力类型。</p>

<h3 id="工具定义">工具定义</h3>

<p>每个工具包含三个部分：</p>
<ul>
  <li><strong>name</strong>: 唯一标识符</li>
  <li><strong>description</strong>: 功能描述，AI 根据此判断调用时机</li>
  <li><strong>inputSchema</strong>: JSON Schema 格式的参数定义</li>
</ul>

<p>示例：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
</pre></td><td class="code"><pre><code class="javascript"><span class="line"><span class="p">{</span>
</span><span class="line">  <span class="nl">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">adb-install</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">  <span class="nx">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Install APK on connected device</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">  <span class="nx">inputSchema</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">    <span class="nl">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">    <span class="nx">properties</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">      <span class="nl">apkPath</span><span class="p">:</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">,</span> <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Path to the APK file</span><span class="dl">'</span> <span class="p">}</span>
</span><span class="line">    <span class="p">},</span>
</span><span class="line">    <span class="nx">required</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">apkPath</span><span class="dl">'</span><span class="p">]</span>
</span><span class="line">  <span class="p">}</span>
</span><span class="line"><span class="p">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="请求处理">请求处理</h3>

<p>MCP 定义两种请求类型：</p>

<p><strong>ListToolsRequest</strong> - 列出所有可用工具：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="javascript"><span class="line"><span class="nx">server</span><span class="p">.</span><span class="nx">setRequestHandler</span><span class="p">(</span><span class="nx">ListToolsRequestSchema</span><span class="p">,</span> <span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span><span class="line">  <span class="k">return</span> <span class="p">{</span> <span class="nx">tools</span> <span class="p">};</span>
</span><span class="line"><span class="p">});</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p><strong>CallToolRequest</strong> - 执行具体工具：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
</pre></td><td class="code"><pre><code class="javascript"><span class="line"><span class="nx">server</span><span class="p">.</span><span class="nx">setRequestHandler</span><span class="p">(</span><span class="nx">CallToolRequestSchema</span><span class="p">,</span> <span class="k">async</span> <span class="p">(</span><span class="nx">request</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span><span class="line">  <span class="kd">const</span> <span class="p">{</span> <span class="nx">name</span><span class="p">,</span> <span class="na">arguments</span><span class="p">:</span> <span class="nx">args</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">request</span><span class="p">.</span><span class="nx">params</span><span class="p">;</span>
</span><span class="line">  <span class="c1">// 根据 name 执行相应 ADB 命令</span>
</span><span class="line"><span class="p">});</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="命令执行">命令执行</h3>

<p>使用 <code class="language-plaintext highlighter-rouge">child_process</code> 执行 ADB 命令：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="javascript"><span class="line"><span class="kd">const</span> <span class="p">{</span> <span class="nx">stdout</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">execPromise</span><span class="p">(</span><span class="s2">`adb devices -l`</span><span class="p">);</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<hr />

<h2 id="配置方式">配置方式</h2>

<h3 id="claude-desktop-配置">Claude Desktop 配置</h3>

<p>编辑 <code class="language-plaintext highlighter-rouge">~/Library/Application Support/Claude/claude_desktop_config.json</code>：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="p">{</span><span class="w">
</span><span class="line">  </span><span class="nl">"mcpServers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="line">    </span><span class="nl">"adb"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="line">      </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node"</span><span class="p">,</span><span class="w">
</span><span class="line">      </span><span class="nl">"args"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"/Users/你的用户名/Documents/self_host/adb_mcp/adb-mcp-server.js"</span><span class="p">]</span><span class="w">
</span><span class="line">    </span><span class="p">}</span><span class="w">
</span><span class="line">  </span><span class="p">}</span><span class="w">
</span><span class="line"></span><span class="p">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>配置完成后重启 Claude Desktop。</p>

<h3 id="claude-code-cli-配置">Claude Code CLI 配置</h3>

<p>使用 Claude Code 命令行工具添加 MCP Server：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">claude <span class="nt">-p</span> mcp add adb node /Users/xxxx/Documents/sxxx/adb_mcp/adb-mcp-server.js</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>参数说明：</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">adb</code>: MCP Server 名称</li>
  <li><code class="language-plaintext highlighter-rouge">node</code>: 运行命令</li>
  <li>最后是 <code class="language-plaintext highlighter-rouge">adb-mcp-server.js</code> 的完整路径</li>
</ul>

<h3 id="gemini-cli-配置">Gemini CLI 配置</h3>

<p>编辑 Gemini CLI 配置文件 <code class="language-plaintext highlighter-rouge">~/.gemini/mcp_config.json</code>：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="p">{</span><span class="w">
</span><span class="line">  </span><span class="nl">"mcpServers"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="line">    </span><span class="nl">"adb"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="line">      </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node"</span><span class="p">,</span><span class="w">
</span><span class="line">      </span><span class="nl">"args"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"/Users/xxxx/Documents/sxxx/adb_mcp/adb-mcp-server.js"</span><span class="p">]</span><span class="w">
</span><span class="line">    </span><span class="p">}</span><span class="w">
</span><span class="line">  </span><span class="p">}</span><span class="w">
</span><span class="line"></span><span class="p">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p><strong>注</strong>：Gemini CLI 的 MCP 配置可能因版本而异，建议以官方文档为准。</p>

<h3 id="copilot-cli-配置">Copilot CLI 配置</h3>

<p>编辑 Copilot CLI 配置文件 <code class="language-plaintext highlighter-rouge">~/.github-copilot/mcp_servers.json</code>：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="p">{</span><span class="w">
</span><span class="line">  </span><span class="nl">"adb"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="line">    </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node"</span><span class="p">,</span><span class="w">
</span><span class="line">    </span><span class="nl">"args"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"/Users/xxxx/Documents/sxxx/adb_mcp/adb-mcp-server.js"</span><span class="p">]</span><span class="w">
</span><span class="line">  </span><span class="p">}</span><span class="w">
</span><span class="line"></span><span class="p">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p><strong>注</strong>：Copilot CLI 的 MCP 配置可能因版本而异，建议以官方文档为准。</p>

<hr />

<h2 id="使用示例">使用示例</h2>

<p>在 Claude 中直接使用自然语言：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="err">查看连接的</span><span class="w"> </span><span class="err">Android</span><span class="w"> </span><span class="err">设备</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="err">获取最近</span><span class="w"> </span><span class="mi">50</span><span class="w"> </span><span class="err">行</span><span class="w"> </span><span class="err">logcat</span><span class="w"> </span><span class="err">日志</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="err">查看</span><span class="w"> </span><span class="err">com.android.chrome</span><span class="w"> </span><span class="err">的内存使用情况</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="err">清除</span><span class="w"> </span><span class="err">com.example.app</span><span class="w"> </span><span class="err">的数据</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Claude 会自动调用对应的 MCP 工具执行操作。</p>

<hr />

<h2 id="应用场景">应用场景</h2>

<h3 id="日志分析">日志分析</h3>

<p>传统方式：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">adb logcat <span class="nt">-d</span> <span class="o">&gt;</span> log.txt
</span><span class="line"><span class="c"># 手动搜索错误信息</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>使用 MCP：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">获取最近的 logcat 日志，找出所有 ERROR 级别信息</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Claude 自动执行并分析结果。</p>

<h3 id="性能监控">性能监控</h3>

<p>传统方式：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">adb shell dumpsys meminfo com.example.app
</span><span class="line"><span class="c"># 手动分析输出数据</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>使用 MCP：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">查看 com.example.app 的内存使用，分析是否有内存泄漏</span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="批量操作">批量操作</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">列出所有连接的设备，在每个设备上安装 /path/to/app.apk</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Claude 自动处理设备列表和批量安装。</p>

<hr />

<h2 id="扩展功能">扩展功能</h2>

<h3 id="添加截图">添加截图</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
</pre></td><td class="code"><pre><code class="javascript"><span class="line"><span class="p">{</span>
</span><span class="line">  <span class="nl">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">adb-screenshot</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">  <span class="nx">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Take a screenshot</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">  <span class="nx">inputSchema</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">    <span class="nl">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">    <span class="nx">properties</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">      <span class="nl">savePath</span><span class="p">:</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">,</span> <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Path to save screenshot</span><span class="dl">'</span> <span class="p">}</span>
</span><span class="line">    <span class="p">},</span>
</span><span class="line">    <span class="nx">required</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">savePath</span><span class="dl">'</span><span class="p">]</span>
</span><span class="line">  <span class="p">}</span>
</span><span class="line"><span class="p">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>执行命令：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="javascript"><span class="line"><span class="k">await</span> <span class="nx">execPromise</span><span class="p">(</span><span class="s2">`adb exec-out screencap -p &gt; </span><span class="p">${</span><span class="nx">args</span><span class="p">.</span><span class="nx">savePath</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="添加性能监控">添加性能监控</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="javascript"><span class="line"><span class="p">{</span>
</span><span class="line">  <span class="nl">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">adb-top</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">  <span class="nx">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Get CPU usage</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">  <span class="nx">inputSchema</span><span class="p">:</span> <span class="p">{</span> <span class="nl">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span><span class="p">,</span> <span class="nx">properties</span><span class="p">:</span> <span class="p">{}</span> <span class="p">}</span>
</span><span class="line"><span class="p">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="添加文件传输">添加文件传输</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
</pre></td><td class="code"><pre><code class="javascript"><span class="line"><span class="p">{</span>
</span><span class="line">  <span class="nl">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">adb-push</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">  <span class="nx">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Push file to device</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">  <span class="nx">inputSchema</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">    <span class="nl">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span><span class="p">,</span>
</span><span class="line">    <span class="nx">properties</span><span class="p">:</span> <span class="p">{</span>
</span><span class="line">      <span class="nl">localPath</span><span class="p">:</span> <span class="p">{</span> <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span> <span class="p">},</span>
</span><span class="line">      <span class="nx">remotePath</span><span class="p">:</span> <span class="p">{</span> <span class="nl">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span> <span class="p">}</span>
</span><span class="line">    <span class="p">},</span>
</span><span class="line">    <span class="nx">required</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">localPath</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">remotePath</span><span class="dl">'</span><span class="p">]</span>
</span><span class="line">  <span class="p">}</span>
</span><span class="line"><span class="p">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<hr />

<h2 id="注意事项">注意事项</h2>

<h3 id="权限检查">权限检查</h3>

<p>确保 ADB 已添加到系统 PATH：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">which adb
</span><span class="line">adb version</span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="设备授权">设备授权</h3>

<p>使用前确认设备已连接并授权：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">adb devices</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>如果显示 <code class="language-plaintext highlighter-rouge">unauthorized</code>，需在设备上确认 USB 调试授权。</p>

<h3 id="错误处理">错误处理</h3>

<p>生产环境应添加：</p>
<ul>
  <li>详细日志记录</li>
  <li>设备断开连接处理</li>
  <li>命令超时机制</li>
</ul>

<h3 id="安全性">安全性</h3>

<ul>
  <li>避免在不信任的环境使用</li>
  <li>注意 APK 路径注入风险</li>
  <li>考虑添加命令白名单</li>
</ul>

<hr />

<h2 id="参考资源">参考资源</h2>

<ul>
  <li><a href="https://modelcontextprotocol.io/">MCP 官方文档</a></li>
  <li><a href="https://github.com/modelcontextprotocol/sdk">MCP SDK GitHub</a></li>
  <li><a href="https://developer.android.com/tools/adb">ADB 官方文档</a></li>
</ul>
<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[同样是 Sonnet 4.5，为何 CLI 工具差距这么大]]></title>
    <link href="https://droidyue.com/blog/2025/10/13/tong-ge-sonnet-4-dot-5-wei-he-biao-xian-chai-5-bei/"/>
    <updated>2025-10-13T08:00:00+08:00</updated>
    <id>https://droidyue.com/blog/2025/10/13/tong-ge-sonnet-4-dot-5-wei-he-biao-xian-chai-5-bei</id>
    <content type="html"><![CDATA[<p>最近使用 Claude Code CLI 和 GitHub Copilot CLI 时发现，虽然两者都使用 Claude Sonnet 4.5 模型，但 Claude Code 明显更智能。本文记录性能差异的技术原因。</p>

<!--more-->

<h2 id="核心问题">核心问题</h2>

<p><strong>同模型不等于同性能</strong>。Claude Sonnet 4.5 原生支持 200K tokens 上下文和 Extended Thinking，但 Copilot CLI 通过中间层大幅限制了这些能力。</p>

<hr />

<h2 id="copilot-cli-的三大限制">Copilot CLI 的三大限制</h2>

<h3 id="1-上下文窗口严重缩水">1. 上下文窗口严重缩水</h3>

<p><strong>Claude Sonnet 4.5 原生能力</strong>：</p>
<ul>
  <li>标准：200K tokens</li>
  <li>长上下文版本：1M tokens</li>
</ul>

<p><strong>Copilot CLI 实际限制</strong>：</p>
<ul>
  <li>约 <strong>8K tokens</strong> 上下文窗口</li>
  <li>官方未明确公布，但用户实测约为此值</li>
</ul>

<p><strong>实际影响</strong>：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 场景：分析涉及 10 个文件的代码库
</span><span class="line"></span>
</span><span class="line"><span class="c1"># Claude Code CLI (200K 上下文)
</span><span class="line"># ✓ 可同时加载多个相关文件
</span><span class="line"># ✓ 保持完整的代码关系理解
</span><span class="line"># ✓ 前后一致的分析结果
</span><span class="line"></span>
</span><span class="line"><span class="c1"># Copilot CLI (8K 上下文)
</span><span class="line"># ✗ 只能保持 1-2 个文件
</span><span class="line"># ✗ 频繁遗忘先前分析内容
</span><span class="line"># ✗ 需要反复重新读取</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>8K tokens 约等于 <strong>6000 英文单词</strong> 或 <strong>1500 行代码</strong>。Copilot CLI 的小窗口导致频繁的上下文切换和信息丢失。</p>

<h3 id="2-extended-thinking-功能完全缺失">2. Extended Thinking 功能完全缺失</h3>

<p><strong>什么是 Extended Thinking</strong>：</p>

<p>允许模型进行深度推理，配置 1K-64K tokens 的”思考预算”，在复杂任务中显著提升表现。</p>

<p><strong>Claude Code CLI 配置示例</strong>：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="p">{</span><span class="w">
</span><span class="line">  </span><span class="nl">"model"</span><span class="p">:</span><span class="w"> </span><span class="s2">"claude-sonnet-4-5-20250929"</span><span class="p">,</span><span class="w">
</span><span class="line">  </span><span class="nl">"thinking"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="line">    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"enabled"</span><span class="p">,</span><span class="w">
</span><span class="line">    </span><span class="nl">"budget_tokens"</span><span class="p">:</span><span class="w"> </span><span class="mi">10000</span><span class="w">
</span><span class="line">  </span><span class="p">}</span><span class="w">
</span><span class="line"></span><span class="p">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p><strong>Copilot CLI</strong>：</p>

<p>完全不支持此配置，无法启用 Extended Thinking。这是两者智能表现差异的关键原因。</p>

<h3 id="3-资源配额与超时策略">3. 资源配额与超时策略</h3>

<p><strong>Claude Code CLI</strong>：</p>
<ul>
  <li>按 token 计费（$3/百万输入，$15/百万输出）</li>
  <li>允许长时间运行</li>
  <li>支持检查点功能，可保存进度</li>
</ul>

<p><strong>Copilot CLI</strong>：</p>
<ul>
  <li>Premium Request 配额制（Pro 300 次/月）</li>
  <li>隐性”思考预算”限制</li>
  <li>超时中断机制</li>
</ul>

<p><strong>比喻</strong>：</p>

<p>Claude Code 设计用于 <strong>跑马拉松</strong>（长时间、多步骤任务），Copilot CLI 只能 <strong>跑百米</strong>（快速交互）。</p>

<hr />

<h2 id="架构差异">架构差异</h2>

<h3 id="claude-code-cli直接访问">Claude Code CLI：直接访问</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="err">用户</span><span class="w"> </span><span class="err">→</span><span class="w"> </span><span class="err">Anthropic</span><span class="w"> </span><span class="err">API</span><span class="w"> </span><span class="err">→</span><span class="w"> </span><span class="err">Claude</span><span class="w"> </span><span class="err">Sonnet</span><span class="w"> </span><span class="mf">4.5</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p><strong>特点</strong>：</p>
<ul>
  <li>完整的 200K tokens 上下文</li>
  <li>支持 Extended Thinking</li>
  <li>完全参数控制</li>
  <li>并行工具调用</li>
  <li>无功能限制</li>
</ul>

<p><strong>代价</strong>：</p>
<ul>
  <li>使用 grep-only 检索（无语义索引）</li>
  <li>大量顺序工具调用</li>
  <li><strong>速度慢 4-5 倍</strong></li>
</ul>

<h3 id="copilot-cli中间层架构">Copilot CLI：中间层架构</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="err">用户</span><span class="w"> </span><span class="err">→</span><span class="w"> </span><span class="err">GitHub</span><span class="w"> </span><span class="err">编排层</span><span class="w"> </span><span class="err">→</span><span class="w"> </span><span class="err">Anthropic</span><span class="w"> </span><span class="err">API</span><span class="w"> </span><span class="err">→</span><span class="w"> </span><span class="err">Claude</span><span class="w"> </span><span class="err">Sonnet</span><span class="w"> </span><span class="mf">4.5</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p><strong>中间层作用</strong>：</p>
<ul>
  <li>模型路由和切换</li>
  <li>成本控制和配额管理</li>
  <li>上下文窗口限制（8K）</li>
  <li>屏蔽高级功能（Extended Thinking）</li>
  <li>GitHub 生态集成</li>
</ul>

<p><strong>优点</strong>：</p>
<ul>
  <li>多模型选择</li>
  <li>GitHub 深度集成</li>
  <li>相对稳定</li>
</ul>

<p><strong>代价</strong>：</p>
<ul>
  <li>模型能力被”阉割”</li>
  <li>上下文限制严重</li>
  <li>复杂任务表现差</li>
</ul>

<hr />

<h2 id="实测性能对比">实测性能对比</h2>

<h3 id="速度差异">速度差异</h3>

<p>重构 React 前端任务（约 15 个文件）：</p>
<ul>
  <li><strong>Claude Code CLI</strong>: 18 分 20 秒</li>
  <li><strong>Claude Chat 手动</strong>: 4 分 30 秒</li>
  <li><strong>Copilot CLI</strong>: 约 <strong>90 分钟</strong>（18 分 × 5）</li>
</ul>

<p>用户反馈：Copilot CLI 比 Claude Code <strong>慢 5 倍以上</strong>。</p>

<h3 id="大文件处理">大文件处理</h3>

<p><strong>Claude Code 限制</strong>：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line"><span class="c"># 单文件读取限制 25K tokens</span>
</span><span class="line">Error: File content <span class="o">(</span>28375 tokens<span class="o">)</span> exceeds maximum allowed tokens <span class="o">(</span>25000<span class="o">)</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>需要使用 offset 和 limit 分块读取，导致反复工具调用。</p>

<p><strong>Copilot CLI 问题</strong>：</p>
<ul>
  <li>8K tokens 窗口导致频繁分块</li>
  <li>
    <blockquote>
      <p>1000 行文件经常卡顿</p>
    </blockquote>
  </li>
  <li>有时卡死 30 分钟后超时</li>
</ul>

<hr />

<h2 id="为什么-claude-code-更智能">为什么 Claude Code “更智能”</h2>

<h3 id="1-全局视野-vs-局部视野">1. 全局视野 vs 局部视野</h3>

<p><strong>Claude Code</strong>：</p>
<ul>
  <li>200K tokens 上下文窗口</li>
  <li>可同时保持多个文件内容</li>
  <li>理解代码全局关系</li>
</ul>

<p><strong>Copilot CLI</strong>：</p>
<ul>
  <li>8K tokens 上下文窗口</li>
  <li>只能保持少量文件</li>
  <li>频繁遗忘先前内容</li>
</ul>

<h3 id="2-深度思考-vs-快速响应">2. 深度思考 vs 快速响应</h3>

<p><strong>Claude Code</strong>：</p>
<ul>
  <li>Extended Thinking 允许模型”想”得更久</li>
  <li>可以进行复杂推理</li>
  <li>适合多步骤任务</li>
</ul>

<p><strong>Copilot CLI</strong>：</p>
<ul>
  <li>无 Extended Thinking</li>
  <li>思考预算受限</li>
  <li>倾向快速给出结果</li>
</ul>

<h3 id="3-马拉松-vs-百米">3. 马拉松 vs 百米</h3>

<p><strong>Claude Code</strong>：</p>
<ul>
  <li>设计用于长时间运行</li>
  <li>可处理复杂、多步骤任务</li>
  <li>允许大量 token 消耗</li>
</ul>

<p><strong>Copilot CLI</strong>：</p>
<ul>
  <li>为快速交互优化</li>
  <li>超出一定时限就中断</li>
  <li>控制成本和资源</li>
</ul>

<hr />

<h2 id="稳定性问题">稳定性问题</h2>

<h3 id="copilot-cli">Copilot CLI</h3>

<p>GitHub 社区反馈：</p>
<ul>
  <li>Claude Sonnet 4 在约 5 个提示后停止</li>
  <li>频繁 “I’m sorry but there was an error”</li>
  <li>上下文突然丢失</li>
</ul>

<p>官方承认是”已知的服务器端问题”。</p>

<h3 id="claude-code">Claude Code</h3>

<ul>
  <li>使用 31% 配额时被提前限制</li>
  <li>陷入”无限压缩循环”</li>
  <li>读取大文件时崩溃</li>
</ul>

<hr />

<h2 id="优化建议">优化建议</h2>

<h3 id="claude-code-1">Claude Code</h3>

<p><strong>使用语义索引</strong>：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line"><span class="c"># 安装 MCP 服务器（如 Serena MCP）</span>
</span><span class="line"><span class="c"># 替代低效 grep 检索</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p><strong>主动管理上下文</strong>：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">/clear   <span class="c"># 清理上下文</span>
</span><span class="line">/compact <span class="c"># 压缩上下文</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p><strong>维护 CLAUDE.md</strong>：</p>
<ul>
  <li>项目规范</li>
  <li>禁止目录</li>
  <li>常用命令</li>
</ul>

<h3 id="copilot-cli-1">Copilot CLI</h3>

<p><strong>监控配额</strong>：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">/usage  <span class="c"># 查看使用情况</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p><strong>按任务选模型</strong>：</p>
<ul>
  <li>复杂任务：Claude Sonnet 4.5</li>
  <li>简单任务：Haiku</li>
</ul>

<p><strong>避免大任务接近限制时启动</strong></p>

<hr />

<h2 id="总结">总结</h2>

<p>两者差异的根本原因：</p>

<table>
  <thead>
    <tr>
      <th>维度</th>
      <th>Claude Code CLI</th>
      <th>Copilot CLI</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>上下文窗口</strong></td>
      <td>200K tokens</td>
      <td>~8K tokens</td>
    </tr>
    <tr>
      <td><strong>Extended Thinking</strong></td>
      <td>✓ 支持</td>
      <td>✗ 不支持</td>
    </tr>
    <tr>
      <td><strong>资源策略</strong></td>
      <td>马拉松</td>
      <td>百米</td>
    </tr>
    <tr>
      <td><strong>架构</strong></td>
      <td>直接访问</td>
      <td>中间层限制</td>
    </tr>
    <tr>
      <td><strong>适用场景</strong></td>
      <td>复杂重构</td>
      <td>快速迭代</td>
    </tr>
  </tbody>
</table>

<p><strong>Claude Code</strong> 提供完整模型能力但速度慢，像让模型”看全局、想得久”。</p>

<p><strong>Copilot CLI</strong> 功能受限但集成好，像让模型”看局部、快速答”。</p>

<p>用户反馈的”8K tokens 限制”并非误解，而是 Copilot CLI 的真实约束。这个限制加上 Extended Thinking 缺失，是智能表现差异的核心原因。</p>

<p>实际使用中，<strong>许多开发者两者并用</strong>：Claude Code 处理复杂任务，Copilot CLI 处理快速交互。</p>
<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[定位 Android 权限声明来源]]></title>
    <link href="https://droidyue.com/blog/2025/10/12/ding-wei-android-quan-xian-sheng-ming-lai-yuan/"/>
    <updated>2025-10-12T08:00:00+08:00</updated>
    <id>https://droidyue.com/blog/2025/10/12/ding-wei-android-quan-xian-sheng-ming-lai-yuan</id>
    <content type="html"><![CDATA[<p>开发中经常需要排查某个权限是由哪个依赖库引入的，本文记录通过 Gradle daemon 日志快速定位权限来源的方法。</p>

<!--more-->

<h2 id="查询方法">查询方法</h2>

<p>使用以下命令在 Gradle daemon 日志中搜索权限声明：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line"><span class="nb">grep</span> <span class="nt">-n</span> <span class="nt">-C</span> 2 <span class="s2">"android.permission.INTERNET"</span> <span class="nt">--include</span><span class="o">=</span><span class="s2">"*.out.log"</span> <span class="nt">-R</span> ~/.gradle/daemon/ .</span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="参数说明">参数说明</h3>

<ul>
  <li><code class="language-plaintext highlighter-rouge">-n</code>：显示行号</li>
  <li><code class="language-plaintext highlighter-rouge">-C 2</code>：显示匹配行前后各 2 行上下文（关于 grep 上下文参数的详细用法见<a href="https://droidyue.com/blog/2025/06/24/use-grep-with-context/">这篇文章</a>）</li>
  <li><code class="language-plaintext highlighter-rouge">--include="*.out.log"</code>：只搜索 <code class="language-plaintext highlighter-rouge">.out.log</code> 文件</li>
  <li><code class="language-plaintext highlighter-rouge">-R</code>：递归搜索目录</li>
  <li><code class="language-plaintext highlighter-rouge">~/.gradle/daemon/</code>：Gradle daemon 日志目录</li>
</ul>

<hr />

<h2 id="daemon-日志文件说明">daemon 日志文件说明</h2>

<h3 id="什么是-daemonoutlog">什么是 daemon*.out.log</h3>

<p>Gradle daemon 是 Gradle 构建系统的后台进程，用于加速构建过程。<code class="language-plaintext highlighter-rouge">daemon-*.out.log</code> 文件记录了 daemon 进程的详细输出信息，包括：</p>

<ul>
  <li><strong>依赖解析过程</strong>：库的下载、合并信息</li>
  <li><strong>Manifest 合并日志</strong>：权限、组件的合并来源</li>
  <li><strong>构建任务执行</strong>：编译、打包等任务的详细输出</li>
  <li><strong>错误堆栈信息</strong>：构建失败时的完整日志</li>
</ul>

<h3 id="文件位置">文件位置</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">~/.gradle/daemon/
</span><span class="line">├── 5.4.1/
</span><span class="line">│   ├── daemon-77407.out.log
</span><span class="line">│   └── daemon-77408.out.log
</span><span class="line">├── 7.0.2/
</span><span class="line">│   └── daemon-88901.out.log
</span><span class="line">└── ...</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>每个 Gradle 版本对应一个目录，每次 daemon 启动会生成新的日志文件，文件名中的数字为进程 ID。</p>

<hr />

<h2 id="结果分析">结果分析</h2>

<h3 id="查询结果示例">查询结果示例</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="plaintext"><span class="line">/daemon/5.4.1/daemon-77407.out.log:132336:Merging uses-permission#android.permission.INTERNET 
</span><span class="line">with lower [net.butterflytv.utils:rtmp-client:3.0.1] AndroidManifest.xml:11:5-67</span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="信息解读">信息解读</h3>

<p>从日志可以看出：</p>

<ul>
  <li><strong>权限名称</strong>：<code class="language-plaintext highlighter-rouge">android.permission.INTERNET</code></li>
  <li><strong>来源依赖</strong>：<code class="language-plaintext highlighter-rouge">net.butterflytv.utils:rtmp-client:3.0.1</code></li>
  <li><strong>声明位置</strong>：该依赖的 <code class="language-plaintext highlighter-rouge">AndroidManifest.xml</code> 第 11 行</li>
  <li><strong>日志文件</strong>：<code class="language-plaintext highlighter-rouge">daemon-77407.out.log</code> 第 132336 行</li>
</ul>

<h3 id="处理方式">处理方式</h3>

<p>使用 <code class="language-plaintext highlighter-rouge">&lt;uses-permission tools:node="remove"&gt;</code> 在主 Manifest 中显式移除。</p>

<hr />

<h2 id="注意事项">注意事项</h2>

<ul>
  <li>daemon 日志会随着构建次数增多而变大，定期清理 <code class="language-plaintext highlighter-rouge">~/.gradle/daemon/</code> 目录</li>
  <li>不同 Gradle 版本的日志格式可能略有差异</li>
  <li>查询结果可能包含多个匹配项，需根据依赖关系判断实际来源</li>
</ul>

<h2 id="延伸阅读">延伸阅读</h2>

<ul>
  <li><a href="https://docs.gradle.org/current/userguide/gradle_daemon.html">Gradle Daemon 官方文档</a></li>
  <li><a href="https://developer.android.com/studio/build/manifest-merge">Android Manifest 合并机制</a></li>
</ul>
<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Android 升级 targetSDK 35 解决 namespace 问题]]></title>
    <link href="https://droidyue.com/blog/2025/10/04/android-sheng-ji-targetsdk-35-jie-jue-namespace-wen-ti/"/>
    <updated>2025-10-04T10:00:00+08:00</updated>
    <id>https://droidyue.com/blog/2025/10/04/android-sheng-ji-targetsdk-35-jie-jue-namespace-wen-ti</id>
    <content type="html"><![CDATA[<p>升级 Android targetSDK 至 35 并使用 Gradle 8.0+ 后，遇到了第三方库 namespace 配置问题。</p>

<!--more-->

<h2 id="错误信息">错误信息</h2>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">Execution failed <span class="k">for </span>task <span class="s1">':react-native-inappbrowser:processDebugManifest'</span><span class="nb">.</span>
</span><span class="line"><span class="o">&gt;</span> A failure occurred <span class="k">while </span>executing com.android.build.gradle.tasks.ProcessLibraryManifest<span class="nv">$ProcessLibWorkAction</span>
</span><span class="line"><span class="o">&gt;</span> Setting the namespace via the package attribute <span class="k">in </span>the <span class="nb">source </span>AndroidManifest.xml is no longer supported.
</span><span class="line">  Recommendation: remove <span class="nv">package</span><span class="o">=</span><span class="s2">"com.proyecto26.inappbrowser"</span> from the <span class="nb">source </span>AndroidManifest.xml.</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>或者类似错误：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">Namespace not specified. Please specify a namespace <span class="k">in </span>the module<span class="s1">'s build.gradle file like so:
</span><span class="line">
</span><span class="line">android {
</span><span class="line">    namespace '</span>com.example.namespace<span class="s1">'
</span><span class="line">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<hr />

<h2 id="原因分析">原因分析</h2>

<p>Android Gradle Plugin 8.0+ 不再支持在 <code class="language-plaintext highlighter-rouge">AndroidManifest.xml</code> 中通过 <code class="language-plaintext highlighter-rouge">package</code> 属性设置 namespace，要求在 <code class="language-plaintext highlighter-rouge">build.gradle</code> 中显式声明。升级 targetSDK 至 35 需要使用 Gradle 8.0+，但很多第三方库（如 <code class="language-plaintext highlighter-rouge">react-native-inappbrowser</code>、<code class="language-plaintext highlighter-rouge">appcenter-analytics</code> 等）尚未更新配置，导致构建失败。</p>

<hr />

<h2 id="解决方案">解决方案</h2>

<p>在项目根目录的 <code class="language-plaintext highlighter-rouge">android/build.gradle</code> 文件中添加以下代码：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
<span class="line-number">28</span>
<span class="line-number">29</span>
<span class="line-number">30</span>
<span class="line-number">31</span>
<span class="line-number">32</span>
<span class="line-number">33</span>
<span class="line-number">34</span>
<span class="line-number">35</span>
<span class="line-number">36</span>
<span class="line-number">37</span>
<span class="line-number">38</span>
<span class="line-number">39</span>
<span class="line-number">40</span>
<span class="line-number">41</span>
<span class="line-number">42</span>
<span class="line-number">43</span>
<span class="line-number">44</span>
<span class="line-number">45</span>
<span class="line-number">46</span>
<span class="line-number">47</span>
<span class="line-number">48</span>
<span class="line-number">49</span>
<span class="line-number">50</span>
<span class="line-number">51</span>
<span class="line-number">52</span>
<span class="line-number">53</span>
<span class="line-number">54</span>
<span class="line-number">55</span>
<span class="line-number">56</span>
<span class="line-number">57</span>
<span class="line-number">58</span>
<span class="line-number">59</span>
<span class="line-number">60</span>
<span class="line-number">61</span>
<span class="line-number">62</span>
<span class="line-number">63</span>
<span class="line-number">64</span>
<span class="line-number">65</span>
<span class="line-number">66</span>
<span class="line-number">67</span>
<span class="line-number">68</span>
<span class="line-number">69</span>
</pre></td><td class="code"><pre><code class="groovy"><span class="line"><span class="n">allprojects</span> <span class="o">{</span>
</span><span class="line">    <span class="n">repositories</span> <span class="o">{</span>
</span><span class="line">        <span class="n">google</span><span class="o">()</span>
</span><span class="line">        <span class="n">mavenCentral</span><span class="o">()</span>
</span><span class="line">        
</span><span class="line">        <span class="c1">// 如果使用 Detox 测试框架，添加此配置</span>
</span><span class="line">        <span class="n">maven</span> <span class="o">{</span>
</span><span class="line">            <span class="n">url</span><span class="o">(</span><span class="s2">"$rootDir/../node_modules/detox/Detox-android"</span><span class="o">)</span>
</span><span class="line">        <span class="o">}</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line">    
</span><span class="line">    <span class="n">subprojects</span> <span class="o">{</span>
</span><span class="line">        <span class="n">afterEvaluate</span> <span class="o">{</span> <span class="n">project</span> <span class="o">-&gt;</span>
</span><span class="line">            <span class="k">if</span> <span class="o">(</span><span class="n">project</span><span class="o">.</span><span class="na">hasProperty</span><span class="o">(</span><span class="s1">'android'</span><span class="o">))</span> <span class="o">{</span>
</span><span class="line">                <span class="n">project</span><span class="o">.</span><span class="na">android</span> <span class="o">{</span>
</span><span class="line">                    <span class="c1">// 自动设置 namespace</span>
</span><span class="line">                    <span class="k">if</span> <span class="o">(</span><span class="n">namespace</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">namespace</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">())</span> <span class="o">{</span>
</span><span class="line">                        <span class="kt">def</span> <span class="n">defaultNamespace</span> <span class="o">=</span> <span class="n">project</span><span class="o">.</span><span class="na">group</span><span class="o">.</span><span class="na">toString</span><span class="o">().</span><span class="na">replace</span><span class="o">(</span><span class="s1">'.'</span><span class="o">,</span> <span class="s1">'_'</span><span class="o">)</span>
</span><span class="line">                        <span class="n">namespace</span> <span class="o">=</span> <span class="n">defaultNamespace</span>
</span><span class="line">                    <span class="o">}</span>
</span><span class="line">
</span><span class="line">                    <span class="c1">// 启用 buildConfig</span>
</span><span class="line">                    <span class="n">buildFeatures</span> <span class="o">{</span>
</span><span class="line">                        <span class="n">buildConfig</span> <span class="o">=</span> <span class="kc">true</span>
</span><span class="line">                    <span class="o">}</span>
</span><span class="line">                <span class="o">}</span>
</span><span class="line">
</span><span class="line">                <span class="c1">// 自动修复 namespace 和清理 AndroidManifest.xml</span>
</span><span class="line">                <span class="n">project</span><span class="o">.</span><span class="na">tasks</span><span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="s2">"fixManifestsAndNamespace"</span><span class="o">)</span> <span class="o">{</span>
</span><span class="line">                    <span class="n">doLast</span> <span class="o">{</span>
</span><span class="line">                        <span class="c1">// 1. 从 AndroidManifest.xml 提取 package 并添加到 build.gradle</span>
</span><span class="line">                        <span class="kt">def</span> <span class="n">buildGradleFile</span> <span class="o">=</span> <span class="n">file</span><span class="o">(</span><span class="s2">"${project.projectDir}/build.gradle"</span><span class="o">)</span>
</span><span class="line">                        <span class="k">if</span> <span class="o">(</span><span class="n">buildGradleFile</span><span class="o">.</span><span class="na">exists</span><span class="o">())</span> <span class="o">{</span>
</span><span class="line">                            <span class="kt">def</span> <span class="n">buildGradleContent</span> <span class="o">=</span> <span class="n">buildGradleFile</span><span class="o">.</span><span class="na">getText</span><span class="o">(</span><span class="s1">'UTF-8'</span><span class="o">)</span>
</span><span class="line">                            <span class="kt">def</span> <span class="n">manifestFile</span> <span class="o">=</span> <span class="n">file</span><span class="o">(</span><span class="s2">"${project.projectDir}/src/main/AndroidManifest.xml"</span><span class="o">)</span>
</span><span class="line">                            <span class="k">if</span> <span class="o">(</span><span class="n">manifestFile</span><span class="o">.</span><span class="na">exists</span><span class="o">())</span> <span class="o">{</span>
</span><span class="line">                                <span class="kt">def</span> <span class="n">manifestContent</span> <span class="o">=</span> <span class="n">manifestFile</span><span class="o">.</span><span class="na">getText</span><span class="o">(</span><span class="s1">'UTF-8'</span><span class="o">)</span>
</span><span class="line">                                <span class="kt">def</span> <span class="n">packageName</span> <span class="o">=</span> <span class="n">manifestContent</span><span class="o">.</span><span class="na">find</span><span class="o">(</span><span class="s">/package="([^"]+)"/</span><span class="o">)</span> <span class="o">{</span> <span class="n">match</span><span class="o">,</span> <span class="n">p</span> <span class="o">-&gt;</span> <span class="n">p</span> <span class="o">}</span>
</span><span class="line">                                <span class="k">if</span> <span class="o">(</span><span class="n">packageName</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="n">buildGradleContent</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="s2">"namespace"</span><span class="o">))</span> <span class="o">{</span>
</span><span class="line">                                    <span class="n">println</span> <span class="s2">"Setting namespace in ${buildGradleFile}"</span>
</span><span class="line">                                    <span class="n">buildGradleContent</span> <span class="o">=</span> <span class="n">buildGradleContent</span><span class="o">.</span><span class="na">replaceFirst</span><span class="o">(</span>
</span><span class="line">                                        <span class="s">/android\s*\{/</span><span class="o">,</span> <span class="s2">"android {\n    namespace '${packageName}'"</span>
</span><span class="line">                                    <span class="o">)</span>
</span><span class="line">                                    <span class="n">buildGradleFile</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="n">buildGradleContent</span><span class="o">,</span> <span class="s1">'UTF-8'</span><span class="o">)</span>
</span><span class="line">                                <span class="o">}</span>
</span><span class="line">                            <span class="o">}</span>
</span><span class="line">                        <span class="o">}</span>
</span><span class="line">
</span><span class="line">                        <span class="c1">// 2. 移除 AndroidManifest.xml 中的 package 属性</span>
</span><span class="line">                        <span class="kt">def</span> <span class="n">manifests</span> <span class="o">=</span> <span class="n">fileTree</span><span class="o">(</span><span class="nl">dir:</span> <span class="n">project</span><span class="o">.</span><span class="na">projectDir</span><span class="o">,</span> <span class="nl">includes:</span> <span class="o">[</span><span class="s1">'**/AndroidManifest.xml'</span><span class="o">])</span>
</span><span class="line">                        <span class="n">manifests</span><span class="o">.</span><span class="na">each</span> <span class="o">{</span> <span class="n">File</span> <span class="n">manifestFile</span> <span class="o">-&gt;</span>
</span><span class="line">                            <span class="kt">def</span> <span class="n">manifestContent</span> <span class="o">=</span> <span class="n">manifestFile</span><span class="o">.</span><span class="na">getText</span><span class="o">(</span><span class="s1">'UTF-8'</span><span class="o">)</span>
</span><span class="line">                            <span class="k">if</span> <span class="o">(</span><span class="n">manifestContent</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="s1">'package='</span><span class="o">))</span> <span class="o">{</span>
</span><span class="line">                                <span class="n">println</span> <span class="s2">"Removing package attribute from ${manifestFile}"</span>
</span><span class="line">                                <span class="n">manifestContent</span> <span class="o">=</span> <span class="n">manifestContent</span><span class="o">.</span><span class="na">replaceAll</span><span class="o">(</span><span class="s">/package="[^"]*"/</span><span class="o">,</span> <span class="s1">''</span><span class="o">)</span>
</span><span class="line">                                <span class="n">manifestFile</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="n">manifestContent</span><span class="o">,</span> <span class="s1">'UTF-8'</span><span class="o">)</span>
</span><span class="line">                            <span class="o">}</span>
</span><span class="line">                        <span class="o">}</span>
</span><span class="line">                    <span class="o">}</span>
</span><span class="line">                <span class="o">}</span>
</span><span class="line">
</span><span class="line">                <span class="c1">// 在构建前自动执行修复</span>
</span><span class="line">                <span class="n">project</span><span class="o">.</span><span class="na">tasks</span><span class="o">.</span><span class="na">matching</span> <span class="o">{</span> <span class="n">it</span><span class="o">.</span><span class="na">name</span><span class="o">.</span><span class="na">startsWith</span><span class="o">(</span><span class="s2">"preBuild"</span><span class="o">)</span> <span class="o">}.</span><span class="na">all</span> <span class="o">{</span>
</span><span class="line">                    <span class="n">dependsOn</span> <span class="n">project</span><span class="o">.</span><span class="na">tasks</span><span class="o">.</span><span class="na">named</span><span class="o">(</span><span class="s2">"fixManifestsAndNamespace"</span><span class="o">)</span>
</span><span class="line">                <span class="o">}</span>
</span><span class="line">            <span class="o">}</span>
</span><span class="line">        <span class="o">}</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line"><span class="o">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<hr />

<h2 id="说明">说明</h2>

<h3 id="工作原理">工作原理</h3>

<p>此方案包含三个层次的处理：</p>

<ol>
  <li><strong>自动设置 namespace</strong>：如果子项目未配置 namespace，自动使用 <code class="language-plaintext highlighter-rouge">project.group</code> 并将点号替换为下划线作为 namespace</li>
  <li><strong>启用 buildConfig</strong>：自动为所有子项目启用 <code class="language-plaintext highlighter-rouge">buildConfig</code> 特性</li>
  <li><strong>自动迁移配置</strong>：
    <ul>
      <li>从 <code class="language-plaintext highlighter-rouge">AndroidManifest.xml</code> 中提取 <code class="language-plaintext highlighter-rouge">package</code> 属性</li>
      <li>将其写入对应的 <code class="language-plaintext highlighter-rouge">build.gradle</code> 作为 <code class="language-plaintext highlighter-rouge">namespace</code></li>
      <li>移除 <code class="language-plaintext highlighter-rouge">AndroidManifest.xml</code> 中的 <code class="language-plaintext highlighter-rouge">package</code> 属性</li>
    </ul>
  </li>
</ol>

<p>这个 task 在每次构建前（<code class="language-plaintext highlighter-rouge">preBuild</code>）自动执行，确保所有第三方库都符合 Gradle 8.0+ 的要求。</p>

<h3 id="适用场景">适用场景</h3>

<ul>
  <li>React Native 项目升级 targetSDK 35</li>
  <li>Flutter 项目升级 targetSDK 35</li>
  <li>使用 Detox 测试框架的项目</li>
  <li>原生 Android 项目使用旧版第三方库</li>
  <li>任何遇到 “namespace not specified” 或 “package attribute not supported” 错误的场景</li>
</ul>

<h3 id="注意事项">注意事项</h3>

<ul>
  <li>此方案会<strong>自动修改</strong>第三方库的 <code class="language-plaintext highlighter-rouge">build.gradle</code> 和 <code class="language-plaintext highlighter-rouge">AndroidManifest.xml</code> 文件</li>
  <li>修改仅在 <code class="language-plaintext highlighter-rouge">node_modules</code> 中生效，不影响源码仓库</li>
  <li>建议在 CI/CD 中首次构建后检查修改是否正确</li>
  <li>如果某些库已经声明了 namespace，不会被覆盖</li>
</ul>

<hr />

<h2 id="验证">验证</h2>

<p>执行以下命令重新构建项目：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line"><span class="nb">cd </span>android
</span><span class="line">./gradlew clean
</span><span class="line">./gradlew assembleDebug</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>或在 React Native 项目中：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">npx react-native run-android</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>构建过程中会看到类似输出：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">Setting namespace <span class="k">in</span> /path/to/project/android/react-native-inappbrowser/build.gradle
</span><span class="line">Removing package attribute from /path/to/project/android/react-native-inappbrowser/src/main/AndroidManifest.xml</span></code></pre></td></tr></table></div></figure></notextile></div>

<hr />

<h2 id="与简化方案对比">与简化方案对比</h2>

<p>如果只需要为缺少 namespace 的库自动设置默认值，可以使用简化版：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
</pre></td><td class="code"><pre><code class="groovy"><span class="line"><span class="n">subprojects</span> <span class="o">{</span>
</span><span class="line">    <span class="n">afterEvaluate</span> <span class="o">{</span> <span class="n">project</span> <span class="o">-&gt;</span>
</span><span class="line">        <span class="k">if</span> <span class="o">(</span><span class="n">project</span><span class="o">.</span><span class="na">hasProperty</span><span class="o">(</span><span class="s1">'android'</span><span class="o">))</span> <span class="o">{</span>
</span><span class="line">            <span class="n">project</span><span class="o">.</span><span class="na">android</span> <span class="o">{</span>
</span><span class="line">                <span class="k">if</span> <span class="o">(</span><span class="n">namespace</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">namespace</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">())</span> <span class="o">{</span>
</span><span class="line">                    <span class="n">namespace</span> <span class="n">project</span><span class="o">.</span><span class="na">group</span><span class="o">.</span><span class="na">toString</span><span class="o">().</span><span class="na">replace</span><span class="o">(</span><span class="s1">'.'</span><span class="o">,</span> <span class="s1">'_'</span><span class="o">)</span>
</span><span class="line">                <span class="o">}</span>
</span><span class="line">            <span class="o">}</span>
</span><span class="line">        <span class="o">}</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line"><span class="o">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>简化方案不会修改任何文件，仅在内存中设置 namespace，但可能无法解决所有第三方库的问题。</p>

<hr />

<h2 id="参考">参考</h2>

<ul>
  <li><a href="https://github.com/proyecto26/react-native-inappbrowser/issues/451">GitHub Issue #451</a></li>
  <li><a href="https://stackoverflow.com/questions/76108428">Stack Overflow - namespace not specified</a></li>
  <li><a href="https://wix.github.io/Detox/docs/introduction/project-setup">Detox Project Setup</a></li>
</ul>
<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[解决 Android Studio 关闭后终端 flutter run 进程自动结束的问题]]></title>
    <link href="https://droidyue.com/blog/2025/09/28/android-studio-guan-bi-shi-bu-zi-dong-tui-chu-flutter-run-deng-jin-cheng-de-jie-jue-fang-an/"/>
    <updated>2025-09-28T08:56:00+08:00</updated>
    <id>https://droidyue.com/blog/2025/09/28/android-studio-guan-bi-shi-bu-zi-dong-tui-chu-flutter-run-deng-jin-cheng-de-jie-jue-fang-an</id>
    <content type="html"><![CDATA[<p>在 Flutter 开发过程中，很多开发者遇到一个困扰的问题：当使用终端运行 <code class="language-plaintext highlighter-rouge">flutter run</code> 命令进行开发时，一旦关闭 Android Studio 或 IntelliJ IDEA，终端中的 <code class="language-plaintext highlighter-rouge">flutter run</code> 进程就会自动结束，导致应用停止运行。本文将详细分析这个问题的原因并提供解决方案。</p>

<!--more-->

<h2 id="问题现象">问题现象</h2>

<h3 id="典型场景">典型场景</h3>
<ol>
  <li>在终端中执行 <code class="language-plaintext highlighter-rouge">flutter run</code> 启动 Flutter 应用</li>
  <li>同时打开 Android Studio 进行代码编辑</li>
  <li>关闭 Android Studio 或 IntelliJ IDEA</li>
  <li>终端中的 <code class="language-plaintext highlighter-rouge">flutter run</code> 进程自动结束，应用停止运行</li>
</ol>

<h3 id="影响范围">影响范围</h3>
<ul>
  <li>通过终端启动的 <code class="language-plaintext highlighter-rouge">flutter run</code> 进程</li>
  <li>相关的热重载功能失效</li>
  <li>调试连接中断</li>
  <li>需要重新启动应用才能继续开发</li>
</ul>

<h2 id="问题原因分析">问题原因分析</h2>

<h3 id="根本原因">根本原因</h3>

<p>当 Android Studio 启动时，它会自动管理 ADB（Android Debug Bridge）服务器的生命周期。默认情况下，IDE 会：</p>

<ol>
  <li><strong>启动自己的 ADB 服务器实例</strong></li>
  <li><strong>接管现有的调试连接</strong></li>
  <li><strong>在退出时终止所有相关的调试进程</strong></li>
</ol>

<p>这种设计导致即使是通过终端独立启动的 <code class="language-plaintext highlighter-rouge">flutter run</code> 进程，也会因为 ADB 服务器的关闭而被迫结束。</p>

<h3 id="进程依赖关系">进程依赖关系</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class=""><span class="line">终端 flutter run → ADB 连接 → Android Studio 管理的 ADB 服务器</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>当 Android Studio 关闭时，它管理的 ADB 服务器也会关闭，进而导致所有依赖该 ADB 连接的进程（包括终端的 <code class="language-plaintext highlighter-rouge">flutter run</code>）都被终止。</p>

<h2 id="解决方案">解决方案</h2>

<h3 id="配置外部-adb-服务器管理">配置外部 ADB 服务器管理</h3>

<p>最有效的解决方案是让 Android Studio 使用外部手动管理的 ADB 服务器，而不是自己管理一个实例：</p>

<h4 id="配置步骤">配置步骤</h4>

<ol>
  <li>打开 Android Studio 设置（Preferences/Settings）</li>
  <li>导航到 <code class="language-plaintext highlighter-rouge">Build, Execution, Deployment</code> → <code class="language-plaintext highlighter-rouge">Debugger</code></li>
  <li>找到 <code class="language-plaintext highlighter-rouge">Android Debug Bridge (adb)</code> 部分</li>
  <li>在 <code class="language-plaintext highlighter-rouge">Adb Server Lifecycle Management</code> 中选择 <code class="language-plaintext highlighter-rouge">Use existing manually managed server</code></li>
  <li>设置 <code class="language-plaintext highlighter-rouge">Existing ADB server port</code> 为 <code class="language-plaintext highlighter-rouge">5037</code>（默认端口）</li>
</ol>

<p><img src="https://asset.droidyue.com/image/25_h2/android_studido_adb.png" alt="android studio adb config" /></p>

<h4 id="关键配置说明">关键配置说明</h4>

<ul>
  <li><strong>Use existing manually managed server</strong>: 告诉 Android Studio 不要自己管理 ADB 服务器，而是使用外部已存在的服务器</li>
  <li><strong>Existing ADB server port</strong>: 指定外部 ADB 服务器的端口（通常为 5037）</li>
</ul>

<p>这样配置后，Android Studio 不会在启动时接管 ADB 服务器，也不会在关闭时终止它，从而保证终端运行的进程不受影响。</p>

<h2 id="验证配置是否生效">验证配置是否生效</h2>

<p>配置完成后，可以通过以下步骤验证：</p>

<ol>
  <li>在终端启动 <code class="language-plaintext highlighter-rouge">flutter run</code></li>
  <li>打开 Android Studio</li>
  <li>关闭 Android Studio</li>
  <li>检查终端中的 <code class="language-plaintext highlighter-rouge">flutter run</code> 是否依然运行</li>
</ol>

<p>如果 <code class="language-plaintext highlighter-rouge">flutter run</code> 进程没有被终止，说明配置成功。</p>

<h2 id="总结">总结</h2>

<p>通过配置 Android Studio 使用外部手动管理的 ADB 服务器，可以有效解决 IDE 关闭后终端 <code class="language-plaintext highlighter-rouge">flutter run</code> 进程自动结束的问题。这种方法的优势在于：</p>

<ol>
  <li><strong>进程独立性</strong>：终端和 IDE 的调试进程相互独立</li>
  <li><strong>开发效率</strong>：无需频繁重启应用</li>
  <li><strong>资源优化</strong>：避免不必要的进程重启</li>
  <li><strong>稳定性</strong>：减少因 IDE 操作导致的调试中断</li>
</ol>

<p>推荐所有 Flutter 开发者采用这种配置方式，特别是那些习惯在终端中运行 <code class="language-plaintext highlighter-rouge">flutter run</code> 的开发者。</p>
<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[使用 grep 查找关键字并显示上下文行]]></title>
    <link href="https://droidyue.com/blog/2025/06/24/use-grep-with-context/"/>
    <updated>2025-06-24T10:30:00+08:00</updated>
    <id>https://droidyue.com/blog/2025/06/24/use-grep-with-context</id>
    <content type="html"><![CDATA[<h2 id="背景">背景</h2>

<p>排查日志时，常需要定位关键字并带上一两行上下文确认语义。<code class="language-plaintext highlighter-rouge">grep</code> 内建的上下文选项可以直接满足需求，不必再手动 <code class="language-plaintext highlighter-rouge">sed -n '19,21p'</code>。</p>

<!--more-->

<h2 id="快速示例">快速示例</h2>

<p>假设想在 <code class="language-plaintext highlighter-rouge">app.log</code> 中找出包含 <code class="language-plaintext highlighter-rouge">Fatal error</code> 的行，并且同时看到上一行与下一行：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line"><span class="nb">grep</span> <span class="nt">-n</span> <span class="nt">-C</span> 1 <span class="s2">"Fatal error"</span> app.log</span></code></pre></td></tr></table></div></figure></notextile></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">-n</code> 会显示行号，便于定位。</li>
  <li><code class="language-plaintext highlighter-rouge">-C 1</code> 等价于 <code class="language-plaintext highlighter-rouge">--context=1</code>，表示向前向后各多带 1 行。想多看几行时调整数字即可。</li>
</ul>

<p>输出中，命中的行以冒号分隔行号与内容，上下文行则以短横线 <code class="language-plaintext highlighter-rouge">-</code> 连接，快速区分重点。</p>

<h2 id="控制上下文范围">控制上下文范围</h2>

<p><code class="language-plaintext highlighter-rouge">grep</code> 提供三个粒度化参数：</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">-C &lt;N&gt;</code>：两侧各 N 行，是最常用的形式。</li>
  <li><code class="language-plaintext highlighter-rouge">-B &lt;N&gt;</code>：只带前 N 行（Before）。</li>
  <li><code class="language-plaintext highlighter-rouge">-A &lt;N&gt;</code>：只带后 N 行（After）。</li>
</ul>

<p>例如只关心关键字后面的调用栈，可使用：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line"><span class="nb">grep</span> <span class="nt">-n</span> <span class="nt">-A</span> 4 <span class="s2">"NullPointerException"</span> stacktrace.txt</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>再配合 <code class="language-plaintext highlighter-rouge">-m 1</code>（匹配一次后退出）可以缩短复杂日志的搜索时间。</p>

<h2 id="与常见参数组合">与常见参数组合</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">-i</code>：忽略大小写，处理大小写不一致的告警信息很方便。</li>
  <li><code class="language-plaintext highlighter-rouge">-E</code>：启用扩展正则，可直接写 <code class="language-plaintext highlighter-rouge">grep -E "(WARN|ERROR)"</code>。</li>
  <li><code class="language-plaintext highlighter-rouge">--color=auto</code>：高亮命中关键字，在终端阅读更直观。</li>
</ul>

<p>将这些参数组合成 Shell 函数，后续排查直接调用。例如在 <code class="language-plaintext highlighter-rouge">~/.bashrc</code> 中定义：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">gctx<span class="o">()</span> <span class="o">{</span>
</span><span class="line">  <span class="nb">local </span><span class="nv">keyword</span><span class="o">=</span><span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="nv">file</span><span class="o">=</span><span class="s2">"</span><span class="nv">$2</span><span class="s2">"</span> <span class="nv">lines</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">3</span><span class="k">:-</span><span class="nv">1</span><span class="k">}</span><span class="s2">"</span>
</span><span class="line">  <span class="nb">grep</span> <span class="nt">-n</span> <span class="nt">--color</span><span class="o">=</span>always <span class="nt">-C</span> <span class="s2">"</span><span class="nv">$lines</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$keyword</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$file</span><span class="s2">"</span>
</span><span class="line"><span class="o">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>执行 <code class="language-plaintext highlighter-rouge">gctx "timeout" service.log 2</code>，即可得到行号、关键字高亮、上下文行的结果。</p>

<h2 id="小结">小结</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">-C/-A/-B</code> 是获取上下文的核心选项，记住数字表示行数即可。</li>
  <li>搭配 <code class="language-plaintext highlighter-rouge">-n</code>、<code class="language-plaintext highlighter-rouge">--color</code>、<code class="language-plaintext highlighter-rouge">-m</code> 等参数可以提升排查效率。</li>
  <li>如果命中结果过多，将命令与 <code class="language-plaintext highlighter-rouge">less -R</code> 或 <code class="language-plaintext highlighter-rouge">fzf</code> 管道组合，能够在终端中进行二次筛选，让排查体验更顺滑。</li>
</ul>
<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Android 开发中的三个常见构建错误及解决方案]]></title>
    <link href="https://droidyue.com/blog/2025/06/23/fix-three-build-error-in-android/"/>
    <updated>2025-06-23T08:33:00+08:00</updated>
    <id>https://droidyue.com/blog/2025/06/23/fix-three-build-error-in-android</id>
    <content type="html"><![CDATA[<p>最近在 Android 项目开发中遇到了几个构建错误，以下是解决方案，供遇到同样问题的开发者参考。</p>

<h2 id="1-meta-inf-文件冲突">1. META-INF 文件冲突</h2>

<h3 id="错误信息">错误信息</h3>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">FAILURE: Build failed with an exception.
</span><span class="line"><span class="k">*</span> What went wrong:
</span><span class="line">Execution failed <span class="k">for </span>task <span class="s1">':app:mergeDebugJavaResource'</span><span class="nb">.</span>
</span><span class="line"><span class="o">&gt;</span> A failure occurred <span class="k">while </span>executing com.android.build.gradle.internal.tasks.MergeJavaResWorkAction
</span><span class="line">   <span class="o">&gt;</span> 2 files found with path <span class="s1">'META-INF/versions/9/OSGI-INF/MANIFEST.MF'</span> from inputs:</span></code></pre></td></tr></table></div></figure></notextile></div>
<!--more-->

<h3 id="解决方案">解决方案</h3>
<p>在 <code class="language-plaintext highlighter-rouge">app/build.gradle</code> 中添加以下配置：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">android <span class="o">{</span>
</span><span class="line">    packagingOptions <span class="o">{</span>
</span><span class="line">        resources <span class="o">{</span>
</span><span class="line">            excludes +<span class="o">=</span> <span class="s2">"META-INF/versions/9/OSGI-INF/MANIFEST.MF"</span>
</span><span class="line">        <span class="o">}</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line"><span class="o">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="说明">说明</h3>
<p>此错误通常由多个依赖包含相同的 META-INF 文件引起，通过 <code class="language-plaintext highlighter-rouge">excludes</code> 排除重复文件即可解决。</p>

<hr />

<h2 id="2-tensorflow-lite-库冲突">2. TensorFlow Lite 库冲突</h2>

<h3 id="错误信息-1">错误信息</h3>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">Caused by: java.lang.RuntimeException: Duplicate class org.tensorflow.lite.DataType found <span class="k">in
</span><span class="line"></span>modules jetified-litert-api-1.0.1-runtime <span class="o">(</span>com.google.ai.edge.litert:litert-api:1.0.1<span class="o">)</span> and
</span><span class="line">jetified-tensorflow-lite-api-2.12.0-runtime <span class="o">(</span>org.tensorflow:tensorflow-lite-api:2.12.0<span class="o">)</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="解决方案-1">解决方案</h3>
<p>在 <code class="language-plaintext highlighter-rouge">app/build.gradle</code> 中添加依赖替换规则：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">configurations.all <span class="o">{</span>
</span><span class="line">    resolutionStrategy.dependencySubstitution <span class="o">{</span>
</span><span class="line">        substitute module<span class="o">(</span><span class="s2">"org.tensorflow:tensorflow-lite"</span><span class="o">)</span> with module<span class="o">(</span><span class="s2">"com.google.ai.edge.litert:litert:1.0.1"</span><span class="o">)</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line"><span class="o">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="说明-1">说明</h3>
<p>Google 将 TensorFlow Lite 迁移到新包名 <code class="language-plaintext highlighter-rouge">com.google.ai.edge.litert</code>，若项目同时包含新旧包名，会导致类冲突。通过依赖替换强制使用新包解决。</p>

<hr />

<h2 id="3-jetifier-与-bouncycastle-兼容性问题">3. Jetifier 与 BouncyCastle 兼容性问题</h2>

<h3 id="错误信息-2">错误信息</h3>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">Caused by: java.lang.RuntimeException: Failed to transform
</span><span class="line"><span class="s1">'/Users/xxxxx/.gradle/caches/modules-2/files-2.1/org.bouncycastle/bcprov-jdk18on/1.78/619aafb92dc0b4c6c
</span><span class="line">c4cf86c487ca48ee2d67a8e/bcprov-jdk18on-1.78.jar'</span> using Jetifier. 
</span><span class="line">Reason: IllegalArgumentException, message: Unsupported class file major version 65.</span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="解决方案-2">解决方案</h3>
<p>在项目根目录的 <code class="language-plaintext highlighter-rouge">android/gradle.properties</code> 文件中添加：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">android.jetifier.ignorelist<span class="o">=</span>bcprov-jdk18on-1.78.jar,bcutil-jdk18on-1.78.jar</span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="说明-2">说明</h3>
<p>BouncyCastle 1.78 版本使用 Java 21 编译（class file major version 65），而 Jetifier 不支持此版本字节码。将相关 jar 包加入 Jetifier 忽略列表可避免转换错误。</p>

<hr />

<h2 id="总结">总结</h2>
<p>以上三个问题是 Android 构建中常见的依赖冲突问题，解决思路包括：</p>
<ul>
  <li>排除重复文件</li>
  <li>替换冲突依赖</li>
  <li>跳过不兼容的处理</li>
</ul>

<p>遇到类似问题时，仔细分析错误信息，通常能找到相应解决方案。</p>
<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[使用 flock 解决 Git `unable to read tree` 问题]]></title>
    <link href="https://droidyue.com/blog/2025/06/15/use-flock-to-resolve-git-concurrent-operation-issues/"/>
    <updated>2025-06-15T08:49:00+08:00</updated>
    <id>https://droidyue.com/blog/2025/06/15/use-flock-to-resolve-git-concurrent-operation-issues</id>
    <content type="html"><![CDATA[<h2 id="背景">背景</h2>

<p>在 CI/CD 环境下，团队常遇到以下错误：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class=""><span class="line">fatal: unable to read tree &lt;SHA&gt;</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>这通常是多个进程或脚本并发操作同一个 Git 仓库，导致元数据损坏或锁冲突。Git 并非为高并发本地操作设计，因此需要解决并发问题。</p>

<!--more-->

<h2 id="问题复现">问题复现</h2>

<p>在自动化脚本中，例如：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">git fetch origin
</span><span class="line">git checkout some-branch</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>如果多个任务同时执行，可能导致锁冲突或元数据损坏。</p>

<h2 id="解决思路">解决思路</h2>

<p>通过加锁机制，让所有 Git 操作串行执行。<code class="language-plaintext highlighter-rouge">flock</code> 是一个简单高效的工具，专为这种场景设计。</p>

<h2 id="flock-安装">flock 安装</h2>

<h3 id="linux">Linux</h3>

<p>大多数 Linux 发行版自带 <code class="language-plaintext highlighter-rouge">flock</code>（属于 <code class="language-plaintext highlighter-rouge">util-linux</code> 套件）。如果没有，可按以下方式安装：</p>

<ul>
  <li><strong>Debian/Ubuntu</strong>:</li>
</ul>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line"><span class="nb">sudo </span>apt-get update
</span><span class="line"><span class="nb">sudo </span>apt-get <span class="nb">install </span>util-linux</span></code></pre></td></tr></table></div></figure></notextile></div>

<ul>
  <li><strong>CentOS/RHEL</strong>:</li>
</ul>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line"><span class="nb">sudo </span>yum <span class="nb">install </span>util-linux</span></code></pre></td></tr></table></div></figure></notextile></div>

<ul>
  <li><strong>Arch</strong>:</li>
</ul>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line"><span class="nb">sudo </span>pacman <span class="nt">-S</span> util-linux</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>安装后即可使用 <code class="language-plaintext highlighter-rouge">flock</code> 命令。</p>

<h3 id="macos">macOS</h3>

<p>macOS 默认不包含 <code class="language-plaintext highlighter-rouge">flock</code>，但可通过 Homebrew 安装兼容版本：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">brew <span class="nb">install </span>flock</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>安装的是 Ben Noordhuis 的 <code class="language-plaintext highlighter-rouge">flock</code>，语法与 Linux 版本基本一致。</p>

<p><strong>提示</strong>：在 CI 服务（如 GitHub Actions）中，可在步骤中提前安装 <code class="language-plaintext highlighter-rouge">flock</code>。</p>

<h2 id="flock-用法">flock 用法</h2>

<p><code class="language-plaintext highlighter-rouge">flock</code> 用于在 shell 脚本中对文件加锁：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">flock &lt;lockfile&gt; &lt;<span class="nb">command</span><span class="o">&gt;</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>建议将锁文件放在 <code class="language-plaintext highlighter-rouge">.git</code> 目录下，避免污染业务代码目录。</p>

<h2 id="实战例子">实战例子</h2>

<p>假设有一个 <code class="language-plaintext highlighter-rouge">deploy.sh</code> 脚本：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line"><span class="c">#!/bin/bash</span>
</span><span class="line">git fetch origin
</span><span class="line">git checkout some-branch
</span><span class="line"><span class="c"># ...more commands...</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>加锁后修改为：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line"><span class="c">#!/bin/bash</span>
</span><span class="line"><span class="nv">LOCK_FILE</span><span class="o">=</span><span class="s2">"/path/to/your/repo/.git/deploy.lock"</span>
</span><span class="line">
</span><span class="line">flock <span class="nt">-n</span> <span class="s2">"</span><span class="nv">$LOCK_FILE</span><span class="s2">"</span> bash <span class="o">&lt;&lt;</span><span class="sh">'</span><span class="no">EOF</span><span class="sh">'
</span><span class="line">git fetch origin
</span><span class="line">git checkout some-branch
</span><span class="line"># ...more commands...
</span><span class="line">EOF</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>或者直接锁定整个脚本：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">flock <span class="nt">-n</span> /path/to/your/repo/.git/deploy.lock ./deploy.sh</span></code></pre></td></tr></table></div></figure></notextile></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">-n</code>：表示拿不到锁时立即退出（可选）。</li>
  <li>建议将锁文件放在 <code class="language-plaintext highlighter-rouge">.git</code> 目录下。</li>
</ul>

<h2 id="总结">总结</h2>

<ul>
  <li>避免并发操作同一个 Git 仓库！</li>
  <li>使用 <code class="language-plaintext highlighter-rouge">flock</code> 使 Git 操作串行，防止元数据损坏。</li>
  <li>Linux 下直接使用，macOS 通过 Homebrew 安装 <code class="language-plaintext highlighter-rouge">flock</code>。</li>
  <li>锁粒度可适当放宽，确保安全优先。</li>
  <li>本地自动化操作 Git 时，<code class="language-plaintext highlighter-rouge">flock</code> 是必备工具，简单高效！</li>
</ul>

<p>如有问题，请在评论区讨论。</p>
<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[幂等性的劣化：从数学确定性到 AI 不确定性]]></title>
    <link href="https://droidyue.com/blog/2024/12/21/mi-deng-xing-de-lie-hua-cong-shu-xue-que-ding-xing-dao-ai-bu-que-ding-xing/"/>
    <updated>2024-12-21T09:30:00+08:00</updated>
    <id>https://droidyue.com/blog/2024/12/21/mi-deng-xing-de-lie-hua-cong-shu-xue-que-ding-xing-dao-ai-bu-que-ding-xing</id>
    <content type="html"><![CDATA[<p>计算机科学正在经历一场微妙但深刻的转变：<strong>幂等性的逐步劣化</strong>。从数学的纯粹确定性,到编程中的纯函数,再到面向对象的状态管理,直至今天的 AI 提示工程,我们在用可控性换取表达力,用确定性换取灵活性。</p>

<!--more-->

<h2 id="数学的完美世界">数学的完美世界</h2>

<h3 id="纯粹的幂等性">纯粹的幂等性</h3>

<p>在数学世界里,幂等性达到了最完美形式:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="o">=</span> <span class="n">x</span><span class="err">²</span>
</span><span class="line"><span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="o">=</span> <span class="mi">9</span>  <span class="c1"># 永远是 9,在任何时空、任何计算环境</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>数学函数的特性:</p>

<ul>
  <li><strong>绝对确定性</strong>: 相同输入永远产生相同输出</li>
  <li><strong>无副作用</strong>: 不依赖外部状态,不改变外部状态</li>
  <li><strong>可组合性</strong>: 函数可以无限组合而不影响确定性</li>
  <li><strong>时空无关</strong>: 结果不受时间、地点、环境影响</li>
</ul>

<p>这种纯粹性源于数学的本质——一个封闭的逻辑系统,完全由公理和推理规则构成。</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 数学式的完美幂等
</span><span class="line"></span><span class="err">∀</span><span class="n">x</span> <span class="err">∈</span> <span class="n">ℝ</span><span class="p">:</span> <span class="n">sin</span><span class="p">(</span><span class="n">π</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span> <span class="o">=</span> <span class="mi">1</span>
</span><span class="line"><span class="c1"># 无论计算多少次,在什么时候计算,结果都是 1</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<hr />

<h2 id="编程中的纯函数妥协">编程中的纯函数妥协</h2>

<h3 id="理想与现实的第一次碰撞">理想与现实的第一次碰撞</h3>

<p>当数学思想进入编程世界,我们遇到了第一个挑战:<strong>物理计算机的限制</strong>。</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># Python 中的纯函数尝试
</span><span class="line"></span><span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
</span><span class="line">    <span class="k">return</span> <span class="n">a</span> <span class="o">+</span> <span class="n">b</span>
</span><span class="line">
</span><span class="line"><span class="c1"># 看起来很纯粹,但实际上...
</span><span class="line"></span><span class="k">print</span><span class="p">(</span><span class="n">add</span><span class="p">(</span><span class="mf">0.1</span><span class="p">,</span> <span class="mf">0.2</span><span class="p">))</span>  <span class="c1"># 0.30000000000000004,不是精确的 0.3</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="妥协-1-浮点数精度">妥协 1: 浮点数精度</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 数学: 0.1 + 0.2 = 0.3
</span><span class="line"># 编程:
</span><span class="line"></span><span class="n">result</span> <span class="o">=</span> <span class="mf">0.1</span> <span class="o">+</span> <span class="mf">0.2</span>
</span><span class="line"><span class="k">print</span><span class="p">(</span><span class="n">result</span> <span class="o">==</span> <span class="mf">0.3</span><span class="p">)</span>  <span class="c1"># False!
</span><span class="line"></span><span class="k">print</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>         <span class="c1"># 0.30000000000000004</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="妥协-2-计算资源限制">妥协 2: 计算资源限制</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 数学中的递归是无限的
</span><span class="line"># 编程中必须考虑栈溢出
</span><span class="line"></span><span class="k">def</span> <span class="nf">fibonacci</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
</span><span class="line">    <span class="k">if</span> <span class="n">n</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">:</span>
</span><span class="line">        <span class="k">return</span> <span class="n">n</span>
</span><span class="line">    <span class="k">return</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">fibonacci</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span>
</span><span class="line">
</span><span class="line"><span class="c1"># fibonacci(100000) → RecursionError: maximum recursion depth exceeded</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="妥协-3-io-的不可避免">妥协 3: I/O 的不可避免</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 纯函数无法真正与外界交互
</span><span class="line"></span><span class="k">def</span> <span class="nf">read_file</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
</span><span class="line">    <span class="c1"># 文件内容可能变化 → 非幂等
</span><span class="line"></span>    <span class="c1"># 文件可能不存在 → 异常处理
</span><span class="line"></span>    <span class="c1"># 磁盘可能损坏 → 外部状态依赖
</span><span class="line"></span>    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s">'r'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
</span><span class="line">        <span class="k">return</span> <span class="n">f</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
</span><span class="line">
</span><span class="line"><span class="c1"># 同样的调用,可能返回不同内容
</span><span class="line"></span><span class="n">content1</span> <span class="o">=</span> <span class="n">read_file</span><span class="p">(</span><span class="s">'data.txt'</span><span class="p">)</span>  <span class="c1"># 返回 "Hello"
</span><span class="line"># 此时文件被修改
</span><span class="line"></span><span class="n">content2</span> <span class="o">=</span> <span class="n">read_file</span><span class="p">(</span><span class="s">'data.txt'</span><span class="p">)</span>  <span class="c1"># 返回 "World"</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="妥协-4-时间依赖">妥协 4: 时间依赖</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
</span><span class="line">
</span><span class="line"><span class="k">def</span> <span class="nf">get_current_hour</span><span class="p">():</span>
</span><span class="line">    <span class="k">return</span> <span class="n">datetime</span><span class="p">.</span><span class="n">now</span><span class="p">().</span><span class="n">hour</span>
</span><span class="line">
</span><span class="line"><span class="c1"># 同样的函数,不同时刻调用,不同结果
</span><span class="line"></span><span class="k">print</span><span class="p">(</span><span class="n">get_current_hour</span><span class="p">())</span>  <span class="c1"># 14
</span><span class="line"># 一小时后
</span><span class="line"></span><span class="k">print</span><span class="p">(</span><span class="n">get_current_hour</span><span class="p">())</span>  <span class="c1"># 15</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="函数式编程的防御战">函数式编程的防御战</h3>

<p>函数式编程社区试图守住最后的阵地:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
</pre></td><td class="code"><pre><code class="haskell"><span class="line"><span class="c1">-- Haskell 通过类型系统隔离副作用</span>
</span><span class="line"><span class="n">pureFunction</span> <span class="o">::</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="kt">Int</span>
</span><span class="line"><span class="n">pureFunction</span> <span class="n">x</span> <span class="o">=</span> <span class="n">x</span> <span class="o">*</span> <span class="mi">2</span>
</span><span class="line">
</span><span class="line"><span class="n">impureFunction</span> <span class="o">::</span> <span class="kt">Int</span> <span class="o">-&gt;</span> <span class="kt">IO</span> <span class="kt">Int</span>
</span><span class="line"><span class="n">impureFunction</span> <span class="n">x</span> <span class="o">=</span> <span class="kr">do</span>
</span><span class="line">  <span class="n">currentTime</span> <span class="o">&lt;-</span> <span class="n">getCurrentTime</span>
</span><span class="line">  <span class="n">return</span> <span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">timeToInt</span> <span class="n">currentTime</span><span class="p">)</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>但现实是,纯函数的比例在实际应用中不断减少。</p>

<hr />

<h2 id="oop-的状态爆炸">OOP 的状态爆炸</h2>

<h3 id="幂等性的加速崩溃">幂等性的加速崩溃</h3>

<p>面向对象编程的引入,彻底改变了游戏规则。它带来了<strong>封装、继承、多态</strong>,但也带来了<strong>状态的无处不在</strong>。</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="k">class</span> <span class="nc">Counter</span><span class="p">:</span>
</span><span class="line">    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class="line">        <span class="bp">self</span><span class="p">.</span><span class="n">_value</span> <span class="o">=</span> <span class="mi">0</span>
</span><span class="line">    
</span><span class="line">    <span class="k">def</span> <span class="nf">increment</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class="line">        <span class="bp">self</span><span class="p">.</span><span class="n">_value</span> <span class="o">+=</span> <span class="mi">1</span>
</span><span class="line">        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">_value</span>
</span><span class="line">
</span><span class="line"><span class="c1"># 使用示例
</span><span class="line"></span><span class="n">counter</span> <span class="o">=</span> <span class="n">Counter</span><span class="p">()</span>
</span><span class="line"><span class="k">print</span><span class="p">(</span><span class="n">counter</span><span class="p">.</span><span class="n">increment</span><span class="p">())</span>  <span class="c1"># 1
</span><span class="line"></span><span class="k">print</span><span class="p">(</span><span class="n">counter</span><span class="p">.</span><span class="n">increment</span><span class="p">())</span>  <span class="c1"># 2
</span><span class="line"></span><span class="k">print</span><span class="p">(</span><span class="n">counter</span><span class="p">.</span><span class="n">increment</span><span class="p">())</span>  <span class="c1"># 3
</span><span class="line"># 完全非幂等!同样的方法调用,不同的结果</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="对象内部状态">对象内部状态</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">UserSession</span> <span class="o">{</span>
</span><span class="line">    <span class="kd">private</span> <span class="nc">String</span> <span class="n">token</span><span class="o">;</span>
</span><span class="line">    <span class="kd">private</span> <span class="nc">LocalDateTime</span> <span class="n">lastActivity</span><span class="o">;</span>
</span><span class="line">    
</span><span class="line">    <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">isValid</span><span class="o">()</span> <span class="o">{</span>
</span><span class="line">        <span class="c1">// 结果依赖于内部状态</span>
</span><span class="line">        <span class="k">if</span> <span class="o">(</span><span class="n">token</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">lastActivity</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span><span class="line">            <span class="k">return</span> <span class="kc">false</span><span class="o">;</span>
</span><span class="line">        <span class="o">}</span>
</span><span class="line">        
</span><span class="line">        <span class="nc">LocalDateTime</span> <span class="n">now</span> <span class="o">=</span> <span class="nc">LocalDateTime</span><span class="o">.</span><span class="na">now</span><span class="o">();</span>
</span><span class="line">        <span class="nc">Duration</span> <span class="n">diff</span> <span class="o">=</span> <span class="nc">Duration</span><span class="o">.</span><span class="na">between</span><span class="o">(</span><span class="n">lastActivity</span><span class="o">,</span> <span class="n">now</span><span class="o">);</span>
</span><span class="line">        <span class="k">return</span> <span class="n">diff</span><span class="o">.</span><span class="na">toMinutes</span><span class="o">()</span> <span class="o">&lt;</span> <span class="mi">30</span><span class="o">;</span>  
</span><span class="line">        <span class="c1">// 同样的对象,不同时刻,不同结果</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line"><span class="o">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="全局状态污染">全局状态污染</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
<span class="line-number">28</span>
<span class="line-number">29</span>
<span class="line-number">30</span>
<span class="line-number">31</span>
<span class="line-number">32</span>
<span class="line-number">33</span>
<span class="line-number">34</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="c1">// 全局配置对象(单例模式)</span>
</span><span class="line"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">AppConfig</span> <span class="o">{</span>
</span><span class="line">    <span class="kd">private</span> <span class="kd">static</span> <span class="nc">AppConfig</span> <span class="n">instance</span><span class="o">;</span>
</span><span class="line">    <span class="kd">private</span> <span class="kt">boolean</span> <span class="n">debugMode</span><span class="o">;</span>
</span><span class="line">    <span class="kd">private</span> <span class="nc">User</span> <span class="n">currentUser</span><span class="o">;</span>
</span><span class="line">    
</span><span class="line">    <span class="kd">private</span> <span class="nf">AppConfig</span><span class="o">()</span> <span class="o">{</span>
</span><span class="line">        <span class="k">this</span><span class="o">.</span><span class="na">debugMode</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
</span><span class="line">        <span class="k">this</span><span class="o">.</span><span class="na">currentUser</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line">    
</span><span class="line">    <span class="kd">public</span> <span class="kd">static</span> <span class="nc">AppConfig</span> <span class="nf">getInstance</span><span class="o">()</span> <span class="o">{</span>
</span><span class="line">        <span class="k">if</span> <span class="o">(</span><span class="n">instance</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span><span class="line">            <span class="n">instance</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AppConfig</span><span class="o">();</span>
</span><span class="line">        <span class="o">}</span>
</span><span class="line">        <span class="k">return</span> <span class="n">instance</span><span class="o">;</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line">    
</span><span class="line">    <span class="kd">public</span> <span class="nc">User</span> <span class="nf">getCurrentUser</span><span class="o">()</span> <span class="o">{</span>
</span><span class="line">        <span class="k">return</span> <span class="n">currentUser</span><span class="o">;</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line">    
</span><span class="line">    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">setCurrentUser</span><span class="o">(</span><span class="nc">User</span> <span class="n">user</span><span class="o">)</span> <span class="o">{</span>
</span><span class="line">        <span class="k">this</span><span class="o">.</span><span class="na">currentUser</span> <span class="o">=</span> <span class="n">user</span><span class="o">;</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line"><span class="o">}</span>
</span><span class="line">
</span><span class="line"><span class="c1">// 任何方法访问这些状态都变成非幂等</span>
</span><span class="line"><span class="kd">public</span> <span class="nc">String</span> <span class="nf">getGreeting</span><span class="o">()</span> <span class="o">{</span>
</span><span class="line">    <span class="nc">AppConfig</span> <span class="n">config</span> <span class="o">=</span> <span class="nc">AppConfig</span><span class="o">.</span><span class="na">getInstance</span><span class="o">();</span>
</span><span class="line">    <span class="nc">User</span> <span class="n">user</span> <span class="o">=</span> <span class="n">config</span><span class="o">.</span><span class="na">getCurrentUser</span><span class="o">();</span>
</span><span class="line">    <span class="k">return</span> <span class="n">user</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">?</span> <span class="s">"Hello, "</span> <span class="o">+</span> <span class="n">user</span><span class="o">.</span><span class="na">getName</span><span class="o">()</span> <span class="o">:</span> <span class="s">"Hello, Guest"</span><span class="o">;</span>
</span><span class="line">    <span class="c1">// 结果依赖全局状态 → 非幂等</span>
</span><span class="line"><span class="o">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="继承链的状态传递">继承链的状态传递</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
<span class="line-number">28</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">BaseComponent</span> <span class="o">{</span>
</span><span class="line">    <span class="kd">protected</span> <span class="kt">boolean</span> <span class="n">initialized</span> <span class="o">=</span> <span class="kc">false</span><span class="o">;</span>
</span><span class="line">    
</span><span class="line">    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">init</span><span class="o">()</span> <span class="o">{</span>
</span><span class="line">        <span class="k">this</span><span class="o">.</span><span class="na">initialized</span> <span class="o">=</span> <span class="kc">true</span><span class="o">;</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line"><span class="o">}</span>
</span><span class="line">
</span><span class="line"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ChildComponent</span> <span class="kd">extends</span> <span class="nc">BaseComponent</span> <span class="o">{</span>
</span><span class="line">    <span class="kd">private</span> <span class="kt">int</span> <span class="n">childState</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
</span><span class="line">    
</span><span class="line">    <span class="nd">@Override</span>
</span><span class="line">    <span class="kd">public</span> <span class="kt">void</span> <span class="nf">init</span><span class="o">()</span> <span class="o">{</span>
</span><span class="line">        <span class="kd">super</span><span class="o">.</span><span class="na">init</span><span class="o">();</span>
</span><span class="line">        <span class="k">this</span><span class="o">.</span><span class="na">childState</span> <span class="o">=</span> <span class="mi">42</span><span class="o">;</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line">    
</span><span class="line">    <span class="kd">public</span> <span class="kt">int</span> <span class="nf">getValue</span><span class="o">()</span> <span class="o">{</span>
</span><span class="line">        <span class="c1">// 结果依赖于 init() 是否被调用</span>
</span><span class="line">        <span class="k">return</span> <span class="k">this</span><span class="o">.</span><span class="na">initialized</span> <span class="o">?</span> <span class="k">this</span><span class="o">.</span><span class="na">childState</span> <span class="o">:</span> <span class="mi">0</span><span class="o">;</span>
</span><span class="line">    <span class="o">}</span>
</span><span class="line"><span class="o">}</span>
</span><span class="line">
</span><span class="line"><span class="c1">// 使用示例</span>
</span><span class="line"><span class="nc">ChildComponent</span> <span class="n">comp</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ChildComponent</span><span class="o">();</span>
</span><span class="line"><span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">comp</span><span class="o">.</span><span class="na">getValue</span><span class="o">());</span>  <span class="c1">// 0</span>
</span><span class="line"><span class="n">comp</span><span class="o">.</span><span class="na">init</span><span class="o">();</span>
</span><span class="line"><span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">comp</span><span class="o">.</span><span class="na">getValue</span><span class="o">());</span>  <span class="c1">// 42</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="设计模式-对抗还是妥协">设计模式: 对抗还是妥协?</h3>

<p>面对状态泛滥,我们发明了各种设计模式:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># Singleton: 全局状态的官方认可
</span><span class="line"></span><span class="k">class</span> <span class="nc">Database</span><span class="p">:</span>
</span><span class="line">    <span class="n">_instance</span> <span class="o">=</span> <span class="bp">None</span>
</span><span class="line">    
</span><span class="line">    <span class="k">def</span> <span class="nf">__new__</span><span class="p">(</span><span class="n">cls</span><span class="p">):</span>
</span><span class="line">        <span class="k">if</span> <span class="n">cls</span><span class="p">.</span><span class="n">_instance</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
</span><span class="line">            <span class="n">cls</span><span class="p">.</span><span class="n">_instance</span> <span class="o">=</span> <span class="nb">super</span><span class="p">().</span><span class="n">__new__</span><span class="p">(</span><span class="n">cls</span><span class="p">)</span>
</span><span class="line">            <span class="n">cls</span><span class="p">.</span><span class="n">_instance</span><span class="p">.</span><span class="n">connection</span> <span class="o">=</span> <span class="bp">None</span>
</span><span class="line">        <span class="k">return</span> <span class="n">cls</span><span class="p">.</span><span class="n">_instance</span>
</span><span class="line">    
</span><span class="line">    <span class="c1"># 现在整个应用都依赖这个单一状态
</span><span class="line"></span>
</span><span class="line"><span class="c1"># Observer: 状态变化的级联效应
</span><span class="line"></span><span class="k">class</span> <span class="nc">Subject</span><span class="p">:</span>
</span><span class="line">    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class="line">        <span class="bp">self</span><span class="p">.</span><span class="n">_observers</span> <span class="o">=</span> <span class="p">[]</span>
</span><span class="line">        <span class="bp">self</span><span class="p">.</span><span class="n">_state</span> <span class="o">=</span> <span class="bp">None</span>
</span><span class="line">    
</span><span class="line">    <span class="k">def</span> <span class="nf">attach</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">observer</span><span class="p">):</span>
</span><span class="line">        <span class="bp">self</span><span class="p">.</span><span class="n">_observers</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">observer</span><span class="p">)</span>
</span><span class="line">    
</span><span class="line">    <span class="k">def</span> <span class="nf">notify</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class="line">        <span class="k">for</span> <span class="n">observer</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">_observers</span><span class="p">:</span>
</span><span class="line">            <span class="n">observer</span><span class="p">.</span><span class="n">update</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">_state</span><span class="p">)</span>  <span class="c1"># 触发连锁反应</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>这些模式本质上是在承认:<strong>我们已经放弃了幂等性,现在只是在管理混乱</strong>。</p>

<h3 id="并发的复杂性">并发的复杂性</h3>

<p>随着多线程、异步编程的引入,状态问题进一步恶化:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
<span class="line-number">28</span>
<span class="line-number">29</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># Python 多线程的竞态条件
</span><span class="line"></span><span class="kn">import</span> <span class="nn">threading</span>
</span><span class="line">
</span><span class="line"><span class="k">class</span> <span class="nc">SharedCounter</span><span class="p">:</span>
</span><span class="line">    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class="line">        <span class="bp">self</span><span class="p">.</span><span class="n">value</span> <span class="o">=</span> <span class="mi">0</span>
</span><span class="line">    
</span><span class="line">    <span class="k">def</span> <span class="nf">increment</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class="line">        <span class="c1"># 非原子操作!
</span><span class="line"></span>        <span class="n">temp</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">value</span>
</span><span class="line">        <span class="n">temp</span> <span class="o">+=</span> <span class="mi">1</span>
</span><span class="line">        <span class="bp">self</span><span class="p">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">temp</span>
</span><span class="line">
</span><span class="line"><span class="n">counter</span> <span class="o">=</span> <span class="n">SharedCounter</span><span class="p">()</span>
</span><span class="line">
</span><span class="line"><span class="k">def</span> <span class="nf">worker</span><span class="p">():</span>
</span><span class="line">    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1000</span><span class="p">):</span>
</span><span class="line">        <span class="n">counter</span><span class="p">.</span><span class="n">increment</span><span class="p">()</span>
</span><span class="line">
</span><span class="line"><span class="c1"># 两个线程同时操作
</span><span class="line"></span><span class="n">t1</span> <span class="o">=</span> <span class="n">threading</span><span class="p">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">worker</span><span class="p">)</span>
</span><span class="line"><span class="n">t2</span> <span class="o">=</span> <span class="n">threading</span><span class="p">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">worker</span><span class="p">)</span>
</span><span class="line"><span class="n">t1</span><span class="p">.</span><span class="n">start</span><span class="p">()</span>
</span><span class="line"><span class="n">t2</span><span class="p">.</span><span class="n">start</span><span class="p">()</span>
</span><span class="line"><span class="n">t1</span><span class="p">.</span><span class="n">join</span><span class="p">()</span>
</span><span class="line"><span class="n">t2</span><span class="p">.</span><span class="n">join</span><span class="p">()</span>
</span><span class="line">
</span><span class="line"><span class="k">print</span><span class="p">(</span><span class="n">counter</span><span class="p">.</span><span class="n">value</span><span class="p">)</span>  <span class="c1"># 预期 2000,实际可能是 1856、1923...
</span><span class="line"># 完全不可预测!</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<hr />

<h2 id="ai-时代的彻底不确定性">AI 时代的彻底不确定性</h2>

<h3 id="从确定性到概率性">从确定性到概率性</h3>

<p>如果说 OOP 让我们失去了幂等性,那么 AI 则让我们进入了一个<strong>根本上不确定</strong>的新世界。</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 同样的 prompt,不同的结果
</span><span class="line"></span><span class="n">prompt</span> <span class="o">=</span> <span class="s">"请写一个二分查找算法"</span>
</span><span class="line">
</span><span class="line"><span class="n">response1</span> <span class="o">=</span> <span class="n">openai</span><span class="p">.</span><span class="n">chat</span><span class="p">.</span><span class="n">completions</span><span class="p">.</span><span class="n">create</span><span class="p">(</span>
</span><span class="line">    <span class="n">model</span><span class="o">=</span><span class="s">"gpt-4"</span><span class="p">,</span>
</span><span class="line">    <span class="n">messages</span><span class="o">=</span><span class="p">[{</span><span class="s">"role"</span><span class="p">:</span> <span class="s">"user"</span><span class="p">,</span> <span class="s">"content"</span><span class="p">:</span> <span class="n">prompt</span><span class="p">}]</span>
</span><span class="line"><span class="p">)</span>
</span><span class="line"><span class="c1"># 可能返回递归实现
</span><span class="line"></span>
</span><span class="line"><span class="n">response2</span> <span class="o">=</span> <span class="n">openai</span><span class="p">.</span><span class="n">chat</span><span class="p">.</span><span class="n">completions</span><span class="p">.</span><span class="n">create</span><span class="p">(</span>
</span><span class="line">    <span class="n">model</span><span class="o">=</span><span class="s">"gpt-4"</span><span class="p">,</span>
</span><span class="line">    <span class="n">messages</span><span class="o">=</span><span class="p">[{</span><span class="s">"role"</span><span class="p">:</span> <span class="s">"user"</span><span class="p">,</span> <span class="s">"content"</span><span class="p">:</span> <span class="n">prompt</span><span class="p">}]</span>
</span><span class="line"><span class="p">)</span>
</span><span class="line"><span class="c1"># 可能返回迭代实现
</span><span class="line"></span>
</span><span class="line"><span class="c1"># 即使设置 temperature=0,也无法保证完全相同</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="模型层面的不确定性">模型层面的不确定性</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 即使是相同的模型、相同的温度
</span><span class="line"></span><span class="kn">import</span> <span class="nn">anthropic</span>
</span><span class="line">
</span><span class="line"><span class="n">client</span> <span class="o">=</span> <span class="n">anthropic</span><span class="p">.</span><span class="n">Anthropic</span><span class="p">()</span>
</span><span class="line">
</span><span class="line"><span class="k">def</span> <span class="nf">generate_code</span><span class="p">(</span><span class="n">prompt</span><span class="p">):</span>
</span><span class="line">    <span class="k">return</span> <span class="n">client</span><span class="p">.</span><span class="n">messages</span><span class="p">.</span><span class="n">create</span><span class="p">(</span>
</span><span class="line">        <span class="n">model</span><span class="o">=</span><span class="s">"claude-sonnet-4.5"</span><span class="p">,</span>
</span><span class="line">        <span class="n">max_tokens</span><span class="o">=</span><span class="mi">1024</span><span class="p">,</span>
</span><span class="line">        <span class="n">temperature</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span>  <span class="c1"># 最低随机性
</span><span class="line"></span>        <span class="n">messages</span><span class="o">=</span><span class="p">[{</span><span class="s">"role"</span><span class="p">:</span> <span class="s">"user"</span><span class="p">,</span> <span class="s">"content"</span><span class="p">:</span> <span class="n">prompt</span><span class="p">}]</span>
</span><span class="line">    <span class="p">)</span>
</span><span class="line">
</span><span class="line"><span class="n">result1</span> <span class="o">=</span> <span class="n">generate_code</span><span class="p">(</span><span class="s">"实现快速排序"</span><span class="p">)</span>
</span><span class="line"><span class="n">result2</span> <span class="o">=</span> <span class="n">generate_code</span><span class="p">(</span><span class="s">"实现快速排序"</span><span class="p">)</span>
</span><span class="line">
</span><span class="line"><span class="c1"># result1 和 result2 可能在变量命名、注释风格、
</span><span class="line"># 优化策略上有差异</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="上下文依赖的复杂性">上下文依赖的复杂性</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 简单问题: 相对确定
</span><span class="line"></span><span class="n">prompt1</span> <span class="o">=</span> <span class="s">"1+1等于几?"</span>
</span><span class="line"><span class="c1"># → 几乎总是返回 "2"
</span><span class="line"></span>
</span><span class="line"><span class="c1"># 复杂问题: 高度不确定
</span><span class="line"></span><span class="n">prompt2</span> <span class="o">=</span> <span class="s">"""
</span><span class="line">我有一个 Web 应用,用户反馈加载慢。
</span><span class="line">技术栈是 React + Node.js + MongoDB。
</span><span class="line">日活 5000,数据库有 100 万条记录。
</span><span class="line">请给出优化方案。
</span><span class="line">"""</span>
</span><span class="line"><span class="c1"># AI 需要假设/推断:
</span><span class="line"># - 慢在哪里?前端还是后端?
</span><span class="line"># - 什么查询?是否有索引?
</span><span class="line"># - 服务器配置如何?
</span><span class="line"># - 网络状况如何?
</span><span class="line"></span>
</span><span class="line"><span class="c1"># 每次可能给出完全不同的优化建议:
</span><span class="line"># - 增加索引
</span><span class="line"># - 实现缓存层
</span><span class="line"># - 代码分割
</span><span class="line"># - CDN 加速
</span><span class="line"># - 数据库分片</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="时间维度的漂移">时间维度的漂移</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 2023 年 1 月
</span><span class="line"></span><span class="n">response</span> <span class="o">=</span> <span class="n">ask_llm</span><span class="p">(</span><span class="s">"Python 最流行的 Web 框架是什么?"</span><span class="p">)</span>
</span><span class="line"><span class="c1"># → "Django 和 Flask 是最流行的..."
</span><span class="line"></span>
</span><span class="line"><span class="c1"># 2024 年 12 月
</span><span class="line"></span><span class="n">response</span> <span class="o">=</span> <span class="n">ask_llm</span><span class="p">(</span><span class="s">"Python 最流行的 Web 框架是什么?"</span><span class="p">)</span>
</span><span class="line"><span class="c1"># → "FastAPI 近年来非常流行..."
</span><span class="line"></span>
</span><span class="line"><span class="c1"># 同样的问题,因为:
</span><span class="line"># 1. 模型训练数据更新
</span><span class="line"># 2. 实际技术趋势变化
</span><span class="line"># 3. 社区共识演变
</span><span class="line"># "正确答案"本身就在变化</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="prompt-微小变化的蝴蝶效应">Prompt 微小变化的蝴蝶效应</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 版本 1
</span><span class="line"></span><span class="n">prompt_v1</span> <span class="o">=</span> <span class="s">"写一个排序函数"</span>
</span><span class="line"><span class="c1"># → 可能返回冒泡排序(简单教学版)
</span><span class="line"></span>
</span><span class="line"><span class="c1"># 版本 2(仅添加"高效")
</span><span class="line"></span><span class="n">prompt_v2</span> <span class="o">=</span> <span class="s">"写一个高效的排序函数"</span>
</span><span class="line"><span class="c1"># → 可能返回快速排序或归并排序
</span><span class="line"></span>
</span><span class="line"><span class="c1"># 版本 3(仅添加"生产环境")
</span><span class="line"></span><span class="n">prompt_v3</span> <span class="o">=</span> <span class="s">"写一个生产环境用的排序函数"</span>
</span><span class="line"><span class="c1"># → 可能返回 Tim Sort 并附带详细错误处理
</span><span class="line"></span>
</span><span class="line"><span class="c1"># 微小的措辞差异导致完全不同的输出</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="prompt-工程-与不确定性共舞">Prompt 工程: 与不确定性共舞</h3>

<p>我们试图通过 Prompt 工程来”驯服” AI 的不确定性:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="n">prompt</span> <span class="o">=</span> <span class="s">"""
</span><span class="line">请实现一个 HTTP 服务器的接口限流中间件。
</span><span class="line">
</span><span class="line">技术要求:
</span><span class="line">- 语言: Python 3.10+
</span><span class="line">- 框架: Flask
</span><span class="line">- 算法: 令牌桶(Token Bucket)
</span><span class="line">- 限流规则: 每 IP 每分钟 100 请求
</span><span class="line">
</span><span class="line">代码规范:
</span><span class="line">- 遵循 PEP 8
</span><span class="line">- 类型注解完整
</span><span class="line">- 包含 docstring
</span><span class="line">- 添加单元测试
</span><span class="line">
</span><span class="line">请给出完整实现,包括:
</span><span class="line">1. 中间件类定义
</span><span class="line">2. 配置接口
</span><span class="line">3. 使用示例
</span><span class="line">4. 单元测试用例
</span><span class="line">"""</span>
</span><span class="line">
</span><span class="line"><span class="c1"># 通过详细约束提高输出质量
</span><span class="line"># 但不同执行可能强调不同方面</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="不可逾越的鸿沟">不可逾越的鸿沟</h3>

<p>AI 的不确定性是<strong>本质性</strong>的:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 概率本质: 每个 token 都是从概率分布中采样
</span><span class="line"></span><span class="k">def</span> <span class="nf">llm_generate</span><span class="p">(</span><span class="n">prompt</span><span class="p">):</span>
</span><span class="line">    <span class="c1"># 简化的概念模型
</span><span class="line"></span>    <span class="n">context</span> <span class="o">=</span> <span class="n">encode</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span>
</span><span class="line">    <span class="n">output</span> <span class="o">=</span> <span class="p">[]</span>
</span><span class="line">    
</span><span class="line">    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">max_tokens</span><span class="p">):</span>
</span><span class="line">        <span class="c1"># 预测下一个 token 的概率分布
</span><span class="line"></span>        <span class="n">probs</span> <span class="o">=</span> <span class="n">model</span><span class="p">.</span><span class="n">predict_next_token</span><span class="p">(</span><span class="n">context</span><span class="p">)</span>
</span><span class="line">        <span class="c1"># {
</span><span class="line"></span>        <span class="c1">#   "def": 0.45,
</span><span class="line"></span>        <span class="c1">#   "class": 0.30,
</span><span class="line"></span>        <span class="c1">#   "import": 0.15,
</span><span class="line"></span>        <span class="c1">#   "from": 0.08,
</span><span class="line"></span>        <span class="c1">#   ...
</span><span class="line"></span>        <span class="c1"># }
</span><span class="line"></span>        
</span><span class="line">        <span class="c1"># 采样!即使 temperature=0,也是取最高概率
</span><span class="line"></span>        <span class="c1"># 但多个 token 概率接近时,微小的数值差异
</span><span class="line"></span>        <span class="c1"># 就可能导致不同选择
</span><span class="line"></span>        <span class="n">next_token</span> <span class="o">=</span> <span class="n">sample</span><span class="p">(</span><span class="n">probs</span><span class="p">,</span> <span class="n">temperature</span><span class="p">)</span>
</span><span class="line">        <span class="n">output</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">next_token</span><span class="p">)</span>
</span><span class="line">        <span class="n">context</span> <span class="o">=</span> <span class="n">update</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">next_token</span><span class="p">)</span>
</span><span class="line">    
</span><span class="line">    <span class="k">return</span> <span class="n">decode</span><span class="p">(</span><span class="n">output</span><span class="p">)</span>
</span><span class="line">
</span><span class="line"><span class="c1"># 每次采样都可能走上不同的路径</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>即使是”最确定”的情况:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="n">prompt</span> <span class="o">=</span> <span class="s">"请计算: 2 + 2 = ?"</span>
</span><span class="line">
</span><span class="line"><span class="c1"># 99.9999% 的情况返回 "4"
</span><span class="line"># 但理论上可能返回:
</span><span class="line"># - "4(十进制)"
</span><span class="line"># - "10(二进制)"
</span><span class="line"># - "在模 2 算术中等于 0"
</span><span class="line"># - "这取决于你使用的数制..."
</span><span class="line"># - 开始解释加法的历史
</span><span class="line"></span>
</span><span class="line"><span class="c1"># 因为 LLM 是模式匹配,不是符号运算
</span><span class="line"># 完全的幂等性是不可能的</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<hr />

<h2 id="为什么我们接受了这种劣化">为什么我们接受了这种劣化?</h2>

<h3 id="表达力的代价">表达力的代价</h3>

<p>每一次幂等性的丧失,都换来了更强的表达力:</p>

<table>
  <thead>
    <tr>
      <th>阶段</th>
      <th>获得</th>
      <th>失去</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>数学→编程</td>
      <td>可执行性、实用性</td>
      <td>无限精度、完美抽象</td>
    </tr>
    <tr>
      <td>纯函数→OOP</td>
      <td>模块化、可复用、领域建模</td>
      <td>函数纯度、可预测性</td>
    </tr>
    <tr>
      <td>OOP→AI</td>
      <td>自然语言交互、知识泛化、创造力</td>
      <td>确定性、可控性、可解释性</td>
    </tr>
  </tbody>
</table>

<h3 id="web-开发的实例对比">Web 开发的实例对比</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
<span class="line-number">28</span>
<span class="line-number">29</span>
<span class="line-number">30</span>
<span class="line-number">31</span>
<span class="line-number">32</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 纯函数时代: 简单但局限
</span><span class="line"></span><span class="k">def</span> <span class="nf">calculate_tax</span><span class="p">(</span><span class="n">amount</span><span class="p">):</span>
</span><span class="line">    <span class="k">return</span> <span class="n">amount</span> <span class="o">*</span> <span class="mf">0.1</span>  <span class="c1"># 完全确定,但功能单一
</span><span class="line"></span>
</span><span class="line"><span class="c1"># OOP 时代: 强大但复杂
</span><span class="line"></span><span class="k">class</span> <span class="nc">ShoppingCart</span><span class="p">:</span>
</span><span class="line">    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class="line">        <span class="bp">self</span><span class="p">.</span><span class="n">items</span> <span class="o">=</span> <span class="p">[]</span>
</span><span class="line">        <span class="bp">self</span><span class="p">.</span><span class="n">user</span> <span class="o">=</span> <span class="bp">None</span>
</span><span class="line">        <span class="bp">self</span><span class="p">.</span><span class="n">discounts</span> <span class="o">=</span> <span class="p">[]</span>
</span><span class="line">    
</span><span class="line">    <span class="k">def</span> <span class="nf">add_item</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">item</span><span class="p">):</span>
</span><span class="line">        <span class="bp">self</span><span class="p">.</span><span class="n">items</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
</span><span class="line">        <span class="bp">self</span><span class="p">.</span><span class="n">recalculate_total</span><span class="p">()</span>  <span class="c1"># 级联更新
</span><span class="line"></span>    
</span><span class="line">    <span class="k">def</span> <span class="nf">apply_discount</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">code</span><span class="p">):</span>
</span><span class="line">        <span class="c1"># 依赖外部 API 验证
</span><span class="line"></span>        <span class="c1"># 依赖用户状态
</span><span class="line"></span>        <span class="c1"># 依赖时间(限时优惠)
</span><span class="line"></span>        <span class="c1"># 完全非幂等
</span><span class="line"></span>        <span class="k">pass</span>
</span><span class="line">
</span><span class="line"><span class="c1"># AI 时代: 智能但不确定
</span><span class="line"></span><span class="k">async</span> <span class="k">def</span> <span class="nf">optimize_checkout</span><span class="p">(</span><span class="n">cart</span><span class="p">,</span> <span class="n">user_history</span><span class="p">):</span>
</span><span class="line">    <span class="n">prompt</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"""
</span><span class="line">    用户购物车: </span><span class="si">{</span><span class="n">cart</span><span class="si">}</span><span class="s">
</span><span class="line">    历史记录: </span><span class="si">{</span><span class="n">user_history</span><span class="si">}</span><span class="s">
</span><span class="line">    请推荐最佳的支付方式和优惠组合
</span><span class="line">    """</span>
</span><span class="line">    
</span><span class="line">    <span class="c1"># 每次可能给出不同建议
</span><span class="line"></span>    <span class="k">return</span> <span class="k">await</span> <span class="n">ai</span><span class="p">.</span><span class="n">complete</span><span class="p">(</span><span class="n">prompt</span><span class="p">)</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="复杂性的本质">复杂性的本质</h3>

<p>这种劣化反映了我们处理问题的方式转变:</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 数学: 封闭世界假设
</span><span class="line"># "给定完整的信息,推导确定的结论"
</span><span class="line"></span><span class="n">theorem</span> <span class="o">=</span> <span class="s">"∀x ∈ ℕ: x + 0 = x"</span>
</span><span class="line">
</span><span class="line"><span class="c1"># 编程: 开放世界假设
</span><span class="line"># "在不完整的信息下,做出最佳决策"
</span><span class="line"></span><span class="k">def</span> <span class="nf">handle_user_input</span><span class="p">(</span><span class="n">input_string</span><span class="p">):</span>
</span><span class="line">    <span class="c1"># 输入可能是任何东西
</span><span class="line"></span>    <span class="c1"># 必须处理边界情况
</span><span class="line"></span>    <span class="c1"># 必须应对意外情况
</span><span class="line"></span>    <span class="k">if</span> <span class="ow">not</span> <span class="n">input_string</span><span class="p">:</span>
</span><span class="line">        <span class="k">return</span> <span class="n">default_value</span><span class="p">()</span>
</span><span class="line">    <span class="k">try</span><span class="p">:</span>
</span><span class="line">        <span class="k">return</span> <span class="n">process</span><span class="p">(</span><span class="n">input_string</span><span class="p">)</span>
</span><span class="line">    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
</span><span class="line">        <span class="n">log_error</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
</span><span class="line">        <span class="k">return</span> <span class="n">fallback_value</span><span class="p">()</span>
</span><span class="line">
</span><span class="line"><span class="c1"># AI: 概率世界假设
</span><span class="line"># "基于模式和统计,生成最可能的答案"
</span><span class="line"></span><span class="k">def</span> <span class="nf">ai_solve</span><span class="p">(</span><span class="n">problem_description</span><span class="p">):</span>
</span><span class="line">    <span class="c1"># 没有"正确答案"
</span><span class="line"></span>    <span class="c1"># 只有"合理答案"
</span><span class="line"></span>    <span class="c1"># 输出是概率分布的采样
</span><span class="line"></span>    <span class="k">return</span> <span class="n">sample_from_distribution</span><span class="p">(</span>
</span><span class="line">        <span class="n">learned_patterns</span><span class="p">(</span><span class="n">problem_description</span><span class="p">)</span>
</span><span class="line">    <span class="p">)</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<hr />

<h2 id="拥抱不确定性">拥抱不确定性</h2>

<h3 id="概率性编程">概率性编程</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 不是追求"肯定返回 X"
</span><span class="line"># 而是"90% 概率返回 X 类,10% 概率返回 Y 类"
</span><span class="line"></span>
</span><span class="line"><span class="k">class</span> <span class="nc">AICodeGenerator</span><span class="p">:</span>
</span><span class="line">    <span class="k">async</span> <span class="k">def</span> <span class="nf">generate_function</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">spec</span><span class="p">):</span>
</span><span class="line">        <span class="c1"># 生成多个候选
</span><span class="line"></span>        <span class="n">candidates</span> <span class="o">=</span> <span class="k">await</span> <span class="n">asyncio</span><span class="p">.</span><span class="n">gather</span><span class="p">(</span><span class="o">*</span><span class="p">[</span>
</span><span class="line">            <span class="bp">self</span><span class="p">.</span><span class="n">_generate_once</span><span class="p">(</span><span class="n">spec</span><span class="p">)</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
</span><span class="line">        <span class="p">])</span>
</span><span class="line">        
</span><span class="line">        <span class="c1"># 评估质量分布
</span><span class="line"></span>        <span class="n">scores</span> <span class="o">=</span> <span class="p">[</span><span class="bp">self</span><span class="p">.</span><span class="n">_evaluate</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">candidates</span><span class="p">]</span>
</span><span class="line">        
</span><span class="line">        <span class="c1"># 选择最优,但承认不确定性
</span><span class="line"></span>        <span class="n">best_idx</span> <span class="o">=</span> <span class="n">scores</span><span class="p">.</span><span class="n">index</span><span class="p">(</span><span class="nb">max</span><span class="p">(</span><span class="n">scores</span><span class="p">))</span>
</span><span class="line">        <span class="k">return</span> <span class="p">{</span>
</span><span class="line">            <span class="s">'code'</span><span class="p">:</span> <span class="n">candidates</span><span class="p">[</span><span class="n">best_idx</span><span class="p">],</span>
</span><span class="line">            <span class="s">'confidence'</span><span class="p">:</span> <span class="nb">max</span><span class="p">(</span><span class="n">scores</span><span class="p">),</span>
</span><span class="line">            <span class="s">'alternatives'</span><span class="p">:</span> <span class="n">candidates</span><span class="p">[:</span><span class="mi">3</span><span class="p">]</span>
</span><span class="line">        <span class="p">}</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="幂等性的局部保证">幂等性的局部保证</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 不是全局幂等,而是关键路径幂等
</span><span class="line"></span><span class="k">class</span> <span class="nc">PaymentService</span><span class="p">:</span>
</span><span class="line">    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class="line">        <span class="bp">self</span><span class="p">.</span><span class="n">_processed</span> <span class="o">=</span> <span class="p">{}</span>  <span class="c1"># 幂等性缓存
</span><span class="line"></span>    
</span><span class="line">    <span class="k">async</span> <span class="k">def</span> <span class="nf">process_payment</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">transaction_id</span><span class="p">,</span> <span class="n">amount</span><span class="p">):</span>
</span><span class="line">        <span class="c1"># 这个操作必须幂等!
</span><span class="line"></span>        <span class="k">if</span> <span class="n">transaction_id</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">_processed</span><span class="p">:</span>
</span><span class="line">            <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">_processed</span><span class="p">[</span><span class="n">transaction_id</span><span class="p">]</span>
</span><span class="line">        
</span><span class="line">        <span class="c1"># 实际处理(可能非幂等)
</span><span class="line"></span>        <span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="p">.</span><span class="n">_execute_payment</span><span class="p">(</span><span class="n">amount</span><span class="p">)</span>
</span><span class="line">        
</span><span class="line">        <span class="c1"># 缓存结果保证幂等性
</span><span class="line"></span>        <span class="bp">self</span><span class="p">.</span><span class="n">_processed</span><span class="p">[</span><span class="n">transaction_id</span><span class="p">]</span> <span class="o">=</span> <span class="n">result</span>
</span><span class="line">        <span class="k">return</span> <span class="n">result</span>
</span><span class="line">    
</span><span class="line">    <span class="k">async</span> <span class="k">def</span> <span class="nf">_execute_payment</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">amount</span><span class="p">):</span>
</span><span class="line">        <span class="c1"># 这里可以调用 AI 做风控分析(非幂等)
</span><span class="line"></span>        <span class="n">risk_score</span> <span class="o">=</span> <span class="k">await</span> <span class="n">ai_risk_assessment</span><span class="p">(</span><span class="n">amount</span><span class="p">)</span>
</span><span class="line">        <span class="k">if</span> <span class="n">risk_score</span> <span class="o">&gt;</span> <span class="mf">0.8</span><span class="p">:</span>
</span><span class="line">            <span class="c1"># AI 给出的理由可能每次不同,但决策是幂等的
</span><span class="line"></span>            <span class="k">raise</span> <span class="n">PaymentRejected</span><span class="p">(</span><span class="s">"High risk detected"</span><span class="p">)</span>
</span><span class="line">        
</span><span class="line">        <span class="k">return</span> <span class="k">await</span> <span class="n">bank_api</span><span class="p">.</span><span class="n">charge</span><span class="p">(</span><span class="n">amount</span><span class="p">)</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="验证驱动开发">验证驱动开发</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
<span class="line-number">28</span>
<span class="line-number">29</span>
<span class="line-number">30</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 对于 AI 生成的代码,不追求幂等性
</span><span class="line"># 而是通过测试保证正确性
</span><span class="line"></span>
</span><span class="line"><span class="k">class</span> <span class="nc">AIAssistedDevelopment</span><span class="p">:</span>
</span><span class="line">    <span class="k">async</span> <span class="k">def</span> <span class="nf">implement_feature</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">requirements</span><span class="p">):</span>
</span><span class="line">        <span class="c1"># 生成多个实现
</span><span class="line"></span>        <span class="n">implementations</span> <span class="o">=</span> <span class="p">[]</span>
</span><span class="line">        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">):</span>
</span><span class="line">            <span class="n">code</span> <span class="o">=</span> <span class="k">await</span> <span class="n">ai</span><span class="p">.</span><span class="n">generate_code</span><span class="p">(</span><span class="n">requirements</span><span class="p">)</span>
</span><span class="line">            <span class="n">implementations</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">code</span><span class="p">)</span>
</span><span class="line">        
</span><span class="line">        <span class="c1"># 交叉验证
</span><span class="line"></span>        <span class="n">test_results</span> <span class="o">=</span> <span class="p">[]</span>
</span><span class="line">        <span class="k">for</span> <span class="n">impl</span> <span class="ow">in</span> <span class="n">implementations</span><span class="p">:</span>
</span><span class="line">            <span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="p">.</span><span class="n">_run_tests</span><span class="p">(</span><span class="n">impl</span><span class="p">)</span>
</span><span class="line">            <span class="n">test_results</span><span class="p">.</span><span class="n">append</span><span class="p">({</span>
</span><span class="line">                <span class="s">'code'</span><span class="p">:</span> <span class="n">impl</span><span class="p">,</span>
</span><span class="line">                <span class="s">'passed'</span><span class="p">:</span> <span class="n">result</span><span class="p">.</span><span class="n">passed</span><span class="p">,</span>
</span><span class="line">                <span class="s">'coverage'</span><span class="p">:</span> <span class="n">result</span><span class="p">.</span><span class="n">coverage</span><span class="p">,</span>
</span><span class="line">                <span class="s">'performance'</span><span class="p">:</span> <span class="n">result</span><span class="p">.</span><span class="n">performance</span>
</span><span class="line">            <span class="p">})</span>
</span><span class="line">        
</span><span class="line">        <span class="c1"># 选择最可靠的实现
</span><span class="line"></span>        <span class="n">best</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="n">test_results</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="p">(</span>
</span><span class="line">            <span class="n">x</span><span class="p">[</span><span class="s">'passed'</span><span class="p">],</span>
</span><span class="line">            <span class="n">x</span><span class="p">[</span><span class="s">'coverage'</span><span class="p">],</span>
</span><span class="line">            <span class="o">-</span><span class="n">x</span><span class="p">[</span><span class="s">'performance'</span><span class="p">]</span>  <span class="c1"># 越小越好
</span><span class="line"></span>        <span class="p">))</span>
</span><span class="line">        
</span><span class="line">        <span class="k">return</span> <span class="n">best</span><span class="p">[</span><span class="s">'code'</span><span class="p">]</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="ai-时代的测试">AI 时代的测试</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 传统测试: 期望确定的结果
</span><span class="line"></span><span class="k">def</span> <span class="nf">test_add</span><span class="p">():</span>
</span><span class="line">    <span class="k">assert</span> <span class="n">add</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="o">==</span> <span class="mi">5</span>  <span class="c1"># 必须精确等于 5
</span><span class="line"></span>
</span><span class="line"><span class="c1"># AI 时代的测试: 期望合理的结果
</span><span class="line"></span><span class="k">def</span> <span class="nf">test_ai_code_generation</span><span class="p">():</span>
</span><span class="line">    <span class="n">code</span> <span class="o">=</span> <span class="n">ai</span><span class="p">.</span><span class="n">generate</span><span class="p">(</span><span class="s">"实现 add 函数"</span><span class="p">)</span>
</span><span class="line">    
</span><span class="line">    <span class="c1"># 不测试具体代码,测试行为
</span><span class="line"></span>    <span class="k">assert</span> <span class="s">'def'</span> <span class="ow">in</span> <span class="n">code</span> <span class="ow">or</span> <span class="s">'function'</span> <span class="ow">in</span> <span class="n">code</span>
</span><span class="line">    <span class="k">assert</span> <span class="n">is_valid_syntax</span><span class="p">(</span><span class="n">code</span><span class="p">)</span>
</span><span class="line">    
</span><span class="line">    <span class="c1"># 动态执行测试
</span><span class="line"></span>    <span class="n">func</span> <span class="o">=</span> <span class="n">compile_and_import</span><span class="p">(</span><span class="n">code</span><span class="p">)</span>
</span><span class="line">    <span class="k">assert</span> <span class="n">func</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="o">==</span> <span class="mi">5</span>
</span><span class="line">    <span class="k">assert</span> <span class="n">func</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>
</span><span class="line">    <span class="k">assert</span> <span class="n">func</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>
</span><span class="line">    
</span><span class="line">    <span class="c1"># 关注结果的正确性,不关注实现的一致性</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="属性测试">属性测试</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># Python 中的属性测试(使用 hypothesis)
</span><span class="line"></span><span class="kn">from</span> <span class="nn">hypothesis</span> <span class="kn">import</span> <span class="n">given</span><span class="p">,</span> <span class="n">strategies</span> <span class="k">as</span> <span class="n">st</span>
</span><span class="line">
</span><span class="line"><span class="c1"># 传统测试
</span><span class="line"></span><span class="k">def</span> <span class="nf">test_reverse_twice</span><span class="p">():</span>
</span><span class="line">    <span class="k">assert</span> <span class="n">reverse</span><span class="p">(</span><span class="n">reverse</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]))</span> <span class="o">==</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
</span><span class="line">
</span><span class="line"><span class="c1"># 属性测试: 测试性质而非具体值
</span><span class="line"></span><span class="o">@</span><span class="n">given</span><span class="p">(</span><span class="n">st</span><span class="p">.</span><span class="n">lists</span><span class="p">(</span><span class="n">st</span><span class="p">.</span><span class="n">integers</span><span class="p">()))</span>
</span><span class="line"><span class="k">def</span> <span class="nf">test_ai_generated_sort</span><span class="p">(</span><span class="n">arr</span><span class="p">):</span>
</span><span class="line">    <span class="n">sort_fn</span> <span class="o">=</span> <span class="n">ai</span><span class="p">.</span><span class="n">generate_sort_function</span><span class="p">()</span>
</span><span class="line">    <span class="n">sorted_arr</span> <span class="o">=</span> <span class="n">sort_fn</span><span class="p">(</span><span class="n">arr</span><span class="p">)</span>
</span><span class="line">    
</span><span class="line">    <span class="c1"># 测试排序的性质
</span><span class="line"></span>    <span class="c1"># 1. 长度不变
</span><span class="line"></span>    <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">sorted_arr</span><span class="p">)</span> <span class="o">==</span> <span class="nb">len</span><span class="p">(</span><span class="n">arr</span><span class="p">)</span>
</span><span class="line">    
</span><span class="line">    <span class="c1"># 2. 顺序正确
</span><span class="line"></span>    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">sorted_arr</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">):</span>
</span><span class="line">        <span class="k">assert</span> <span class="n">sorted_arr</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">sorted_arr</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>
</span><span class="line">    
</span><span class="line">    <span class="c1"># 3. 元素相同
</span><span class="line"></span>    <span class="k">assert</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">sorted_arr</span><span class="p">)</span> <span class="o">==</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">arr</span><span class="p">)</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="架构层面的应对">架构层面的应对</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
<span class="line-number">28</span>
<span class="line-number">29</span>
<span class="line-number">30</span>
<span class="line-number">31</span>
<span class="line-number">32</span>
<span class="line-number">33</span>
<span class="line-number">34</span>
<span class="line-number">35</span>
<span class="line-number">36</span>
<span class="line-number">37</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 确定性层 + 不确定性层的分离架构
</span><span class="line"></span><span class="k">class</span> <span class="nc">HybridSystem</span><span class="p">:</span>
</span><span class="line">    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span><span class="line">        <span class="c1"># 确定性组件
</span><span class="line"></span>        <span class="bp">self</span><span class="p">.</span><span class="n">cache</span> <span class="o">=</span> <span class="n">DeterministicCache</span><span class="p">()</span>
</span><span class="line">        <span class="bp">self</span><span class="p">.</span><span class="n">validator</span> <span class="o">=</span> <span class="n">DeterministicValidator</span><span class="p">()</span>
</span><span class="line">        
</span><span class="line">        <span class="c1"># 不确定性组件
</span><span class="line"></span>        <span class="bp">self</span><span class="p">.</span><span class="n">ai_engine</span> <span class="o">=</span> <span class="n">AIEngine</span><span class="p">()</span>
</span><span class="line">    
</span><span class="line">    <span class="k">async</span> <span class="k">def</span> <span class="nf">handle_request</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">request</span><span class="p">):</span>
</span><span class="line">        <span class="c1"># 1. 尝试确定性路径
</span><span class="line"></span>        <span class="n">cached</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">cache</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">request</span><span class="p">.</span><span class="n">key</span><span class="p">)</span>
</span><span class="line">        <span class="k">if</span> <span class="n">cached</span> <span class="ow">and</span> <span class="bp">self</span><span class="p">.</span><span class="n">validator</span><span class="p">.</span><span class="n">is_valid</span><span class="p">(</span><span class="n">cached</span><span class="p">):</span>
</span><span class="line">            <span class="k">return</span> <span class="n">cached</span>  <span class="c1"># 幂等返回
</span><span class="line"></span>        
</span><span class="line">        <span class="c1"># 2. 进入不确定性路径
</span><span class="line"></span>        <span class="n">candidates</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="p">.</span><span class="n">ai_engine</span><span class="p">.</span><span class="n">generate_responses</span><span class="p">(</span>
</span><span class="line">            <span class="n">request</span><span class="p">,</span> <span class="n">n</span><span class="o">=</span><span class="mi">3</span>
</span><span class="line">        <span class="p">)</span>
</span><span class="line">        
</span><span class="line">        <span class="c1"># 3. 通过确定性验证
</span><span class="line"></span>        <span class="n">valid_candidates</span> <span class="o">=</span> <span class="p">[</span>
</span><span class="line">            <span class="n">c</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">candidates</span> 
</span><span class="line">            <span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">validator</span><span class="p">.</span><span class="n">validate</span><span class="p">(</span><span class="n">c</span><span class="p">)</span>
</span><span class="line">        <span class="p">]</span>
</span><span class="line">        
</span><span class="line">        <span class="k">if</span> <span class="ow">not</span> <span class="n">valid_candidates</span><span class="p">:</span>
</span><span class="line">            <span class="k">raise</span> <span class="n">NoValidResponseError</span><span class="p">()</span>
</span><span class="line">        
</span><span class="line">        <span class="c1"># 4. 选择最优(可能非幂等)
</span><span class="line"></span>        <span class="n">best</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">_select_best</span><span class="p">(</span><span class="n">valid_candidates</span><span class="p">)</span>
</span><span class="line">        
</span><span class="line">        <span class="c1"># 5. 缓存以提供局部幂等性
</span><span class="line"></span>        <span class="bp">self</span><span class="p">.</span><span class="n">cache</span><span class="p">.</span><span class="nb">set</span><span class="p">(</span><span class="n">request</span><span class="p">.</span><span class="n">key</span><span class="p">,</span> <span class="n">best</span><span class="p">)</span>
</span><span class="line">        
</span><span class="line">        <span class="k">return</span> <span class="n">best</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<hr />

<h2 id="总结">总结</h2>

<p>幂等性的劣化,表面上看是能力的退化,实质上是<strong>抽象层次的跃升</strong>。</p>

<h3 id="四个时代的对比">四个时代的对比</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 数学时代: 完美但封闭
</span><span class="line"></span><span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="o">=</span> <span class="mi">9</span>  <span class="c1"># 永远如此,但只能计算
</span><span class="line"></span>
</span><span class="line"><span class="c1"># 编程时代: 实用但妥协
</span><span class="line"></span><span class="n">square</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>  <span class="c1"># 通常是 9,但有浮点误差
</span><span class="line"></span>
</span><span class="line"><span class="c1"># OOP 时代: 灵活但混乱
</span><span class="line"></span><span class="n">counter</span><span class="p">.</span><span class="n">increment</span><span class="p">()</span>  <span class="c1"># 每次不同,但能建模复杂系统
</span><span class="line"></span>
</span><span class="line"><span class="c1"># AI 时代: 智能但不确定
</span><span class="line"></span><span class="n">ai</span><span class="p">.</span><span class="n">solve</span><span class="p">(</span><span class="s">"优化这段代码"</span><span class="p">)</span>  <span class="c1"># 每次可能不同,但可能发现我们想不到的方案</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="核心转变">核心转变</h3>

<ol>
  <li><strong>确定性 → 概率性</strong>: 不再问”结果是什么”,而问”结果的分布是什么”</li>
  <li><strong>一次正确 → 多次验证</strong>: 不再依赖单次执行,而是通过多次采样和验证</li>
  <li><strong>完美解 → 满意解</strong>: 不再追求理论最优,而是追求实践可行</li>
  <li><strong>封闭系统 → 开放系统</strong>: 不再假设完整信息,而是适应不完整和变化</li>
</ol>

<h3 id="实践启示">实践启示</h3>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
</pre></td><td class="code"><pre><code class="python"><span class="line"><span class="c1"># 1. 分层保证确定性
</span><span class="line"># 关键路径: 强幂等性(支付、数据一致性)
</span><span class="line"># 辅助功能: 弱幂等性(推荐、搜索)
</span><span class="line"># AI 增强: 非幂等性(内容生成、智能建议)
</span><span class="line"></span>
</span><span class="line"><span class="c1"># 2. 用测试替代幂等性
</span><span class="line"># 不是保证"代码相同"
</span><span class="line"># 而是保证"行为正确"
</span><span class="line"></span>
</span><span class="line"><span class="c1"># 3. 拥抱多样性
</span><span class="line"># 接受 AI 的创造力
</span><span class="line"># 用验证机制保证质量
</span><span class="line"></span>
</span><span class="line"><span class="c1"># 4. 建立反馈循环
</span><span class="line"># 收集真实世界的数据
</span><span class="line"># 持续优化决策边界</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>幂等性没有消失,它只是变成了一个<strong>局部的、概率的、上下文相关的</strong>特性。我们不是在失去控制,而是在学习与不确定性共舞。</p>
<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Could not create task ':generateDebugRFile' 问题小记]]></title>
    <link href="https://droidyue.com/blog/2024/11/03/could-not-create-task-generatedebugrfile-wen-ti-xiao-ji/"/>
    <updated>2024-11-03T00:49:00+08:00</updated>
    <id>https://droidyue.com/blog/2024/11/03/could-not-create-task-generatedebugrfile-wen-ti-xiao-ji</id>
    <content type="html"><![CDATA[<p>前段时间，处理一个比较旧的 flutter plugin，涉及到 Android 的部分，一顿修改后，发现无法 gradle sync 成功。 报错如下，</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
</pre></td><td class="code"><pre><code class="java"><span class="line"><span class="nc">Could</span> <span class="n">not</span> <span class="n">create</span> <span class="n">task</span> <span class="err">'</span><span class="o">:</span><span class="n">generateDebugRFile</span><span class="err">'</span><span class="o">.</span>
</span><span class="line"><span class="nc">Cannot</span> <span class="n">use</span> <span class="nd">@TaskAction</span> <span class="n">annotation</span> <span class="n">on</span> <span class="n">method</span> <span class="nc">IncrementalTask</span><span class="o">.</span><span class="na">taskAction</span><span class="n">$gradle_core</span><span class="o">()</span> <span class="n">because</span> <span class="kd">interface</span> <span class="nc">org</span><span class="o">.</span><span class="na">gradle</span><span class="o">.</span><span class="na">api</span><span class="o">.</span><span class="na">tasks</span><span class="o">.</span><span class="na">incremental</span><span class="o">.</span><span class="na">IncrementalTaskInputs</span> <span class="n">is</span> <span class="n">not</span> <span class="n">a</span> <span class="n">valid</span> <span class="n">parameter</span> <span class="n">to</span> <span class="n">an</span> <span class="n">action</span> <span class="n">method</span><span class="o">.</span>
</span><span class="line">
</span><span class="line"><span class="o">*</span> <span class="nl">Try:</span>
</span><span class="line"><span class="o">&gt;</span> <span class="nc">Run</span> <span class="n">with</span> <span class="o">--</span><span class="n">debug</span> <span class="n">option</span> <span class="n">to</span> <span class="n">get</span> <span class="n">more</span> <span class="n">log</span> <span class="n">output</span><span class="o">.</span>
</span><span class="line"><span class="o">&gt;</span> <span class="nc">Run</span> <span class="n">with</span> <span class="o">--</span><span class="n">scan</span> <span class="n">to</span> <span class="n">get</span> <span class="n">full</span> <span class="n">insights</span><span class="o">.</span>
</span><span class="line">
</span><span class="line"><span class="o">*</span> <span class="nc">Exception</span> <span class="nl">is:</span>
</span><span class="line"><span class="n">com</span><span class="o">.</span><span class="na">intellij</span><span class="o">.</span><span class="na">openapi</span><span class="o">.</span><span class="na">externalSystem</span><span class="o">.</span><span class="na">model</span><span class="o">.</span><span class="na">ExternalSystemException</span><span class="o">:</span> <span class="nc">Could</span> <span class="n">not</span> <span class="n">create</span> <span class="n">task</span> <span class="err">'</span><span class="o">:</span><span class="n">generateDebugRFile</span><span class="err">'</span><span class="o">.</span>
</span><span class="line"><span class="nc">Cannot</span> <span class="n">use</span> <span class="nd">@TaskAction</span> <span class="n">annotation</span> <span class="n">on</span> <span class="n">method</span> <span class="nc">IncrementalTask</span><span class="o">.</span><span class="na">taskAction</span><span class="n">$gradle_core</span><span class="o">()</span> <span class="n">because</span> <span class="kd">interface</span> <span class="nc">org</span><span class="o">.</span><span class="na">gradle</span><span class="o">.</span><span class="na">api</span><span class="o">.</span><span class="na">tasks</span><span class="o">.</span><span class="na">incremental</span><span class="o">.</span><span class="na">IncrementalTaskInputs</span> <span class="n">is</span> <span class="n">not</span> <span class="n">a</span> <span class="n">valid</span> <span class="n">parameter</span> <span class="n">to</span> <span class="n">an</span> <span class="n">action</span> <span class="n">method</span><span class="o">.</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<!--more-->

<p>根据分析上面的错误信息，判定与 gradle 有关，和修改的 kotlin 代码无关。</p>

<p>经过一些简短尝试，最终确定是 gradle 版本不匹配的问题（主要由这一句推断 because interface org.gradle.api.tasks.incremental.IncrementalTaskInputs is not a valid parameter to an action method.）。</p>

<h2 id="原因与解法">原因与解法</h2>
<ul>
  <li>原因为 Android Gradle Plugin 与 gradle 不匹配。</li>
  <li>可以修改 gradle plugin 版本，也可以修改 gradle 版本。</li>
</ul>

<h3 id="修改-agp-版本">修改 AGP 版本</h3>
<p>classpath ‘com.android.tools.build:gradle:7.1.2’    // The Android Gradle plugin.</p>

<h3 id="修改-gradle-版本">修改 gradle 版本</h3>
<p>修改gradle/wrapper/gradle-wrapper.properties</p>

<p>distributionUrl=https://services.gradle.org/distributions/gradle-8.0-all.zip</p>

<p>修改成（或者对应的gradle 版本） 
distributionUrl=https://services.gradle.org/distributions/gradle-7.4-all.zip</p>

<h3 id="如何确定-agp-与-gradle-对应关系">如何确定 AGP 与 gradle 对应关系</h3>
<p>查询，请访问 这里 https://developer.android.com/build/releases/gradle-plugin?#updating-gradle</p>

<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Android 模拟器实现 hosts 修改]]></title>
    <link href="https://droidyue.com/blog/2023/03/12/how-to-modify-android-emulator-hosts/"/>
    <updated>2023-03-12T20:50:00+08:00</updated>
    <id>https://droidyue.com/blog/2023/03/12/how-to-modify-android-emulator-hosts</id>
    <content type="html"><![CDATA[<p>有时候我们需要使用 Android 模拟器来 绑定一下 hosts 来实现功能的开发与验证，刚好最近遇到了这样的需求，处理完成，简单记录一下。</p>

<!--more-->

<h2 id="替换m1-实现针对-苹果-m1-芯片才需要处理">替换m1 实现（针对 苹果 M1 芯片才需要处理）</h2>
<p>下载这个文件 https://github.com/google/android-emulator-m1-preview/releases/download/0.2/emulator-darwin-aarch64-0.2-engine-only.zip</p>

<p>解压，然后将 <code class="language-plaintext highlighter-rouge">emulator</code> 和 <code class="language-plaintext highlighter-rouge">emulator-check</code> 替换掉这里面的文件 <code class="language-plaintext highlighter-rouge">~/Library/Android/sdk/tools/</code> （原有的可以备份为 xxx_backup）</p>

<h2 id="查看-avd_id">查看 avd_id</h2>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">~/Library/Android/sdk/tools/emulator <span class="nt">-list-avds</span>
</span><span class="line">Pixel6ProAPI33
</span><span class="line">Pixel_3a_API_33_arm64-v8a
</span><span class="line">Pixel_6_API_22
</span><span class="line">Pixel_6_API_28
</span><span class="line">Pixel_6_Pro_API_23
</span><span class="line">Pixel_6_Pro_API_30_X86</span></code></pre></td></tr></table></div></figure></notextile></div>

<h2 id="启动-avd可写入状态">启动 avd，可写入状态</h2>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">~/Library/Android/sdk/tools/emulator <span class="nt">-avd</span> Pixel_3a_API_33_arm64-v8a  <span class="nt">-writable-system</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<h2 id="新起终端tab-执行">新起终端tab 执行</h2>
<ol>
  <li>adb root</li>
  <li>adb remount</li>
  <li>adb push your_hosts_on_mac /etc/hosts</li>
</ol>

<h2 id="验证ping">验证ping</h2>
<p>假设上面的 hosts 我们新增了 <code class="language-plaintext highlighter-rouge">127.0.0.1	baidu.com</code></p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">adb shell
</span><span class="line">
</span><span class="line">ping baidu.com
</span><span class="line">PING baidu.com <span class="o">(</span>127.0.0.1<span class="o">)</span> 56<span class="o">(</span>84<span class="o">)</span> bytes of data.
</span><span class="line">64 bytes from baidu.com <span class="o">(</span>127.0.0.1<span class="o">)</span>: <span class="nv">icmp_seq</span><span class="o">=</span>1 <span class="nv">ttl</span><span class="o">=</span>64 <span class="nb">time</span><span class="o">=</span>1.55 ms
</span><span class="line">64 bytes from baidu.com <span class="o">(</span>127.0.0.1<span class="o">)</span>: <span class="nv">icmp_seq</span><span class="o">=</span>2 <span class="nv">ttl</span><span class="o">=</span>64 <span class="nb">time</span><span class="o">=</span>0.180 ms
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>注意： hosts 修改建议在 mac 上进行处理，然后使用<code class="language-plaintext highlighter-rouge">adb push your_hosts_on_mac /etc/hosts</code> 替换手机内的hosts。手机内置的 vi 很弱，可能无法编辑。</p>

<p>以上。</p>
<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Vs Code 快速实现 重写 方法]]></title>
    <link href="https://droidyue.com/blog/2023/02/12/how-to-override-methods-in-vscode/"/>
    <updated>2023-02-12T20:20:00+08:00</updated>
    <id>https://droidyue.com/blog/2023/02/12/how-to-override-methods-in-vscode</id>
    <content type="html"><![CDATA[<p>作为一个从 Android Studio/IntelliJ 切到 VS code 的开发者，一开始会遇到各种不适应的情况。 比如快捷键不一样，使用习惯不一样等。</p>

<p>这里将简单记录一下 个人遇到的一些痛点，比如如何重写方法。</p>

<!--more-->

<p>在 Android Studio/ IntelliJ 中，使用起来很简单，比如弹出这个菜单，选择 <code class="language-plaintext highlighter-rouge">Override Methods</code> 即可，实现重写 <code class="language-plaintext highlighter-rouge">initState</code> 方法</p>

<p><img src="https://asset.droidyue.com/image/2023/h1/as_intellij_override_methods.png" alt="https://asset.droidyue.com/image/2023/h1/as_intellij_override_methods.png" /></p>

<p>但是切到 Vs Code 后，发现找不到快捷键，后来经过一些摸索，还是找到了 如何快速实现方法重写的方法。</p>

<p>如下图，只需要输入待重写的方法的首字母，即可弹出提示。</p>

<p><img src="https://asset.droidyue.com/image/2023/h1/vscode_override_method.png" alt="https://asset.droidyue.com/image/2023/h1/vscode_override_method.png" /></p>

<p>VS Code 的方式显得会更加的简单。（后来才发现同样的方式 在 Android Studio/Intellij 也支持，Orz）</p>
<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Merge(Pull) Request 推荐的标签列表]]></title>
    <link href="https://droidyue.com/blog/2022/11/27/recommended-lables-merge-requests-or-pull-requests/"/>
    <updated>2022-11-27T21:35:00+08:00</updated>
    <id>https://droidyue.com/blog/2022/11/27/recommended-lables-merge-requests-or-pull-requests</id>
    <content type="html"><![CDATA[<h2 id="一个merge-request-的-阶段">一个Merge Request 的 阶段</h2>
<ol>
  <li>代码添加或修改,需要进行review</li>
  <li>代码review结束,需要修改</li>
  <li>重复步骤1和步骤2,直到达到可以合并的标准</li>
</ol>

<!--more-->

<h2 id="角色">角色</h2>
<ul>
  <li>MR submitter 负责提交Merge Request,并针对review做修改</li>
  <li>MR reviewer  负责review Merge Request,提出MR中存在的问题,该角色可以对应多个人</li>
  <li>MR dispatcher 负责分发MR,修改或增加MR reviewer</li>
  <li>MR terminator  最终负责MR结果走向的人,比如合并或者关闭</li>
</ul>

<h3 id="注意">注意</h3>
<ul>
  <li>上述角色至少需要两个人</li>
  <li>因权限问题, MR reviewer 可能无权限合并该MR</li>
</ul>

<h2 id="有哪些标签">有哪些标签</h2>
<h3 id="mrneeds-reviewmr需要review">MR:Needs Review(MR:需要Review)</h3>

<ul>
  <li>当MR创建或者进行了更新,需要人员Review时,MR submitter 设置该标签</li>
  <li>如果MR对应的内容不需要跟版,不需要现在合并的,不要增加该Lable</li>
  <li>如果一个MR,当前的label不包含MR:Needs Review,MR reviewer 则不会review</li>
</ul>

<h3 id="mrreviewed-with-commentsmr需要修改">MR:Reviewed With Comments(MR:需要修改)</h3>
<ul>
  <li>MR reviewer 进行了review,并提出了一些评论来记录发现的问题和疑问</li>
  <li>MR reviewer 移除 MR:Needs Review 并添加标签 MR:Reviewed With Comments</li>
  <li>MR submitter 根据提出的问题和疑问进行修改或回答,当修改完毕后,移除标签 MR:Reviewed With Comments,并设置MR:Needs Review</li>
</ul>

<h3 id="good-to-merge可以合并">Good to Merge(可以合并)</h3>
<ul>
  <li>经过上面的来回操作,在某一点,MR达到了一个可以合并的时候,这时候需要移除前面的标签,设置成Good to Merge</li>
  <li>设置这个标签,需要由MR reviewer 操作,而不是MR submitter</li>
  <li>设置完这个标签后,MR不需要再更新</li>
  <li>因MR reviewer不一定有merge权限,这一标签还是有必要的</li>
</ul>

<h3 id="do-not-merge请勿合并">Do Not Merge(请勿合并)</h3>
<ul>
  <li>禁止合并标签,该MR可以被 review,但是不要合并进来.</li>
  <li>适用于未来的需求,目前尚不需要加入到主分支</li>
  <li>辅助标签,更加明确表明不需要合并当前MR</li>
</ul>

<h3 id="待验收">待验收</h3>
<ul>
  <li>该功能没有开始进行验收(测试,UI,UE,产品,数据等)</li>
  <li>如果当前MR包含待验收,通常不会被合并</li>
</ul>

<h3 id="验收中">验收中</h3>
<ul>
  <li>该功能正在处于验收中(测试,UI,UE,产品,数据等)</li>
  <li>如果当前MR包含该标签,通常不会被合并</li>
</ul>

<h3 id="验收通过">验收通过</h3>
<ul>
  <li>该功能已经通过验收(测试,UI,UE,产品,数据等)</li>
  <li>如果当前MR包含验收通过,可以进行合并</li>
</ul>

<h3 id="bugfix">BugFix</h3>
<ul>
  <li>仅用于修复线上版本的崩溃提交时,使用</li>
</ul>

<h3 id="可能不上线">可能不上线</h3>
<ul>
  <li>有些MR已经完成,但不确定什么版本引入,需要增加该标签</li>
</ul>

<h3 id="技术需求">技术需求</h3>
<ul>
  <li>非产品迭代需求
    <h2 id="references">References</h2>
  </li>
  <li>https://engineering.invisionapp.com/post/pr-labels/</li>
</ul>
<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[中国特惠！多平台广告屏蔽专家 AdGuard 买断仅需 119 元起]]></title>
    <link href="https://droidyue.com/blog/2022/11/27/adguard-no-more-ads-mac-windows-ios-android/"/>
    <updated>2022-11-27T21:23:00+08:00</updated>
    <id>https://droidyue.com/blog/2022/11/27/adguard-no-more-ads-mac-windows-ios-android</id>
    <content type="html"><![CDATA[<p>网络垃圾广告令人深恶痛绝，它不仅浪费流量、拖慢设备性能，还存在诸多安全隐患！所以很多用户选用 <strong>AdGuard</strong> 来对付这种「数字牛皮癣」。</p>

<p><img src="https://asset.droidyue.com/image/lizhi_io/adguard/1.png" alt="image alt" /></p>

<p>点击[合作伙伴]<a href="https://store.lizhi.io/site/products/id/31?cid=wncr9wz5">专属优惠链接</a>，前往数码荔枝下单 AdGuard 终生版最低仅需 <strong>119</strong> 元，新注册用户再享受立减 <strong>5</strong> 元优惠！</p>

<!--more-->

<h2 id="全方位广告拦截">全方位广告拦截</h2>

<p>为了应对无孔不入的垃圾广告，大家也许已经尝试过不少工具。</p>

<p>目前大多数的广告拦截工具仅支持<strong>电脑网页端</strong>，同时因为技术问题，往往需要逐个浏览器单独配置，最后效果还很一般 (比如面对反屏蔽脚本时无能为力)。</p>

<p><img src="https://asset.droidyue.com/image/lizhi_io/adguard/2.png" alt="image alt" /></p>

<p>AdGuard 不仅内置防反屏蔽功能，同时支持 Win、Mac、安卓、iOS 四大主流系统，而且一经启用，无须额外配置即可在系统内生效！</p>

<p>AdGuard 还可以通过屏蔽垃圾信息，净化网页和软件，让页面更快完成载入，有效节省带宽流量和系统性能。</p>

<p>以视频播放前的贴片广告为例，这类可恶的广告，短则几十秒，长则数分钟！非常浪费时间。</p>

<p><img src="https://asset.droidyue.com/image/lizhi_io/adguard/3.gif" alt="image alt" /></p>

<p>在安装 AdGuard 并启动后，用户再通过浏览器浏览视频，就能享受如同 B 站一般，无广告直接播放的畅快体验 (屏蔽功能不支持国内部分网站和 App)。</p>

<p><img src="https://asset.droidyue.com/image/lizhi_io/adguard/4.gif" alt="image alt" /></p>

<p>以搜索同一个常见疾病为例，在没有拦截广告时，搜索结果通常会<strong>置顶多条广告信息</strong>。在开启拦截后，搜索结果会清爽得多。</p>

<p><img src="https://asset.droidyue.com/image/lizhi_io/adguard/5.png" alt="image alt" /></p>

<p>赶快点击[合作伙伴]<a href="https://store.lizhi.io/site/products/id/31?cid=wncr9wz5">专属优惠链接</a>购买 AdGuard 畅享无广告的清爽体验，终生版仅需 <strong>119</strong> 元起！</p>

<h2 id="隐私与保护">隐私与保护</h2>

<p>无论是大人还是小孩，如今每天接触互联网的时间越来越多。怎样保护个人隐私，怎样让孩子时刻远离危险网站，正困扰着无数人。</p>

<p>除了过滤广告，AdGuard 还提供了「隐形模式」等功能，可以帮助用户远离成千上万的在线跟踪器，保护用户的数据免受网络分析：</p>

<p><img src="https://asset.droidyue.com/image/lizhi_io/adguard/6.png" alt="image alt" /></p>

<p>它能通过隐藏搜索记录、User-Agent 以及阻止网站通过 Cookie、IP 地址等信息追踪到你，实现对个人信息的有效保护。</p>

<p>AdGuard 还内置了 DNS 保护、危险网站警告、家长控制等功能来为用户保驾护航。当用户即将进入<strong>钓鱼、欺诈或恶意网站</strong>时，AdGuard 会向用户发出警告。</p>

<p><img src="https://asset.droidyue.com/image/lizhi_io/adguard/7.png" alt="image alt" /></p>

<p>利用家长控制功能，简单操作就能建立<strong>黑白名单</strong>，设定允许和禁止访问的网站，配合阻止下载可执行文件的功能，切实避免小朋友接触到有害的网站 / 文件。</p>

<p><img src="https://asset.droidyue.com/image/lizhi_io/adguard/8.png" alt="image alt" /></p>

<h2 id="实用扩展功能">实用扩展功能</h2>

<p>AdGuard 一大特色在于它提供了强大的<strong>可扩展性</strong>，通过快速迭代且持续不断的「进化」来切实保护你的网络安全：</p>

<ul>
  <li>AdGuard 助手：网页悬浮球帮你管理过滤选项；</li>
  <li>弹窗拦截器：拦截烦人的网页弹窗广告；</li>
  <li>Web of Trust：检测网页的信誉度来判断是否安全；</li>
  <li>AdGuard Extra：应对常规广告拦截规则无效的复杂情况……</li>
</ul>

<p><img src="https://asset.droidyue.com/image/lizhi_io/adguard/9.png" alt="image alt" /></p>

<p>AdGuard 是一款少见的，能同时在广告拦截和网络安全上有着亮眼表现的工具，它在扩展功能上的持续开发更显示出了十足的诚意。</p>

<p>如果你需要一款支持多系统 + 全局广告拦截 + 个人隐私保护 + 儿童安全浏览 + 丰富扩展功能的工具，可以选择的大概只有 AdGuard 了！</p>

<p>它是超级聪明的广告屏蔽专家，更是值得信赖的安全防护墙！</p>

<p>马上点击[合作伙伴]<a href="https://store.lizhi.io/site/products/id/31?cid=wncr9wz5">专属优惠链接</a>，AdGuard 终生版最低仅需 <strong>119</strong> 元！新注册用户再享受立减 <strong>5</strong> 元优惠，心动不如行动，马上入手吧！</p>
<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[使用 FVM 解决 flutter 3 无法添加 uploader 问题]]></title>
    <link href="https://droidyue.com/blog/2022/11/20/use-fvm-to-add-unpub-uploaders/"/>
    <updated>2022-11-20T21:41:00+08:00</updated>
    <id>https://droidyue.com/blog/2022/11/20/use-fvm-to-add-unpub-uploaders</id>
    <content type="html"><![CDATA[<p>Flutter 3 之后，移除了 添加 uploader 的功能，这使得一些使用unpub 的用户很是苦恼，所以想要继续使用命令添加 uploader， 需要切回 flutter 2 才可以。</p>

<p>这里简单介绍一个很便捷的方式来处理上面的问题，就是使用 fvm 来指定 flutter 2 来执行 uploader 添加。</p>

<!--more-->

<h2 id="安装-fvm">安装 FVM</h2>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class=""><span class="line">brew tap leoafarias/fvm
</span><span class="line">brew install fvm</span></code></pre></td></tr></table></div></figure></notextile></div>
<p>注： 如果没有安装homebrew， 需要安装一下。</p>

<h2 id="使用">使用</h2>

<p>如下内容保存为脚本 <code class="language-plaintext highlighter-rouge">addUnpubUploader.sh</code></p>
<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class=""><span class="line">#!/bin/bash
</span><span class="line">fvm spawn 2.10.3 packages pub uploader  add $1 --verbose</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>执行脚本</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">bash addUnpubUploader.sh aaa@gmail.com </span></code></pre></td></tr></table></div></figure></notextile></div>

<h2 id="referrence">Referrence</h2>
<ul>
  <li>https://fvm.app/</li>
</ul>
<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[AAPT2 aapt2-7.2.2-7984345-osx Daemon #5: Idle daemon unexpectedly exit. This should not happen 问题解决]]></title>
    <link href="https://droidyue.com/blog/2022/11/14/aapt2-aapt2-7-dot-2-2-7984345-osx-daemon-number-5-idle-daemon-unexpectedly-exit-this-should-not-happen/"/>
    <updated>2022-11-14T21:28:00+08:00</updated>
    <id>https://droidyue.com/blog/2022/11/14/aapt2-aapt2-7-dot-2-2-7984345-osx-daemon-number-5-idle-daemon-unexpectedly-exit-this-should-not-happen</id>
    <content type="html"><![CDATA[<p>CI 构建机，一直有概率出现构建失败的情况，查看了日志，得到了这样的相关错误信息</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">AAPT2 aapt2-7.2.2-7984345-osx Daemon <span class="c">#7: Idle daemon unexpectedly exit. This should not happen.</span>
</span><span class="line">15:32:49 <span class="o">[</span>        <span class="o">]</span> AAPT2 aapt2-7.2.2-7984345-osx Daemon <span class="c">#8: Idle daemon unexpectedly exit. This should not happen.</span>
</span><span class="line">15:32:49 <span class="o">[</span>        <span class="o">]</span> AAPT2 aapt2-7.2.2-7984345-osx Daemon <span class="c">#0: shutdown</span>
</span><span class="line">15:32:49 <span class="o">[</span>  +98 ms] AAPT2 aapt2-7.2.2-7984345-osx Daemon <span class="c">#6: Idle daemon unexpectedly exit. This should not happen.</span>
</span><span class="line">15:32:49 <span class="o">[</span>        <span class="o">]</span> AAPT2 aapt2-7.2.2-7984345-osx Daemon <span class="c">#4: Idle daemon unexpectedly exit. This should not happen.</span>
</span><span class="line">15:32:49 <span class="o">[</span>        <span class="o">]</span> AAPT2 aapt2-7.2.2-7984345-osx Daemon <span class="c">#3: Idle daemon unexpectedly exit. This should not happen.</span>
</span><span class="line">15:32:49 <span class="o">[</span>        <span class="o">]</span> AAPT2 aapt2-7.2.2-7984345-osx Daemon <span class="c">#5: Idle daemon unexpectedly exit. This should not happen.</span>
</span><span class="line">15:32:49 <span class="o">[</span>        <span class="o">]</span> AAPT2 aapt2-7.2.2-7984345-osx Daemon <span class="c">#1: Idle daemon unexpectedly exit. This should not happen.</span>
</span><span class="line">15:32:49 <span class="o">[</span> +499 ms] The message received from the daemon indicates that the daemon has disappeared.</span></code></pre></td></tr></table></div></figure></notextile></div>
<!--more-->

<h2 id="解决方法">解决方法</h2>
<ul>
  <li>看日志感觉是 Gradle 守护进程的问题</li>
  <li>想要既保持 Gralde 守护进程，又要解决这个问题，需要更多的时间</li>
  <li>比较简单的方式就是 禁用 Gradle 守护进程。</li>
</ul>

<h3 id="命令参数传递">命令参数传递</h3>
<ul>
  <li>适用于能够直接使用<code class="language-plaintext highlighter-rouge">gradlew</code></li>
  <li>也适用于不想全局应用配置的情况</li>
</ul>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">./gradlew <span class="nt">--no-daemon</span> assembleXXX</span></code></pre></td></tr></table></div></figure></notextile></div>

<h3 id="gradleproperties">gradle.properties</h3>
<ul>
  <li>适用于 无法直接配置 <code class="language-plaintext highlighter-rouge">--no-daemon</code> 的情况，比如 flutter 执行 Android 构建。</li>
  <li>适用于全局配置</li>
</ul>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line"><span class="c">## 修改这个文件 ~/.gradle/gradle.properties 如果没有，直接创建即可</span>
</span><span class="line">org.gradle.daemon<span class="o">=</span><span class="nb">false</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>注意： 这里配置完成，最好执行一下<code class="language-plaintext highlighter-rouge">./gradlew --stop</code> 确保不适用已有的守护进程。</p>
<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[git clone 使用代理，实现百倍加速]]></title>
    <link href="https://droidyue.com/blog/2022/10/26/speed-up-git-clone-via-proxy/"/>
    <updated>2022-10-26T08:51:00+08:00</updated>
    <id>https://droidyue.com/blog/2022/10/26/speed-up-git-clone-via-proxy</id>
    <content type="html"><![CDATA[<p>有时候我们对 github 的仓库进行 clone 的时候，会发现很慢，甚至是龟速，很不够效率。好在有一个简单且快捷的方法来倍速提升clone 效率。</p>

<p>我们通过检索 git 的帮助文档发现有这样的描述</p>

<blockquote>
  <p>If you just want to use proxy on a specified repository, don’t need on other repositories. The preferable way is the -c, –config &lt;key=value&gt; option when you git clone a repository. e.g.</p>
</blockquote>

<!--more-->

<h2 id="实践起来">实践起来</h2>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">git clone https://github.com/flutter/flutter.git <span class="nt">--config</span> <span class="s2">"http.proxy=192.168.1.6:1611"</span></span></code></pre></td></tr></table></div></figure></notextile></div>

<p>上面的例子</p>

<ul>
  <li>通过 <code class="language-plaintext highlighter-rouge">--config "http.proxy=192.168.1.6:1611"</code> 设置代理</li>
  <li>其中 <code class="language-plaintext highlighter-rouge">192.168.1.6:1611</code> 是代理的地址，需要自己搭建或者可用的</li>
</ul>

<p>上面的配置好，再次执行，基本上可以得到百倍的提效。</p>

<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Flutter 处理 Error Setter not found AsciiChar 问题]]></title>
    <link href="https://droidyue.com/blog/2022/10/11/flutter-error-member-not-found-asciichar/"/>
    <updated>2022-10-11T07:35:00+08:00</updated>
    <id>https://droidyue.com/blog/2022/10/11/flutter-error-member-not-found-asciichar</id>
    <content type="html"><![CDATA[<p>当我们进行了 flutter 升级后，有时候运行程序会发现无法编译，出现下面这样的错误</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line"><span class="o">[</span>        <span class="o">]</span> <span class="o">[</span>   +2 ms] ../../../../.pub-cache/hosted/pub.flutter-io.cn/cached_network_image-3.2.0/lib/src/image_provider/multi_image_stream_completer.dart:152:22: Warning: Operand of null-aware operation <span class="s1">'?.'</span> has
</span><span class="line"><span class="nb">type</span> <span class="s1">'SchedulerBinding'</span> which excludes null.
</span><span class="line"><span class="o">[</span>        <span class="o">]</span> <span class="o">[</span>        <span class="o">]</span>  - <span class="s1">'SchedulerBinding'</span> is from <span class="s1">'package:flutter/src/scheduler/binding.dart'</span> <span class="o">(</span><span class="s1">'../../../../code/flutter_3/packages/flutter/lib/src/scheduler/binding.dart'</span><span class="o">)</span><span class="nb">.</span>
</span><span class="line"><span class="o">[</span>        <span class="o">]</span> <span class="o">[</span>        <span class="o">]</span>     SchedulerBinding.instance?.scheduleFrameCallback<span class="o">(</span>_handleAppFrame<span class="o">)</span><span class="p">;</span>
</span><span class="line"><span class="o">[</span>        <span class="o">]</span> <span class="o">[</span>        <span class="o">]</span>                      ^
</span><span class="line"><span class="o">[</span> +402 ms] <span class="o">[</span> +414 ms] ../../../../.pub-cache/hosted/pub.flutter-io.cn/win32-2.3.1/lib/src/structs.g.dart:554:31: Error: Member not found: <span class="s1">'UnicodeChar'</span><span class="nb">.</span>
</span><span class="line"><span class="o">[</span>        <span class="o">]</span> <span class="o">[</span>        <span class="o">]</span>   int get UnicodeChar <span class="o">=&gt;</span> Char.UnicodeChar<span class="p">;</span>
</span><span class="line"><span class="o">[</span>        <span class="o">]</span> <span class="o">[</span>        <span class="o">]</span>                               ^^^^^^^^^^^
</span><span class="line"><span class="o">[</span>        <span class="o">]</span> <span class="o">[</span>        <span class="o">]</span> ../../../../.pub-cache/hosted/pub.flutter-io.cn/win32-2.3.1/lib/src/structs.g.dart:555:38: Error: Setter not found: <span class="s1">'UnicodeChar'</span><span class="nb">.</span>
</span><span class="line"><span class="o">[</span>        <span class="o">]</span> <span class="o">[</span>        <span class="o">]</span>   <span class="nb">set </span>UnicodeChar<span class="o">(</span>int value<span class="o">)</span> <span class="o">=&gt;</span> Char.UnicodeChar <span class="o">=</span> value<span class="p">;</span>
</span><span class="line"><span class="o">[</span>        <span class="o">]</span> <span class="o">[</span>        <span class="o">]</span>                                      ^^^^^^^^^^^
</span><span class="line"><span class="o">[</span>        <span class="o">]</span> <span class="o">[</span>        <span class="o">]</span> ../../../../.pub-cache/hosted/pub.flutter-io.cn/win32-2.3.1/lib/src/structs.g.dart:557:29: Error: Member not found: <span class="s1">'AsciiChar'</span><span class="nb">.</span>
</span><span class="line"><span class="o">[</span>        <span class="o">]</span> <span class="o">[</span>        <span class="o">]</span>   int get AsciiChar <span class="o">=&gt;</span> Char.AsciiChar<span class="p">;</span>
</span><span class="line"><span class="o">[</span>        <span class="o">]</span> <span class="o">[</span>        <span class="o">]</span>                             ^^^^^^^^^
</span><span class="line"><span class="o">[</span>        <span class="o">]</span> <span class="o">[</span>        <span class="o">]</span> ../../../../.pub-cache/hosted/pub.flutter-io.cn/win32-2.3.1/lib/src/structs.g.dart:558:36: Error: Setter not found: <span class="s1">'AsciiChar'</span><span class="nb">.</span>
</span><span class="line"><span class="o">[</span>        <span class="o">]</span> <span class="o">[</span>        <span class="o">]</span>   <span class="nb">set </span>AsciiChar<span class="o">(</span>int value<span class="o">)</span> <span class="o">=&gt;</span> Char.AsciiChar <span class="o">=</span> value<span class="p">;</span>
</span><span class="line"><span class="o">[</span>        <span class="o">]</span> <span class="o">[</span>        <span class="o">]</span>                                    ^^^^^^^^^
</span></code></pre></td></tr></table></div></figure></notextile></div>

<!--more-->

<p>对于这种问题，解决起来也比较简单，执行</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="shell"><span class="line">flutter pub upgrade</span></code></pre></td></tr></table></div></figure></notextile></div>

<br/><img src="https://asset.droidyue.com/image/2020_05/droidyue_gzh_green_png.png"  class="no_boarder_class" style="max-width:100%" /><br/>]]></content>
  </entry>
  
</feed>
